From 3af44d8f1621b9a30ac52ece808dc8aa09e36885 Mon Sep 17 00:00:00 2001 From: doyle Date: Wed, 11 Aug 2021 01:06:50 +1000 Subject: [PATCH] strings: Add more string helpers, add more string tests --- Dqn.h | 267 ++++++++++++++++++++++++++---------- Dqn_Tests.cpp | 365 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 456 insertions(+), 176 deletions(-) diff --git a/Dqn.h b/Dqn.h index 76bccfa..ee08a33 100644 --- a/Dqn.h +++ b/Dqn.h @@ -1182,7 +1182,8 @@ struct Dqn_StringW }; // Make a string from a pre-existing string. -DQN_API Dqn_String Dqn_String_Init (char const *string, Dqn_isize size); +DQN_API Dqn_String Dqn_String_Init (char const *string, Dqn_isize size); +DQN_API Dqn_String Dqn_String_InitCString(char const *string); // Make an empty string from a the buffer. 1 byte is reserved for the null-terminator DQN_API Dqn_String Dqn_String_InitMemory(char *buf, Dqn_isize capacity); @@ -1210,6 +1211,7 @@ DQN_API Dqn_String Dqn_String__Copy(Dqn_String const src, Dqn_ArenaAllocator *ar DQN_API Dqn_String Dqn_String_TrimWhitespaceAround(Dqn_String src); DQN_API Dqn_b32 operator== (Dqn_String const &lhs, Dqn_String const &rhs); +DQN_API Dqn_b32 operator!= (Dqn_String const &lhs, Dqn_String const &rhs); // Append to the string if there's enough capacity. No reallocation is permitted, fails if not enough space DQN_API Dqn_b32 Dqn_String_AppendFmtV (Dqn_String *str, char const *fmt, va_list va); @@ -1225,7 +1227,12 @@ DQN_API Dqn_b32 Dqn_String_Eq (Dqn_String const DQN_API Dqn_b32 Dqn_String_EqInsensitive (Dqn_String const lhs, Dqn_String const rhs); DQN_API Dqn_b32 Dqn_String_StartsWith (Dqn_String string, Dqn_String prefix, Dqn_StringEqCase eq_case = Dqn_StringEqCase::Sensitive); DQN_API Dqn_b32 Dqn_String_StartsWithInsensitive(Dqn_String string, Dqn_String prefix); +DQN_API Dqn_b32 Dqn_String_EndsWith (Dqn_String string, Dqn_String prefix, Dqn_StringEqCase eq_case = Dqn_StringEqCase::Sensitive); +DQN_API Dqn_b32 Dqn_String_EndsWithInsensitive (Dqn_String string, Dqn_String prefix); DQN_API Dqn_Array Dqn_String_Split (Dqn_String src, Dqn_ArenaAllocator *arena); +DQN_API Dqn_String Dqn_String_TrimPrefix (Dqn_String src, Dqn_String prefix, Dqn_StringEqCase eq_case = Dqn_StringEqCase::Sensitive); +DQN_API Dqn_String Dqn_String_TrimSuffix (Dqn_String src, Dqn_String suffix, Dqn_StringEqCase eq_case = Dqn_StringEqCase::Sensitive); +DQN_API Dqn_b32 Dqn_String_IsAllDigits (Dqn_String src); DQN_API Dqn_u64 Dqn_String_ToU64 (Dqn_String str); DQN_API Dqn_i64 Dqn_String_ToI64 (Dqn_String str); @@ -1704,12 +1711,12 @@ DQN_API Dqn_String Dqn_Hex_U8ArrayToString (Dqn_Array const byte // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_Str // ------------------------------------------------------------------------------------------------- -DQN_API Dqn_b32 Dqn_Str_Equals (char const *a, char const *b, Dqn_isize a_len = -1, Dqn_isize b_len = -1); -DQN_API char const *Dqn_Str_FindMulti (char const *buf, char const *find_list[], Dqn_isize const *find_string_lens, Dqn_isize find_len, Dqn_isize *match_index, Dqn_isize buf_len = -1); -DQN_API char const *Dqn_Str_Find (char const *buf, char const *find, Dqn_isize buf_len = -1, Dqn_isize find_len = -1, Dqn_b32 case_insensitive = false); -DQN_API char const *Dqn_Str_FileNameFromPath (char const *path, Dqn_isize len = -1, Dqn_isize *file_name_len = nullptr); +DQN_API Dqn_b32 Dqn_Str_Equals (char const *a, char const *b, Dqn_isize a_size = -1, Dqn_isize b_size = -1); +DQN_API char const *Dqn_Str_FindMulti (char const *buf, char const *find_list[], Dqn_isize const *find_string_sizes, Dqn_isize find_size, Dqn_isize *match_index, Dqn_isize buf_size = -1); +DQN_API char const *Dqn_Str_Find (char const *buf, char const *find, Dqn_isize buf_size = -1, Dqn_isize find_size = -1, Dqn_b32 case_insensitive = false); +DQN_API char const *Dqn_Str_FileNameFromPath (char const *path, Dqn_isize size = -1, Dqn_isize *file_name_size = nullptr); DQN_API Dqn_isize Dqn_Str_Size (char const *a); -DQN_API Dqn_b32 Dqn_Str_Match (char const *src, char const *find, int find_len); +DQN_API Dqn_b32 Dqn_Str_Match (char const *src, char const *find, int find_size); DQN_API char const *Dqn_Str_SkipToChar (char const *src, char ch); DQN_API char const *Dqn_Str_SkipToNextAlphaNum (char const *src); DQN_API char const *Dqn_Str_SkipToNextDigit (char const *src); @@ -1725,10 +1732,11 @@ DQN_API char const *Dqn_Str_SkipToNextWordInPlace (char const **src); DQN_API char const *Dqn_Str_SkipWhitespaceInPlace (char const **src); DQN_API char const *Dqn_Str_TrimWhitespaceAround (char const *src, Dqn_isize size, Dqn_isize *new_size); DQN_API char const *Dqn_Str_TrimPrefix (char const *src, Dqn_isize size, char const *prefix, Dqn_isize prefix_size, Dqn_isize *trimmed_size); +DQN_API Dqn_b32 Dqn_Str_IsAllDigits (char const *src, Dqn_isize size); // separator: The separator between the thousand-th digits, i.e. separator = ',' converts '1,234' to '1234'. -DQN_API Dqn_u64 Dqn_Str_ToU64 (char const *buf, int len = -1, char separator = ','); -DQN_API Dqn_i64 Dqn_Str_ToI64 (char const *buf, int len = -1, char separator = ','); +DQN_API Dqn_u64 Dqn_Str_ToU64 (char const *buf, int size = -1, char separator = ','); +DQN_API Dqn_i64 Dqn_Str_ToI64 (char const *buf, int size = -1, char separator = ','); DQN_API Dqn_isize Dqn_StrW_Size (wchar_t const *a); @@ -1859,6 +1867,9 @@ DQN_API void Dqn_Win_DumpLastError(Dqn_String prefix); // Automatical DQN_API Dqn_StringW Dqn_Win_UTF8ToWChar (Dqn_String src, Dqn_ArenaAllocator *arena); DQN_API Dqn_String Dqn_Win_WCharToUTF8 (Dqn_StringW src, Dqn_ArenaAllocator *arena); +DQN_API Dqn_StringW Dqn_Win_GetExecutableDirectoryW(Dqn_ArenaAllocator *arena); +DQN_API Dqn_String Dqn_Win_GetExecutableDirectory (Dqn_ArenaAllocator *arena, Dqn_ArenaAllocator *tmp_arena); + // size: (Optional) The size of the current directory string returned // suffix: (Optional) A suffix to append to the current working directory // suffix_size: (Optional) The size of the suffix to append @@ -2897,6 +2908,9 @@ Dqn_b32 Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *iterator) #define FILE_ATTRIBUTE_READONLY 0x00000001 #define FILE_ATTRIBUTE_HIDDEN 0x00000002 + // NOTE: GetModuleFileNameW + #define ERROR_INSUFFICIENT_BUFFER 122L + // --------------------------------------------------------------------- // Data Structures // --------------------------------------------------------------------- @@ -3017,6 +3031,7 @@ Dqn_b32 Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *iterator) void *GetProcAddress (void *hmodule, char const *proc_name); unsigned int GetWindowModuleFileNameA (void *hwnd, char *file_name, unsigned int file_name_max); HMODULE GetModuleHandleA (char const *lpModuleName); + DWORD GetModuleFileNameW (HMODULE hModule, wchar_t *lpFilename, DWORD nSize); DWORD WaitForSingleObject (HANDLE handle, DWORD milliseconds); @@ -3446,6 +3461,16 @@ DQN_API Dqn_String Dqn_String_Init(char const *string, Dqn_isize size) return result; } +DQN_API Dqn_String Dqn_String_InitCString(char const *string) +{ + Dqn_String result = {}; + result.str = DQN_CAST(char *)string; + result.size = Dqn_Str_Size(string); + result.cap = result.size; + return result; +} + + DQN_API Dqn_String Dqn_String_InitMemory(char *buf, Dqn_isize capacity) { DQN_ASSERT(capacity > 0); @@ -3523,7 +3548,15 @@ DQN_API Dqn_String Dqn_String_TrimWhitespaceAround(Dqn_String const src) DQN_API Dqn_b32 operator==(Dqn_String const &lhs, Dqn_String const &rhs) { - Dqn_b32 result = lhs.size == rhs.size && (DQN_MEMCMP(lhs.str, rhs.str, lhs.size) == 0); + Dqn_b32 result = false; + if (lhs.size == rhs.size) + result = (lhs.str == rhs.str) || (DQN_MEMCMP(lhs.str, rhs.str, lhs.size) == 0); + return result; +} + +DQN_API Dqn_b32 operator!=(Dqn_String const &lhs, Dqn_String const &rhs) +{ + Dqn_b32 result = !(lhs == rhs); return result; } @@ -3595,6 +3628,25 @@ DQN_API Dqn_b32 Dqn_String_StartsWithInsensitive(Dqn_String string, Dqn_String p return result; } +DQN_API Dqn_b32 Dqn_String_EndsWith(Dqn_String string, + Dqn_String suffix, + Dqn_StringEqCase eq_case) +{ + Dqn_b32 result = false; + if (suffix.size > string.size) + return result; + + Dqn_String substring = Dqn_String{string.str + string.size - suffix.size, suffix.size, suffix.size}; + result = Dqn_String_Eq(substring, suffix, eq_case); + return result; +} + +DQN_API Dqn_b32 Dqn_String_EndsWithInsensitive(Dqn_String string, Dqn_String suffix) +{ + Dqn_b32 result = Dqn_String_EndsWith(string, suffix, Dqn_StringEqCase::Insensitive); + return result; +} + DQN_API Dqn_Array Dqn_String_Split(Dqn_String src, Dqn_ArenaAllocator *arena) { enum StringSplitStage @@ -3644,6 +3696,41 @@ DQN_API Dqn_Array Dqn_String_Split(Dqn_String src, Dqn_ArenaAllocato return result; } +DQN_API Dqn_String Dqn_String_TrimPrefix(Dqn_String str, Dqn_String prefix, Dqn_StringEqCase eq_case) +{ + Dqn_String result = str; + if (Dqn_String_StartsWith(str, prefix, eq_case)) + { + result.str += prefix.size; + result.size -= prefix.size; + } + + return result; +} + +DQN_API Dqn_String Dqn_String_TrimSuffix(Dqn_String str, Dqn_String suffix, Dqn_StringEqCase eq_case) +{ + Dqn_String result = str; + if (Dqn_String_EndsWith(str, suffix, eq_case)) + result.size -= suffix.size; + + return result; +} + +DQN_API Dqn_b32 Dqn_String_IsAllDigits(Dqn_String src) +{ + if (!src.str) + return false; + + for (Dqn_isize ch_index = 0; ch_index < src.size; ch_index++) + { + if (!(src.str[ch_index] >= '0' && src.str[ch_index] <= '9')) + return false; + } + Dqn_b32 result = src.str && src.size; + return result; +} + DQN_API Dqn_u64 Dqn_String_ToU64(Dqn_String str) { Dqn_u64 result = Dqn_Str_ToU64(str.str, DQN_CAST(int)str.size); @@ -3712,6 +3799,7 @@ DQN_API Dqn_String Dqn_StringList_Build(Dqn_StringList const *list, Dqn_ArenaAll DQN_MEMCOPY(result.str + result.size, node->string.str, node->string.size); result.size += node->string.size; } + result.str[result.size] = 0; return result; } @@ -4793,31 +4881,31 @@ DQN_API Dqn_String Dqn_Hex_U8ArrayToString(Dqn_Array const bytes, Dqn_Ar // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_Str // ------------------------------------------------------------------------------------------------- -DQN_API Dqn_b32 Dqn_Str_Equals(char const *a, char const *b, Dqn_isize a_len, Dqn_isize b_len) +DQN_API Dqn_b32 Dqn_Str_Equals(char const *a, char const *b, Dqn_isize a_size, Dqn_isize b_size) { - if (a_len == -1) a_len = DQN_CAST(Dqn_isize)Dqn_Str_Size(a); - if (b_len == -1) b_len = DQN_CAST(Dqn_isize)Dqn_Str_Size(b); - if (a_len != b_len) return false; - return (DQN_MEMCMP(a, b, DQN_CAST(size_t)a_len) == 0); + if (a_size == -1) a_size = DQN_CAST(Dqn_isize)Dqn_Str_Size(a); + if (b_size == -1) b_size = DQN_CAST(Dqn_isize)Dqn_Str_Size(b); + if (a_size != b_size) return false; + return (DQN_MEMCMP(a, b, DQN_CAST(size_t)a_size) == 0); } -DQN_API char const *Dqn_Str_FindMulti(char const *buf, char const *find_list[], Dqn_isize const *find_string_lens, Dqn_isize find_len, Dqn_isize *match_index, Dqn_isize buf_len) +DQN_API char const *Dqn_Str_FindMulti(char const *buf, char const *find_list[], Dqn_isize const *find_string_sizes, Dqn_isize find_size, Dqn_isize *match_index, Dqn_isize buf_size) { char const *result = nullptr; - if (find_len == 0) return result; - if (buf_len < 0) buf_len = DQN_CAST(Dqn_isize)Dqn_Str_Size(buf); + if (find_size == 0) return result; + if (buf_size < 0) buf_size = DQN_CAST(Dqn_isize)Dqn_Str_Size(buf); - char const *buf_end = buf + buf_len; + char const *buf_end = buf + buf_size; for (; buf != buf_end; ++buf) { Dqn_isize remaining = static_cast(buf_end - buf); - DQN_FOR_EACH(find_index, find_len) + DQN_FOR_EACH(find_index, find_size) { char const *find = find_list[find_index]; - Dqn_isize find_str_len = find_string_lens[find_index]; - if (remaining < find_str_len) continue; + Dqn_isize find_str_size = find_string_sizes[find_index]; + if (remaining < find_str_size) continue; - if (strncmp(buf, find, DQN_CAST(size_t)find_str_len) == 0) + if (strncmp(buf, find, DQN_CAST(size_t)find_str_size) == 0) { result = buf; *match_index = find_index; @@ -4829,21 +4917,21 @@ DQN_API char const *Dqn_Str_FindMulti(char const *buf, char const *find_list[], return result; } -DQN_API char const *Dqn_Str_Find(char const *buf, char const *find, Dqn_isize buf_len, Dqn_isize find_len, Dqn_b32 case_insensitive) +DQN_API char const *Dqn_Str_Find(char const *buf, char const *find, Dqn_isize buf_size, Dqn_isize find_size, Dqn_b32 case_insensitive) { - if (find_len == 0) return nullptr; - if (buf_len < 0) buf_len = DQN_CAST(Dqn_isize)Dqn_Str_Size(buf); - if (find_len < 0) find_len = DQN_CAST(Dqn_isize)Dqn_Str_Size(find); + if (find_size == 0) return nullptr; + if (buf_size < 0) buf_size = DQN_CAST(Dqn_isize)Dqn_Str_Size(buf); + if (find_size < 0) find_size = DQN_CAST(Dqn_isize)Dqn_Str_Size(find); - char const *buf_end = buf + buf_len; + char const *buf_end = buf + buf_size; char const *result = nullptr; for (; buf != buf_end; ++buf) { Dqn_isize remaining = static_cast(buf_end - buf); - if (remaining < find_len) break; + if (remaining < find_size) break; Dqn_b32 matched = true; - for (Dqn_isize index = 0; index < find_len; index++) + for (Dqn_isize index = 0; index < find_size; index++) { char lhs = buf[index]; char rhs = find[index]; @@ -4870,24 +4958,22 @@ DQN_API char const *Dqn_Str_Find(char const *buf, char const *find, Dqn_isize bu return result; } -DQN_API char const *Dqn_Str_FileNameFromPath(char const *path, Dqn_isize len, Dqn_isize *file_name_len) +DQN_API char const *Dqn_Str_FileNameFromPath(char const *path, Dqn_isize size, Dqn_isize *file_name_size) { - char const *result = path; - Dqn_isize result_len = len == -1 ? Dqn_Str_Size(path) : len; - for (Dqn_isize i = (result_len - 1); i >= 0; --i) + char const *result = path; + Dqn_isize result_size = size <= -1 ? Dqn_Str_Size(path) : size; + for (Dqn_isize i = (result_size - 1); i >= 0; --i) { if (result[i] == '\\' || result[i] == '/') { - char const *file_end = result + result_len; + char const *file_end = result + result_size; result = result + (i + 1); - result_len = DQN_CAST(Dqn_isize)(file_end - result); + result_size = DQN_CAST(Dqn_isize)(file_end - result); break; } } - if (file_name_len) - *file_name_len = result_len; - + if (file_name_size) *file_name_size = result_size; return result; } @@ -4903,10 +4989,10 @@ DQN_API Dqn_isize Dqn_Str_Size(char const *src) return result; } -DQN_API Dqn_b32 Dqn_Str_Match(char const *src, char const *find, int find_len) +DQN_API Dqn_b32 Dqn_Str_Match(char const *src, char const *find, int find_size) { - if (find_len == -1) find_len = Dqn_Safe_TruncateUSizeToInt(Dqn_Str_Size(find)); - auto result = DQN_CAST(Dqn_b32)(strncmp(src, find, DQN_CAST(size_t)find_len) == 0); + if (find_size == -1) find_size = Dqn_Safe_TruncateUSizeToInt(Dqn_Str_Size(find)); + Dqn_b32 result = (DQN_MEMCMP(src, find, DQN_CAST(size_t)find_size) == 0); return result; } @@ -5014,40 +5100,44 @@ DQN_API char const *Dqn_Str_TrimWhitespaceAround(char const *src, Dqn_isize size DQN_API char const *Dqn_Str_TrimPrefix(char const *src, Dqn_isize size, char const *prefix, Dqn_isize prefix_size, Dqn_isize *trimmed_size) { + if (size <= -1) size = Dqn_Str_Size(src); + if (prefix_size <= -1) prefix_size = Dqn_Str_Size(prefix); char const *result = src; - if (size >= prefix_size) - { - Dqn_b32 prefix_matched = true; - for (Dqn_isize prefix_index = 0; - prefix_index < prefix_size && prefix_matched; - prefix_index++) - { - char prefix_ch = prefix[prefix_index]; - char src_ch = src[prefix_index]; - prefix_matched = src_ch == prefix_ch; - } + if (prefix_size > size) + return result; - if (prefix_matched) - { - result += prefix_size; - *trimmed_size = size - prefix_size; - } + if (DQN_MEMCMP(src, prefix, prefix_size) == 0) + { + result += prefix_size; + if (trimmed_size) *trimmed_size = size - prefix_size; } return result; } +DQN_API Dqn_b32 Dqn_Str_IsAllDigits(char const *src, Dqn_isize size) +{ + if (!src) return false; + if (size <= -1) size = Dqn_Str_Size(src); + for (Dqn_isize ch_index = 0; ch_index < size; ch_index++) + { + if (!(src[ch_index] >= '0' && src[ch_index] <= '9')) + return false; + } + Dqn_b32 result = src && size > 0; + return result; +} -DQN_API Dqn_u64 Dqn_Str_ToU64(char const *buf, int len, char separator) +DQN_API Dqn_u64 Dqn_Str_ToU64(char const *buf, int size, char separator) { Dqn_u64 result = 0; if (!buf) return result; - if (len == -1) len = Dqn_Safe_TruncateUSizeToInt(Dqn_Str_Size(buf)); - if (len == 0) return result; + if (size == -1) size = Dqn_Safe_TruncateUSizeToInt(Dqn_Str_Size(buf)); + if (size == 0) return result; char const *buf_ptr = Dqn_Str_SkipWhitespace(buf); - len -= static_cast(buf_ptr - buf); - for (int buf_index = 0; buf_index < len; ++buf_index) + size -= DQN_CAST(int)(buf_ptr - buf); + for (int buf_index = 0; buf_index < size; ++buf_index) { char ch = buf_ptr[buf_index]; if (buf_index && ch == separator) @@ -5065,24 +5155,24 @@ DQN_API Dqn_u64 Dqn_Str_ToU64(char const *buf, int len, char separator) return result; } -DQN_API Dqn_i64 Dqn_Str_ToI64(char const *buf, int len, char separator) +DQN_API Dqn_i64 Dqn_Str_ToI64(char const *buf, int size, char separator) { Dqn_i64 result = 0; if (!buf) return result; - if (len == -1) len = Dqn_Safe_TruncateUSizeToInt(Dqn_Str_Size(buf)); - if (len == 0) return result; + if (size == -1) size = Dqn_Safe_TruncateUSizeToInt(Dqn_Str_Size(buf)); + if (size == 0) return result; char const *buf_ptr = Dqn_Str_SkipWhitespace(buf); - len -= static_cast(buf_ptr - buf); + size -= static_cast(buf_ptr - buf); Dqn_b32 negative = (buf[0] == '-'); if (negative) { ++buf_ptr; - --len; + --size; } - for (int buf_index = 0; buf_index < len; ++buf_index) + for (int buf_index = 0; buf_index < size; ++buf_index) { char ch = buf_ptr[buf_index]; if (buf_index && ch == separator) continue; @@ -5690,6 +5780,47 @@ DQN_API Dqn_String Dqn_Win_WCharToUTF8(Dqn_StringW src, Dqn_ArenaAllocator *aren return result; } +DQN_API Dqn_StringW Dqn_Win_GetExecutableDirectoryW(Dqn_ArenaAllocator *arena) +{ + // NOTE: Max size from MSDN, using \\? syntax, but the ? bit can be expanded + // even more so the max size is kind of not well defined. + wchar_t buffer[32767 + 128 /*fudge*/]; + int file_path_size = GetModuleFileNameW(nullptr /*module*/, buffer, Dqn_ArrayCountI(buffer)); + DQN_HARD_ASSERT_MSG(GetLastError() != ERROR_INSUFFICIENT_BUFFER, "How the hell?"); + + int directory_size = file_path_size; + for (int index = directory_size - 1; index >= 0; index--, directory_size--) + { + if (buffer[index] == '\\') + { + directory_size--; + break; + } + } + + Dqn_StringW result = {}; + if (directory_size != 0) + { + auto *str = Dqn_ArenaAllocator_NewArray(arena, wchar_t, directory_size + 1, Dqn_ZeroMem::No); + if (str) + { + DQN_MEMCOPY(str, buffer, sizeof(wchar_t) * directory_size); + str[directory_size] = 0; + result = Dqn_StringW{str, directory_size, directory_size}; + } + } + + return result; +} + +DQN_API Dqn_String Dqn_Win_GetExecutableDirectory(Dqn_ArenaAllocator *arena, Dqn_ArenaAllocator *tmp_arena) +{ + Dqn_ArenaAllocatorAutoRegion tmp_mem_region = Dqn_ArenaAllocator_AutoRegion(tmp_arena); + Dqn_StringW exe_dir_w = Dqn_Win_GetExecutableDirectoryW(tmp_arena); + Dqn_String result = Dqn_Win_WCharToUTF8(exe_dir_w, arena); + return result; +} + DQN_API Dqn_String Dqn_Win_CurrentDir(Dqn_ArenaAllocator *arena, Dqn_ArenaAllocator *tmp_arena, Dqn_String suffix) { Dqn_StringW w_suffix = Dqn_Win_UTF8ToWChar(suffix, tmp_arena); diff --git a/Dqn_Tests.cpp b/Dqn_Tests.cpp index 1b39711..08e700a 100644 --- a/Dqn_Tests.cpp +++ b/Dqn_Tests.cpp @@ -900,147 +900,224 @@ void Dqn_Test_PerfCounter() void Dqn_Test_Str() { Dqn_TestingState testing_state = {}; + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Str"); // --------------------------------------------------------------------------------------------- // NOTE: Dqn_Str_ToI64 // --------------------------------------------------------------------------------------------- { - DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Str_ToI64"); - { - DQN_TEST_START_SCOPE(testing_state, "Convert nullptr"); - Dqn_i64 result = Dqn_Str_ToI64(nullptr); - DQN_TEST_EXPECT(testing_state, result == 0); - } + DQN_TEST_START_SCOPE(testing_state, "To I64: Convert nullptr"); + Dqn_i64 result = Dqn_Str_ToI64(nullptr); + DQN_TEST_EXPECT(testing_state, result == 0); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert empty string"); - Dqn_i64 result = Dqn_Str_ToI64(""); - DQN_TEST_EXPECT(testing_state, result == 0); - } + { + DQN_TEST_START_SCOPE(testing_state, "To I64: Convert empty string"); + Dqn_i64 result = Dqn_Str_ToI64(""); + DQN_TEST_EXPECT(testing_state, result == 0); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"1\""); - Dqn_i64 result = Dqn_Str_ToI64("1"); - DQN_TEST_EXPECT(testing_state, result == 1); - } + { + DQN_TEST_START_SCOPE(testing_state, "To I64: Convert \"1\""); + Dqn_i64 result = Dqn_Str_ToI64("1"); + DQN_TEST_EXPECT(testing_state, result == 1); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"-0\""); - Dqn_i64 result = Dqn_Str_ToI64("-0"); - DQN_TEST_EXPECT(testing_state, result == 0); - } + { + DQN_TEST_START_SCOPE(testing_state, "To I64: Convert \"-0\""); + Dqn_i64 result = Dqn_Str_ToI64("-0"); + DQN_TEST_EXPECT(testing_state, result == 0); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"-1\""); - Dqn_i64 result = Dqn_Str_ToI64("-1"); - DQN_TEST_EXPECT(testing_state, result == -1); - } + { + DQN_TEST_START_SCOPE(testing_state, "To I64: Convert \"-1\""); + Dqn_i64 result = Dqn_Str_ToI64("-1"); + DQN_TEST_EXPECT(testing_state, result == -1); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"1.2\""); - Dqn_i64 result = Dqn_Str_ToI64("1.2"); - DQN_TEST_EXPECT(testing_state, result == 1); - } + { + DQN_TEST_START_SCOPE(testing_state, "To I64: Convert \"1.2\""); + Dqn_i64 result = Dqn_Str_ToI64("1.2"); + DQN_TEST_EXPECT(testing_state, result == 1); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"1,234\""); - Dqn_i64 result = Dqn_Str_ToI64("1,234"); - DQN_TEST_EXPECT(testing_state, result == 1234); - } + { + DQN_TEST_START_SCOPE(testing_state, "To I64: Convert \"1,234\""); + Dqn_i64 result = Dqn_Str_ToI64("1,234"); + DQN_TEST_EXPECT(testing_state, result == 1234); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"1,2\""); - Dqn_i64 result = Dqn_Str_ToI64("1,2"); - DQN_TEST_EXPECT(testing_state, result == 12); - } + { + DQN_TEST_START_SCOPE(testing_state, "To I64: Convert \"1,2\""); + Dqn_i64 result = Dqn_Str_ToI64("1,2"); + DQN_TEST_EXPECT(testing_state, result == 12); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"12a3\""); - Dqn_i64 result = Dqn_Str_ToI64("12a3"); - DQN_TEST_EXPECT(testing_state, result == 12); - } + { + DQN_TEST_START_SCOPE(testing_state, "To I64: Convert \"12a3\""); + Dqn_i64 result = Dqn_Str_ToI64("12a3"); + DQN_TEST_EXPECT(testing_state, result == 12); } // --------------------------------------------------------------------------------------------- // NOTE: Dqn_Str_ToU64 // --------------------------------------------------------------------------------------------- { - DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Str_ToU64"); - { - DQN_TEST_START_SCOPE(testing_state, "Convert nullptr"); - Dqn_u64 result = Dqn_Str_ToU64(nullptr); - DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); - } + DQN_TEST_START_SCOPE(testing_state, "To U64: Convert nullptr"); + Dqn_u64 result = Dqn_Str_ToU64(nullptr); + DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert empty string"); - Dqn_u64 result = Dqn_Str_ToU64(""); - DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); - } + { + DQN_TEST_START_SCOPE(testing_state, "To U64: Convert empty string"); + Dqn_u64 result = Dqn_Str_ToU64(""); + DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"1\""); - Dqn_u64 result = Dqn_Str_ToU64("1"); - DQN_TEST_EXPECT_MSG(testing_state, result == 1, "result: %zu", result); - } + { + DQN_TEST_START_SCOPE(testing_state, "To U64: Convert \"1\""); + Dqn_u64 result = Dqn_Str_ToU64("1"); + DQN_TEST_EXPECT_MSG(testing_state, result == 1, "result: %zu", result); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"-0\""); - Dqn_u64 result = Dqn_Str_ToU64("-0"); - DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); - } + { + DQN_TEST_START_SCOPE(testing_state, "To U64: Convert \"-0\""); + Dqn_u64 result = Dqn_Str_ToU64("-0"); + DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"-1\""); - Dqn_u64 result = Dqn_Str_ToU64("-1"); - DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); - } + { + DQN_TEST_START_SCOPE(testing_state, "To U64: Convert \"-1\""); + Dqn_u64 result = Dqn_Str_ToU64("-1"); + DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %zu", result); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"1.2\""); - Dqn_u64 result = Dqn_Str_ToU64("1.2"); - DQN_TEST_EXPECT_MSG(testing_state, result == 1, "result: %zu", result); - } + { + DQN_TEST_START_SCOPE(testing_state, "To U64: Convert \"1.2\""); + Dqn_u64 result = Dqn_Str_ToU64("1.2"); + DQN_TEST_EXPECT_MSG(testing_state, result == 1, "result: %zu", result); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"1,234\""); - Dqn_u64 result = Dqn_Str_ToU64("1,234"); - DQN_TEST_EXPECT_MSG(testing_state, result == 1234, "result: %zu", result); - } + { + DQN_TEST_START_SCOPE(testing_state, "To U64: Convert \"1,234\""); + Dqn_u64 result = Dqn_Str_ToU64("1,234"); + DQN_TEST_EXPECT_MSG(testing_state, result == 1234, "result: %zu", result); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"1,2\""); - Dqn_u64 result = Dqn_Str_ToU64("1,2"); - DQN_TEST_EXPECT_MSG(testing_state, result == 12, "result: %zu", result); - } + { + DQN_TEST_START_SCOPE(testing_state, "To U64: Convert \"1,2\""); + Dqn_u64 result = Dqn_Str_ToU64("1,2"); + DQN_TEST_EXPECT_MSG(testing_state, result == 12, "result: %zu", result); + } - { - DQN_TEST_START_SCOPE(testing_state, "Convert \"12a3\""); - Dqn_u64 result = Dqn_Str_ToU64("12a3"); - DQN_TEST_EXPECT_MSG(testing_state, result == 12, "result: %zu", result); - } + { + DQN_TEST_START_SCOPE(testing_state, "To U64: Convert \"12a3\""); + Dqn_u64 result = Dqn_Str_ToU64("12a3"); + DQN_TEST_EXPECT_MSG(testing_state, result == 12, "result: %zu", result); } // --------------------------------------------------------------------------------------------- // NOTE: Dqn_Str_Find // --------------------------------------------------------------------------------------------- { - DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Str_Find"); - { - DQN_TEST_START_SCOPE(testing_state, "String (char) is not in buffer"); - char const buf[] = "836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"; - char const find[] = "2"; - char const *result = Dqn_Str_Find(buf, find, Dqn_CharCountI(buf), Dqn_CharCountI(find)); - DQN_TEST_EXPECT(testing_state, result == nullptr); - } + DQN_TEST_START_SCOPE(testing_state, "Find: String (char) is not in buffer"); + char const buf[] = "836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"; + char const find[] = "2"; + char const *result = Dqn_Str_Find(buf, find, Dqn_CharCountI(buf), Dqn_CharCountI(find)); + DQN_TEST_EXPECT(testing_state, result == nullptr); + } - { - DQN_TEST_START_SCOPE(testing_state, "String (char) is in buffer"); - char const buf[] = "836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"; - char const find[] = "6"; - char const *result = Dqn_Str_Find(buf, find, Dqn_CharCountI(buf), Dqn_CharCountI(find)); - DQN_TEST_EXPECT(testing_state, result != nullptr); - DQN_TEST_EXPECT(testing_state, result[0] == '6' && result[1] == 'a'); - } + { + DQN_TEST_START_SCOPE(testing_state, "Find: String (char) is in buffer"); + char const buf[] = "836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55"; + char const find[] = "6"; + char const *result = Dqn_Str_Find(buf, find, Dqn_CharCountI(buf), Dqn_CharCountI(find)); + DQN_TEST_EXPECT(testing_state, result != nullptr); + DQN_TEST_EXPECT(testing_state, result[0] == '6' && result[1] == 'a'); + } + + // --------------------------------------------------------------------------------------------- + // NOTE: Dqn_Str_FileNameFromPath + // --------------------------------------------------------------------------------------------- + { + DQN_TEST_START_SCOPE(testing_state, "File name from Windows path"); + Dqn_isize file_name_size = 0; + char const buf[] = "C:\\ABC\\test.exe"; + char const *result = Dqn_Str_FileNameFromPath(buf, Dqn_CharCountI(buf), &file_name_size); + DQN_TEST_EXPECT_MSG(testing_state, file_name_size == 8, "size: %I64d", file_name_size); + DQN_TEST_EXPECT_MSG(testing_state, Dqn_String_Init(result, file_name_size) == DQN_STRING("test.exe"), "%.*s", file_name_size, result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "File name from Linux path"); + Dqn_isize file_name_size = 0; + char const buf[] = "/ABC/test.exe"; + char const *result = Dqn_Str_FileNameFromPath(buf, Dqn_CharCountI(buf), &file_name_size); + DQN_TEST_EXPECT_MSG(testing_state, file_name_size == 8, "size: %I64d", file_name_size); + DQN_TEST_EXPECT_MSG(testing_state, Dqn_String_Init(result, file_name_size) == DQN_STRING("test.exe"), "%.*s", file_name_size, result); + } + + // --------------------------------------------------------------------------------------------- + // NOTE: Dqn_Str_TrimPrefix + // --------------------------------------------------------------------------------------------- + { + DQN_TEST_START_SCOPE(testing_state, "Trim prefix"); + char const prefix[] = "@123"; + char const buf[] = "@123string"; + Dqn_isize trimmed_size = 0; + char const *result = Dqn_Str_TrimPrefix(buf, Dqn_CharCountI(buf), prefix, Dqn_CharCountI(prefix), &trimmed_size); + DQN_TEST_EXPECT_MSG(testing_state, trimmed_size == 6, "size: %I64d", trimmed_size); + DQN_TEST_EXPECT_MSG(testing_state, Dqn_String_Init(result, trimmed_size) == DQN_STRING("string"), "%.*s", trimmed_size, result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Trim prefix, nullptr trimmed size"); + char const prefix[] = "@123"; + char const buf[] = "@123string"; + char const *result = Dqn_Str_TrimPrefix(buf, Dqn_CharCountI(buf), prefix, Dqn_CharCountI(prefix), nullptr); + DQN_TEST_EXPECT(testing_state, result); + } + + // --------------------------------------------------------------------------------------------- + // NOTE: Dqn_Str_IsAllDigits + // --------------------------------------------------------------------------------------------- + { + DQN_TEST_START_SCOPE(testing_state, "Is all digits fails on non-digit string"); + char const buf[] = "@123string"; + Dqn_b32 result = Dqn_Str_IsAllDigits(buf, Dqn_CharCountI(buf)); + DQN_TEST_EXPECT(testing_state, result == false); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Is all digits fails on nullptr"); + Dqn_b32 result = Dqn_Str_IsAllDigits(nullptr, 0); + DQN_TEST_EXPECT(testing_state, result == false); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Is all digits fails on nullptr w/ size"); + Dqn_b32 result = Dqn_Str_IsAllDigits(nullptr, 1); + DQN_TEST_EXPECT(testing_state, result == false); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Is all digits fails on 0 size w/ string"); + char const buf[] = "@123string"; + Dqn_b32 result = Dqn_Str_IsAllDigits(buf, 0); + DQN_TEST_EXPECT(testing_state, result == false); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Is all digits success"); + char const buf[] = "23"; + Dqn_b32 result = Dqn_Str_IsAllDigits(buf, Dqn_CharCountI(buf)); + DQN_TEST_EXPECT(testing_state, DQN_CAST(bool)result == true); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Is all digits fails on whitespace"); + char const buf[] = "23 "; + Dqn_b32 result = Dqn_Str_IsAllDigits(buf, Dqn_CharCountI(buf)); + DQN_TEST_EXPECT(testing_state, DQN_CAST(bool)result == false); } } @@ -1113,6 +1190,78 @@ void Dqn_Test_String() DQN_TEST_EXPECT_MSG(testing_state, string.str[1] == 'B', "string[1]: %c", string.str[1]); DQN_TEST_EXPECT_MSG(testing_state, string.str[2] == 0, "string[2]: %c", string.str[2]); } + + // --------------------------------------------------------------------------------------------- + // NOTE: Dqn_Str_Trim[Prefix/Suffix] + // --------------------------------------------------------------------------------------------- + { + DQN_TEST_START_SCOPE(testing_state, "Trim prefix with matching prefix"); + Dqn_String input = DQN_STRING("nft/abc"); + Dqn_String result = Dqn_String_TrimPrefix(input, DQN_STRING("nft/")); + DQN_TEST_EXPECT_MSG(testing_state, result == DQN_STRING("abc"), "%.*s", DQN_STRING_FMT(result)); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Trim prefix with non matching prefix"); + Dqn_String input = DQN_STRING("nft/abc"); + Dqn_String result = Dqn_String_TrimPrefix(input, DQN_STRING(" ft/")); + DQN_TEST_EXPECT_MSG(testing_state, result == input, "%.*s", DQN_STRING_FMT(result)); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Trim suffix with matching suffix"); + Dqn_String input = DQN_STRING("nft/abc"); + Dqn_String result = Dqn_String_TrimSuffix(input, DQN_STRING("abc")); + DQN_TEST_EXPECT_MSG(testing_state, result == DQN_STRING("nft/"), "%.*s", DQN_STRING_FMT(result)); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Trim suffix with non matching suffix"); + Dqn_String input = DQN_STRING("nft/abc"); + Dqn_String result = Dqn_String_TrimSuffix(input, DQN_STRING("ab")); + DQN_TEST_EXPECT_MSG(testing_state, result == input, "%.*s", DQN_STRING_FMT(result)); + } + + + // --------------------------------------------------------------------------------------------- + // NOTE: Dqn_String_IsAllDigits + // --------------------------------------------------------------------------------------------- + { + DQN_TEST_START_SCOPE(testing_state, "Is all digits fails on non-digit string"); + Dqn_b32 result = Dqn_String_IsAllDigits(DQN_STRING("@123string")); + DQN_TEST_EXPECT(testing_state, result == false); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Is all digits fails on nullptr"); + Dqn_b32 result = Dqn_String_IsAllDigits(Dqn_String_Init(nullptr, 0)); + DQN_TEST_EXPECT(testing_state, result == false); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Is all digits fails on nullptr w/ size"); + Dqn_b32 result = Dqn_String_IsAllDigits(Dqn_String_Init(nullptr, 1)); + DQN_TEST_EXPECT(testing_state, result == false); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Is all digits fails on string w/ 0 size"); + char const buf[] = "@123string"; + Dqn_b32 result = Dqn_String_IsAllDigits(Dqn_String_Init(buf, 0)); + DQN_TEST_EXPECT(testing_state, result == false); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Is all digits success"); + Dqn_b32 result = Dqn_String_IsAllDigits(DQN_STRING("23")); + DQN_TEST_EXPECT(testing_state, DQN_CAST(bool)result == true); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Is all digits fails on whitespace"); + Dqn_b32 result = Dqn_String_IsAllDigits(DQN_STRING("23 ")); + DQN_TEST_EXPECT(testing_state, DQN_CAST(bool)result == false); + } } void Dqn_Test_StringBuilder()