wchar_t ToLowerCase(wchar_t a) { if (a >= 'A' && a <= 'Z') a += 32; return a; } wchar_t ToUpperCase(wchar_t a) { if (a >= 'a' && a <= 'z') a -= 32; return a; } bool IsWhitespace(wchar_t w) { bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; return result; } bool IsSymbol(wchar_t w) { bool result = (w >= '!' && w <= '/') || (w >= ':' && w <= '@') || (w >= '[' && w <= '`') || (w >= '{' && w <= '~'); return result; } bool IsAlphabetic(wchar_t a) { bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); return result; } bool IsIdent(wchar_t a) { bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_'; return result; } bool IsDigit(wchar_t a) { bool result = a >= '0' && a <= '9'; return result; } bool IsAlphanumeric(wchar_t a) { bool result = IsDigit(a) || IsAlphabetic(a); return result; } bool AreEqual(String16 a, String16 b, unsigned ignore_case = false) { if (a.len != b.len) return false; for (int64_t i = 0; i < a.len; i++) { wchar_t A = a.data[i]; wchar_t B = b.data[i]; if (ignore_case) { A = ToLowerCase(A); B = ToLowerCase(B); } if (A != B) return false; } return true; } inline bool operator==(String16 a, String16 b) { return AreEqual(a, b); } inline bool operator!=(String16 a, String16 b) { return !AreEqual(a, b); } String16 Merge(Allocator allocator, Array list, String16 separator = " ") { int64_t char_count = 0; For(list) char_count += it.len; if (char_count == 0) return {}; int64_t node_count = list.len; int64_t base_size = (char_count + 1); int64_t sep_size = (node_count - 1) * separator.len; int64_t size = base_size + sep_size; wchar_t *buff = (wchar_t *)AllocSize(allocator, sizeof(wchar_t) * (size + 1)); String16 string = {buff, 0}; For(list) { Assert(string.len + it.len <= size); memcpy(string.data + string.len, it.data, it.len * sizeof(wchar_t)); string.len += it.len; if (!IsLast(list, it)) { memcpy(string.data + string.len, separator.data, separator.len * sizeof(wchar_t)); string.len += separator.len; } } Assert(string.len == size - 1); string.data[size] = 0; return string; } bool Seek(String16 string, String16 find, int64_t *index_out = NULL, SeekFlag flags = SeekFlag_None) { bool ignore_case = flags & SeekFlag_IgnoreCase ? true : false; bool result = false; if (flags & SeekFlag_MatchFindLast) { for (int64_t i = string.len; i != 0; i--) { int64_t index = i - 1; String16 substring = GetSlice(string, index, index + find.len); if (AreEqual(substring, find, ignore_case)) { if (index_out) *index_out = index; result = true; break; } } } else { for (int64_t i = 0; i < string.len; i++) { String16 substring = GetSlice(string, i, i + find.len); if (AreEqual(substring, find, ignore_case)) { if (index_out) *index_out = i; result = true; break; } } } return result; } String16 ChopLastSlash(String16 s) { String16 result = s; Seek(s, L"/", &result.len, SeekFlag_MatchFindLast); return result; } String16 ChopLastPeriod(String16 s) { String16 result = s; Seek(s, L".", &result.len, SeekFlag_MatchFindLast); return result; } String16 SkipToLastSlash(String16 s) { int64_t pos; String16 result = s; if (Seek(s, L"/", &pos, SeekFlag_MatchFindLast)) { result = Skip(result, pos + 1); } return result; } String16 SkipToLastPeriod(String16 s) { int64_t pos; String16 result = s; if (Seek(s, L".", &pos, SeekFlag_MatchFindLast)) { result = Skip(result, pos + 1); } return result; } String16 CutPrefix(String16 *string, int64_t len) { String16 result = GetPrefix(*string, len); *string = Skip(*string, len); return result; } String16 CutPostfix(String16 *string, int64_t len) { String16 result = GetPostfix(*string, len); *string = Chop(*string, len); return result; }