aboutsummaryrefslogtreecommitdiff
path: root/src/string.cpp
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2025-01-06 13:43:01 +0000
committerGitHub <noreply@github.com>2025-01-06 13:43:01 +0000
commit6e49bbb66853b5d824ac5bbd534ae3e81c4f39aa (patch)
tree50886a3be8f2fcfab053e07cfe9e15f50fa5f9f6 /src/string.cpp
parentbd96cd0af761994210018ca647eb843dfeb71494 (diff)
parent98efb03934b464a1b23759b5695a12ff37588357 (diff)
Merge branch 'master' into d3d11-annotations
Diffstat (limited to 'src/string.cpp')
-rw-r--r--src/string.cpp216
1 files changed, 207 insertions, 9 deletions
diff --git a/src/string.cpp b/src/string.cpp
index 6eac4f53b..b001adf0e 100644
--- a/src/string.cpp
+++ b/src/string.cpp
@@ -10,6 +10,10 @@ struct String {
return text[i];
}
};
+struct String_Iterator {
+ String const &str;
+ isize pos;
+};
// NOTE(bill): used for printf style arguments
#define LIT(x) ((int)(x).len), (x).text
#if defined(GB_COMPILER_MSVC) && _MSC_VER < 1700
@@ -84,6 +88,12 @@ gb_internal char *alloc_cstring(gbAllocator a, String s) {
return c_str;
}
+gb_internal wchar_t *alloc_wstring(gbAllocator a, String16 s) {
+ wchar_t *c_str = gb_alloc_array(a, wchar_t, s.len+1);
+ gb_memmove(c_str, s.text, s.len*2);
+ c_str[s.len] = '\0';
+ return c_str;
+}
gb_internal gb_inline bool str_eq_ignore_case(String const &a, String const &b) {
@@ -100,6 +110,16 @@ gb_internal gb_inline bool str_eq_ignore_case(String const &a, String const &b)
return false;
}
+template <isize N>
+gb_internal gb_inline bool str_eq_ignore_case(String const &a, char const (&b_)[N]) {
+ if (a.len != N-1) {
+ return false;
+ }
+ String b = {cast(u8 *)b_, N-1};
+ return str_eq_ignore_case(a, b);
+}
+
+
gb_internal void string_to_lower(String *s) {
for (isize i = 0; i < s->len; i++) {
s->text[i] = gb_char_to_lower(s->text[i]);
@@ -136,6 +156,7 @@ gb_internal isize string_index_byte(String const &s, u8 x) {
gb_internal gb_inline bool str_eq(String const &a, String const &b) {
if (a.len != b.len) return false;
+ if (a.len == 0) return true;
return memcmp(a.text, b.text, a.len) == 0;
}
gb_internal gb_inline bool str_ne(String const &a, String const &b) { return !str_eq(a, b); }
@@ -158,6 +179,9 @@ template <isize N> gb_internal bool operator > (String const &a, char const (&b
template <isize N> gb_internal bool operator <= (String const &a, char const (&b)[N]) { return str_le(a, make_string(cast(u8 *)b, N-1)); }
template <isize N> gb_internal bool operator >= (String const &a, char const (&b)[N]) { return str_ge(a, make_string(cast(u8 *)b, N-1)); }
+template <> bool operator == (String const &a, char const (&b)[1]) { return a.len == 0; }
+template <> bool operator != (String const &a, char const (&b)[1]) { return a.len != 0; }
+
gb_internal gb_inline bool string_starts_with(String const &s, String const &prefix) {
if (prefix.len > s.len) {
return false;
@@ -201,11 +225,36 @@ gb_internal gb_inline String string_trim_starts_with(String const &s, String con
}
+gb_internal String string_split_iterator(String_Iterator *it, const char sep) {
+ isize start = it->pos;
+ isize end = it->str.len;
+
+ if (start == end) {
+ return str_lit("");
+ }
+
+ isize i = start;
+ for (; i < it->str.len; i++) {
+ if (it->str[i] == sep) {
+ String res = substring(it->str, start, i);
+ it->pos += res.len + 1;
+ return res;
+ }
+ }
+ it->pos = end;
+ return substring(it->str, start, end);
+}
+
+gb_internal gb_inline bool is_separator(u8 const &ch) {
+ return (ch == '/' || ch == '\\');
+}
+
+
gb_internal gb_inline isize string_extension_position(String const &str) {
isize dot_pos = -1;
isize i = str.len;
while (i --> 0) {
- if (str[i] == GB_PATH_SEPARATOR)
+ if (is_separator(str[i]))
break;
if (str[i] == '.') {
dot_pos = i;
@@ -240,6 +289,43 @@ gb_internal String string_trim_whitespace(String str) {
return str;
}
+gb_internal String string_trim_trailing_whitespace(String str) {
+ while (str.len > 0) {
+ u8 c = str[str.len-1];
+ if (rune_is_whitespace(c) || c == 0) {
+ str.len -= 1;
+ } else {
+ break;
+ }
+ }
+ return str;
+}
+
+gb_internal String split_lines_first_line_from_array(Array<u8> const &array, gbAllocator allocator) {
+ String_Iterator it = {{array.data, array.count}, 0};
+
+ String line = string_split_iterator(&it, '\n');
+ line = string_trim_trailing_whitespace(line);
+ return line;
+}
+
+gb_internal Array<String> split_lines_from_array(Array<u8> const &array, gbAllocator allocator) {
+ Array<String> lines = {};
+ lines.allocator = allocator;
+
+ String_Iterator it = {{array.data, array.count}, 0};
+
+ for (;;) {
+ String line = string_split_iterator(&it, '\n');
+ if (line.len == 0) {
+ break;
+ }
+ line = string_trim_trailing_whitespace(line);
+ array_add(&lines, line);
+ }
+
+ return lines;
+}
gb_internal bool string_contains_char(String const &s, u8 c) {
isize i;
@@ -250,6 +336,25 @@ gb_internal bool string_contains_char(String const &s, u8 c) {
return false;
}
+gb_internal bool string_contains_string(String const &haystack, String const &needle) {
+ if (needle.len == 0) return true;
+ if (needle.len > haystack.len) return false;
+
+ for (isize i = 0; i <= haystack.len - needle.len; i++) {
+ bool found = true;
+ for (isize j = 0; j < needle.len; j++) {
+ if (haystack[i + j] != needle[j]) {
+ found = false;
+ break;
+ }
+ }
+ if (found) {
+ return true;
+ }
+ }
+ return false;
+}
+
gb_internal String filename_from_path(String s) {
isize i = string_extension_position(s);
if (i >= 0) {
@@ -259,8 +364,7 @@ gb_internal String filename_from_path(String s) {
if (i > 0) {
isize j = 0;
for (j = s.len-1; j >= 0; j--) {
- if (s[j] == '/' ||
- s[j] == '\\') {
+ if (is_separator(s[j])) {
break;
}
}
@@ -269,6 +373,17 @@ gb_internal String filename_from_path(String s) {
return make_string(nullptr, 0);
}
+
+gb_internal String filename_without_directory(String s) {
+ isize j = 0;
+ for (j = s.len-1; j >= 0; j--) {
+ if (is_separator(s[j])) {
+ break;
+ }
+ }
+ return substring(s, gb_max(j+1, 0), s.len);
+}
+
gb_internal String concatenate_strings(gbAllocator a, String const &x, String const &y) {
isize len = x.len+y.len;
u8 *data = gb_alloc_array(a, u8, len+1);
@@ -297,6 +412,32 @@ gb_internal String concatenate4_strings(gbAllocator a, String const &x, String c
return make_string(data, len);
}
+#if defined(GB_SYSTEM_WINDOWS)
+gb_internal String escape_char(gbAllocator a, String s, char cte) {
+ isize buf_len = s.len;
+ isize cte_count = 0;
+ for (isize j = 0; j < s.len; j++) {
+ if (s.text[j] == cte) {
+ cte_count++;
+ }
+ }
+
+ u8 *buf = gb_alloc_array(a, u8, buf_len+cte_count);
+ isize i = 0;
+ for (isize j = 0; j < s.len; j++) {
+ u8 c = s.text[j];
+
+ if (c == cte) {
+ buf[i++] = '\\';
+ buf[i++] = c;
+ } else {
+ buf[i++] = c;
+ }
+ }
+ return make_string(buf, i);
+}
+#endif
+
gb_internal String string_join_and_quote(gbAllocator a, Array<String> strings) {
if (!strings.count) {
return make_string(nullptr, 0);
@@ -312,7 +453,11 @@ gb_internal String string_join_and_quote(gbAllocator a, Array<String> strings) {
if (i > 0) {
s = gb_string_append_fmt(s, " ");
}
+#if defined(GB_SYSTEM_WINDOWS)
+ s = gb_string_append_fmt(s, "\"%.*s\" ", LIT(escape_char(a, strings[i], '\\')));
+#else
s = gb_string_append_fmt(s, "\"%.*s\" ", LIT(strings[i]));
+#endif
}
return make_string(cast(u8 *) s, gb_string_length(s));
@@ -325,7 +470,26 @@ gb_internal String copy_string(gbAllocator a, String const &s) {
return make_string(data, s.len);
}
-
+gb_internal String normalize_path(gbAllocator a, String const &path, String const &sep) {
+ String s;
+ if (sep.len < 1) {
+ return path;
+ }
+ if (path.len < 1) {
+ s = STR_LIT("");
+ } else if (is_separator(path[path.len-1])) {
+ s = copy_string(a, path);
+ } else {
+ s = concatenate_strings(a, path, sep);
+ }
+ isize i;
+ for (i = 0; i < s.len; i++) {
+ if (is_separator(s.text[i])) {
+ s.text[i] = sep.text[0];
+ }
+ }
+ return s;
+}
#if defined(GB_SYSTEM_WINDOWS)
@@ -417,6 +581,40 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) {
+gb_internal String temporary_directory(gbAllocator allocator) {
+#if defined(GB_SYSTEM_WINDOWS)
+ DWORD n = GetTempPathW(0, nullptr);
+ if (n == 0) {
+ return String{0};
+ }
+ DWORD len = gb_max(MAX_PATH, n);
+ wchar_t *b = gb_alloc_array(heap_allocator(), wchar_t, len+1);
+ defer (gb_free(heap_allocator(), b));
+ n = GetTempPathW(len, b);
+ if (n == 3 && b[1] == ':' && b[2] == '\\') {
+
+ } else if (n > 0 && b[n-1] == '\\') {
+ n -= 1;
+ }
+ b[n] = 0;
+ String16 s = make_string16(b, n);
+ return string16_to_string(allocator, s);
+#else
+ char const *tmp_env = gb_get_env("TMPDIR", allocator);
+ if (tmp_env) {
+ return make_string_c(tmp_env);
+ }
+
+#if defined(P_tmpdir)
+ String tmp_macro = make_string_c(P_tmpdir);
+ if (tmp_macro.len != 0) {
+ return copy_string(allocator, tmp_macro);
+ }
+#endif
+
+ return copy_string(allocator, str_lit("/tmp"));
+#endif
+}
@@ -520,12 +718,12 @@ gb_internal bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_byt
Rune r = -1;
isize size = utf8_decode(s.text, s.len, &r);
*rune = r;
- *multiple_bytes = true;
- *tail_string = make_string(s.text+size, s.len-size);
+ if (multiple_bytes) *multiple_bytes = true;
+ if (tail_string) *tail_string = make_string(s.text+size, s.len-size);
return true;
} else if (s[0] != '\\') {
*rune = s[0];
- *tail_string = make_string(s.text+1, s.len-1);
+ if (tail_string) *tail_string = make_string(s.text+1, s.len-1);
return true;
}
@@ -611,10 +809,10 @@ gb_internal bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_byt
return false;
}
*rune = r;
- *multiple_bytes = true;
+ if (multiple_bytes) *multiple_bytes = true;
} break;
}
- *tail_string = s;
+ if (tail_string) *tail_string = s;
return true;
}