mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-29 09:24:33 +00:00
308 lines
5.8 KiB
C++
308 lines
5.8 KiB
C++
|
|
// NOTE(bill): Used for UTF-8 strings
|
|
typedef struct String {
|
|
u8 *text;
|
|
isize len;
|
|
} String;
|
|
// NOTE(bill): used for printf style arguments
|
|
#define LIT(x) (x).len, (x).text
|
|
|
|
|
|
|
|
|
|
gb_inline String make_string(u8 *text, isize len) {
|
|
String s;
|
|
s.text = text;
|
|
if (len < 0)
|
|
len = gb_strlen(cast(char *)text);
|
|
s.len = len;
|
|
return s;
|
|
}
|
|
|
|
gb_inline String make_string(char *text) {
|
|
return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
|
|
}
|
|
|
|
gb_inline b32 are_strings_equal(String a, String b) {
|
|
if (a.len == b.len) {
|
|
return gb_memcompare(a.text, b.text, a.len) == 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
gb_inline b32 are_strings_equal_ignore_case(String a, String b) {
|
|
if (a.len == b.len) {
|
|
for (isize i = 0; i < a.len; i++) {
|
|
char x = cast(char)a.text[i];
|
|
char y = cast(char)b.text[i];
|
|
if (gb_char_to_lower(x) != gb_char_to_lower(y))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int string_compare(String x, String y) {
|
|
if (x.len == y.len &&
|
|
x.text == y.text) {
|
|
return 0;
|
|
}
|
|
|
|
isize n = gb_min(x.len, y.len);
|
|
|
|
isize fast = n/gb_size_of(isize) + 1;
|
|
isize offset = (fast-1)*gb_size_of(isize);
|
|
isize curr_block = 0;
|
|
if (n <= gb_size_of(isize)) {
|
|
fast = 0;
|
|
}
|
|
|
|
isize *la = cast(isize *)x.text;
|
|
isize *lb = cast(isize *)y.text;
|
|
|
|
for (; curr_block < fast; curr_block++) {
|
|
if (la[curr_block] ^ lb[curr_block]) {
|
|
for (isize pos = curr_block*gb_size_of(isize); pos < n; pos++) {
|
|
if (x.text[pos] ^ y.text[pos]) {
|
|
return cast(int)x.text[pos] - cast(int)y.text[pos];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (; offset < n; offset++) {
|
|
if (x.text[offset] ^ y.text[offset]) {
|
|
return cast(int)x.text[offset] - cast(int)y.text[offset];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
GB_COMPARE_PROC(string_cmp_proc) {
|
|
String x = *(String *)a;
|
|
String y = *(String *)b;
|
|
return string_compare(x, y);
|
|
}
|
|
|
|
|
|
gb_inline isize string_extension_position(String str) {
|
|
isize dot_pos = -1;
|
|
isize i = str.len;
|
|
b32 seen_dot = false;
|
|
while (i --> 0) {
|
|
if (str.text[i] == GB_PATH_SEPARATOR)
|
|
break;
|
|
if (str.text[i] == '.') {
|
|
dot_pos = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return dot_pos;
|
|
}
|
|
|
|
gb_inline b32 string_has_extension(String str, String ext) {
|
|
if (str.len > ext.len+1) {
|
|
u8 *s = str.text+str.len - ext.len-1;
|
|
if (s[0] == '.') {
|
|
s++;
|
|
return gb_memcompare(s, ext.text, ext.len) == 0;
|
|
}
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
b32 string_contains_char(String s, u8 c) {
|
|
for (isize i = 0; i < s.len; i++) {
|
|
if (s.text[i] == c)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
b32 unquote_char(String s, u8 quote, Rune *rune, b32 *multiple_bytes, String *tail_string) {
|
|
if (s.text[0] == quote &&
|
|
(quote == '$' || quote == '"')) {
|
|
return false;
|
|
} else if (s.text[0] >= 0x80) {
|
|
Rune r = -1;
|
|
isize size = gb_utf8_decode(s.text, s.len, &r);
|
|
*rune = r;
|
|
*multiple_bytes = true;
|
|
*tail_string = make_string(s.text+size, s.len-size);
|
|
return true;
|
|
} else if (s.text[0] != '\\') {
|
|
*rune = s.text[0];
|
|
*tail_string = make_string(s.text+1, s.len-1);
|
|
return true;
|
|
}
|
|
|
|
if (s.len <= 1) {
|
|
return false;
|
|
}
|
|
u8 c = s.text[1];
|
|
s = make_string(s.text+2, s.len-2);
|
|
|
|
switch (c) {
|
|
default: return false;
|
|
|
|
case 'a': *rune = '\a'; break;
|
|
case 'b': *rune = '\b'; break;
|
|
case 'f': *rune = '\f'; break;
|
|
case 'n': *rune = '\n'; break;
|
|
case 'r': *rune = '\r'; break;
|
|
case 't': *rune = '\t'; break;
|
|
case 'v': *rune = '\v'; break;
|
|
case '\\': *rune = '\\'; break;
|
|
|
|
|
|
case '$':
|
|
case '"':
|
|
if (c != quote) {
|
|
return false;
|
|
}
|
|
*rune = c;
|
|
break;
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7': {
|
|
i32 r = gb_digit_to_int(c);
|
|
if (s.len < 2) {
|
|
return false;
|
|
}
|
|
for (isize i = 0; i < 2; i++) {
|
|
i32 d = gb_digit_to_int(s.text[i]);
|
|
if (d < 0 || d > 7) {
|
|
return false;
|
|
}
|
|
r = (r<<3) | d;
|
|
}
|
|
s = make_string(s.text+2, s.len-2);
|
|
if (r > 0xff) {
|
|
return false;
|
|
}
|
|
*rune = r;
|
|
} break;
|
|
|
|
case 'x':
|
|
case 'u':
|
|
case 'U': {
|
|
isize count = 0;
|
|
switch (c) {
|
|
case 'x': count = 2; break;
|
|
case 'u': count = 4; break;
|
|
case 'U': count = 8; break;
|
|
}
|
|
|
|
Rune r = 0;
|
|
if (s.len < count) {
|
|
return false;
|
|
}
|
|
for (isize i = 0; i < count; i++) {
|
|
i32 d = gb_hex_digit_to_int(s.text[i]);
|
|
if (d < 0) {
|
|
return false;
|
|
}
|
|
r = (r<<4) | d;
|
|
}
|
|
s = make_string(s.text+count, s.len-count);
|
|
if (c == 'x') {
|
|
*rune = r;
|
|
break;
|
|
}
|
|
if (r > GB_RUNE_MAX) {
|
|
return false;
|
|
}
|
|
*rune = r;
|
|
*multiple_bytes = true;
|
|
} break;
|
|
}
|
|
*tail_string = s;
|
|
return true;
|
|
}
|
|
|
|
|
|
// 0 == failure
|
|
// 1 == original memory
|
|
// 2 == new allocation
|
|
i32 unquote_string(gbAllocator a, String *s_) {
|
|
GB_ASSERT(s_ != NULL);
|
|
String s = *s_;
|
|
isize n = s.len;
|
|
if (n < 2)
|
|
return 0;
|
|
u8 quote = s.text[0];
|
|
if (quote != s.text[n-1])
|
|
return 0;
|
|
s.text += 1;
|
|
s.len -= 2;
|
|
|
|
if (quote == '`') {
|
|
if (string_contains_char(s, '`')) {
|
|
return 0;
|
|
}
|
|
*s_ = s;
|
|
return 1;
|
|
}
|
|
if (quote != '"' && quote != '$')
|
|
return 0;
|
|
|
|
if (string_contains_char(s, '\n'))
|
|
return 0;
|
|
|
|
if (!string_contains_char(s, '\\') && !string_contains_char(s, quote)) {
|
|
if (quote == '"') {
|
|
*s_ = s;
|
|
return 1;
|
|
} else if (quote == '$') {
|
|
Rune r = GB_RUNE_INVALID;
|
|
isize size = gb_utf8_decode(s.text, s.len, &r);
|
|
if ((size == s.len) && (r != -1 || size != 1)) {
|
|
*s_ = s;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
u8 rune_temp[4] = {};
|
|
isize buf_len = 3*s.len / 2;
|
|
u8 *buf = gb_alloc_array(a, u8, buf_len);
|
|
isize offset = 0;
|
|
while (s.len > 0) {
|
|
String tail_string = {};
|
|
Rune r = 0;
|
|
b32 multiple_bytes = false;
|
|
b32 success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
|
|
if (!success) {
|
|
gb_free(a, buf);
|
|
return 0;
|
|
}
|
|
s = tail_string;
|
|
|
|
if (r < 0x80 || !multiple_bytes) {
|
|
buf[offset++] = cast(u8)r;
|
|
} else {
|
|
isize size = gb_utf8_encode_rune(rune_temp, r);
|
|
gb_memcopy(buf+offset, rune_temp, size);
|
|
offset += size;
|
|
}
|
|
|
|
if (quote == '$' && s.len != 0) {
|
|
gb_free(a, buf);
|
|
return 0;
|
|
}
|
|
}
|
|
*s_ = make_string(buf, offset);
|
|
return 2;
|
|
}
|