From 84bfc9136226501de64ede9c94890a2cf1300963 Mon Sep 17 00:00:00 2001 From: doyle Date: Wed, 22 Mar 2023 23:32:23 +1100 Subject: [PATCH] dqn: Allow DSMap to accept arbitrary keys --- dqn.h | 388 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 234 insertions(+), 154 deletions(-) diff --git a/dqn.h b/dqn.h index 00b3c95..11759b8 100644 --- a/dqn.h +++ b/dqn.h @@ -353,10 +353,12 @@ struct Dqn_String8 ///< Pointer and length style UTF8 strings char *data; ///< The UTF8 bytes of the string Dqn_isize size; ///< The number of bytes in the string + #if defined(__cplusplus) char const *begin() const { return data; } ///< Const begin iterator for range-for loops char const *end () const { return data + size; } ///< Const end iterator for range-for loops char *begin() { return data; } ///< Begin iterator for range-for loops char *end () { return data + size; } ///< End iterator for range-for loops + #endif }; // ---------------------------------+-----------------------------+--------------------------------- @@ -870,10 +872,10 @@ DQN_API Dqn_isize Dqn_CString8_FVSize(char const *fmt, va_list args); /// returned if the arguments were invalid. DQN_API char const *Dqn_CString8_BinarySplit(char const *string, Dqn_isize string_size, char delimiter, Dqn_isize *lhs_size, char **rhs, Dqn_isize *rhs_size); -enum struct Dqn_CString8EqCase +enum Dqn_CString8EqCase { - Sensitive, - Insensitive, + Dqn_CString8EqCase_Sensitive, + Dqn_CString8EqCase_Insensitive, }; /// Compare a string for equality with or without case sensitivity. @@ -884,7 +886,7 @@ enum struct Dqn_CString8EqCase /// @param[in] eq_case Set the comparison to be case sensitive or insensitive /// @return True if the arguments are valid, non-null and the strings /// are equal, false otherwise. -DQN_API bool Dqn_CString8_Eq(char const *lhs, char const *rhs, Dqn_isize lhs_size = -1, Dqn_isize rhs_size = -1, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive); +DQN_API bool Dqn_CString8_Eq(char const *lhs, char const *rhs, Dqn_isize lhs_size = -1, Dqn_isize rhs_size = -1, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive); /// Compare a string for equality, case insensitive /// @see Dqn_CString8_Eq @@ -898,7 +900,7 @@ DQN_API bool Dqn_CString8_EqInsensitive(char const *lhs, char const *rhs, Dqn_is /// @param[in] eq_case Set the comparison to be case sensitive or insensitive /// @return True if the string is valid, non-null and has the specified prefix, /// false otherwise. -DQN_API bool Dqn_CString8_StartsWith(char const *string, char const *prefix, Dqn_isize string_size = -1, Dqn_isize prefix_size = -1, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive); +DQN_API bool Dqn_CString8_StartsWith(char const *string, char const *prefix, Dqn_isize string_size = -1, Dqn_isize prefix_size = -1, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive); /// Check if a string starts with the specified prefix, case insensitive /// @see Dqn_CString8_StartsWith @@ -909,7 +911,7 @@ DQN_API bool Dqn_CString8_StartsWithInsensitive(char const *string, char const * /// @param[in] suffix The suffix to check against the string /// @param[in] eq_case Set the comparison to be case sensitive or insensitive /// @return True if the string has the specified suffix, false otherwise -DQN_API bool Dqn_CString8_EndsWith(char const *string, char const *suffix, Dqn_isize string_size = -1, Dqn_isize suffix_size = -1, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive); +DQN_API bool Dqn_CString8_EndsWith(char const *string, char const *suffix, Dqn_isize string_size = -1, Dqn_isize suffix_size = -1, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive); /// Check if a string ends with the specified suffix, case insensitive /// @param[in] string The string to check for the suffix @@ -929,7 +931,7 @@ DQN_API bool Dqn_CString8_EndsWithInsensitive(char const *string, char const *su /// /// @return The trimmed string. The original input string is returned if /// arguments are invalid or no trim was possible. -DQN_API char const *Dqn_CString8_TrimPrefix(char const *string, char const *prefix, Dqn_isize string_size = -1, Dqn_isize prefix_size = -1, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive, Dqn_isize *trimmed_size = nullptr); +DQN_API char const *Dqn_CString8_TrimPrefix(char const *string, char const *prefix, Dqn_isize string_size = -1, Dqn_isize prefix_size = -1, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive, Dqn_isize *trimmed_size = nullptr); /// Remove the prefix from the given `string` if it ends with it by /// adjusting the input string size. @@ -943,7 +945,7 @@ DQN_API char const *Dqn_CString8_TrimPrefix(char const *string, char const *pref /// /// @return The trimmed string. The original input string is returned if /// arguments are invalid or no trim was possible. -DQN_API char const *Dqn_CString8_TrimSuffix(char const *string, char const *suffix, Dqn_isize string_size = -1, Dqn_isize suffix_size = -1, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive, Dqn_isize *trimmed_size = nullptr); +DQN_API char const *Dqn_CString8_TrimSuffix(char const *string, char const *suffix, Dqn_isize string_size = -1, Dqn_isize suffix_size = -1, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive, Dqn_isize *trimmed_size = nullptr); /// Trim whitespace from the prefix and suffix of the string /// @@ -1079,10 +1081,12 @@ struct Dqn_String16 /// A pointer and length style string that holds slices to U wchar_t *data; ///< The UTF16 bytes of the string Dqn_isize size; ///< The number of bytes in the string + #if defined(__cplusplus) wchar_t const *begin() const { return data; } ///< Const begin iterator for range-for loops wchar_t const *end () const { return data + size; } ///< Const end iterator for range-for loops wchar_t *begin() { return data; } ///< Begin iterator for range-for loops wchar_t *end () { return data + size; } ///< End iterator for range-for loops + #endif }; /// Initialise a string from a pointer and length @@ -1148,28 +1152,28 @@ DQN_API Dqn_String8 Dqn_String8_Copy_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allo DQN_API Dqn_String8 Dqn_String8_BinarySplit(Dqn_String8 string, char delimiter, Dqn_String8 *rhs); /// @see Dqn_CString8_Eq -DQN_API bool Dqn_String8_Eq(Dqn_String8 lhs, Dqn_String8 rhs, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive); +DQN_API bool Dqn_String8_Eq(Dqn_String8 lhs, Dqn_String8 rhs, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive); /// @see Dqn_CString8_EqInsensitive DQN_API bool Dqn_String8_EqInsensitive(Dqn_String8 lhs, Dqn_String8 rhs); /// @see Dqn_CString8_StartsWith -DQN_API bool Dqn_String8_StartsWith(Dqn_String8 string, Dqn_String8 prefix, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive); +DQN_API bool Dqn_String8_StartsWith(Dqn_String8 string, Dqn_String8 prefix, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive); /// @see Dqn_CString8_StartsWithInsensitive DQN_API bool Dqn_String8_StartsWithInsensitive(Dqn_String8 string, Dqn_String8 prefix); /// @see Dqn_CString8_EndsWith -DQN_API bool Dqn_String8_EndsWith(Dqn_String8 string, Dqn_String8 prefix, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive); +DQN_API bool Dqn_String8_EndsWith(Dqn_String8 string, Dqn_String8 prefix, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive); /// @see Dqn_CString8_EndsWithInsensitive DQN_API bool Dqn_String8_EndsWithInsensitive(Dqn_String8 string, Dqn_String8 prefix); /// @see Dqn_CString8_TrimPrefix -DQN_API Dqn_String8 Dqn_String8_TrimPrefix(Dqn_String8 string, Dqn_String8 prefix, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive); +DQN_API Dqn_String8 Dqn_String8_TrimPrefix(Dqn_String8 string, Dqn_String8 prefix, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive); /// @see Dqn_CString8_TrimSuffix -DQN_API Dqn_String8 Dqn_String8_TrimSuffix(Dqn_String8 string, Dqn_String8 suffix, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive); +DQN_API Dqn_String8 Dqn_String8_TrimSuffix(Dqn_String8 string, Dqn_String8 suffix, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive); /// @see Dqn_CString8_TrimWhitespaceAround DQN_API Dqn_String8 Dqn_String8_TrimWhitespaceAround(Dqn_String8 string); @@ -1218,16 +1222,21 @@ DQN_API void Dqn_String8_Remove(Dqn_String8 *string, Dqn_isize begin, Dqn /// @param[in] start_index Set an index within the string string to start the /// search from, if not desired, set to 0 /// @return The index of the matching find, -1 if it is not found -DQN_API Dqn_isize Dqn_String8_FindOffset(Dqn_String8 string, Dqn_String8 find, Dqn_isize start_index, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive); +DQN_API Dqn_isize Dqn_String8_FindOffset(Dqn_String8 string, Dqn_String8 find, Dqn_isize start_index, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive); /// @param start_index Set an index within the string string to start the search /// from, if not desired, set to 0 /// @return A string that points to the matching find, otherwise a 0 length string. -DQN_API Dqn_String8 Dqn_String8_Find(Dqn_String8 string, Dqn_String8 find, Dqn_isize start_index, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive); +DQN_API Dqn_String8 Dqn_String8_Find(Dqn_String8 string, Dqn_String8 find, Dqn_isize start_index, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive); -DQN_API Dqn_String8 Dqn_String8_Replace(Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_isize start_index, Dqn_Allocator allocator, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase::Sensitive); +DQN_API Dqn_String8 Dqn_String8_Replace(Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_isize start_index, Dqn_Allocator allocator, Dqn_CString8EqCase eq_case = Dqn_CString8EqCase_Sensitive); DQN_API Dqn_String8 Dqn_String8_ReplaceInsensitive(Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_isize start_index, Dqn_Allocator allocator); +#if defined(__cplusplus) +bool operator==(Dqn_String8 const &lhs, Dqn_String8 const &rhs); +bool operator!=(Dqn_String8 const &lhs, Dqn_String8 const &rhs); +#endif + // ---------------------------------+-----------------------------+--------------------------------- // [SECT-PRIN] Dqn_Print | | Console printing // ---------------------------------+-----------------------------+--------------------------------- @@ -1498,16 +1507,22 @@ template void Dqn_Map_Erase(Dqn_Map *map, uint64_t hash, Dqn_Zer static_assert((DQN_DSMAP_MIN_SIZE & (DQN_DSMAP_MIN_SIZE - 1)) == 0, "DSMap lazy initialisation size must be a power of two"); #endif -template +#define DQN_DS_MAP_API template DQN_API +#define DQN_DS_MAP Dqn_DSMap + +template struct Dqn_DSMapItem { - uint64_t hash; - T value; - uint8_t occupied; - Dqn_DSMapItem *next; - Dqn_DSMapItem *prev; + K key; + V value; + uint8_t occupied; + Dqn_DSMapItem *next; + Dqn_DSMapItem *prev; }; +/// The hashing function prototype +using Dqn_DSMapHashProc = uint64_t(void *data, size_t size); + /// A hash table configured using the presets recommended by Demitri Spanos from /// the Handmade Network, /// - 70% max load @@ -1515,15 +1530,16 @@ struct Dqn_DSMapItem /// - linear probing on collision /// - tombstoneless deletes (e.g. deletes slots get filled with linear probed /// entries if there are any). -template +template struct Dqn_DSMap { - Dqn_Arena arena; - Dqn_DSMapItem *slots; - Dqn_isize size; ///< The number of slots - Dqn_isize count; ///< The number of slots occupied in the list - Dqn_DSMapItem *link_head; ///< Linked list of items allocated in the map - Dqn_DSMapItem *link_tail; ///< Linked list of items allocated in the map + Dqn_Arena arena; + Dqn_DSMapItem *slots; + Dqn_isize size; ///< The number of slots + Dqn_isize count; ///< The number of slots occupied in the list + Dqn_DSMapItem *link_head; ///< Linked list of items allocated in the map + Dqn_DSMapItem *link_tail; ///< Linked list of items allocated in the map + Dqn_DSMapHashProc *hash_function; }; /// Initialise a hash table with a given size. The given size must be a power of @@ -1531,29 +1547,32 @@ struct Dqn_DSMap /// the hash table will default to the size specified in DQN_DSMAP_MIN_SIZE. /// @param[in] size The number of slots in the hash-table. This value must be /// a power of two. -template -Dqn_DSMap Dqn_DSMap_Init(Dqn_isize size); +DQN_DS_MAP_API +DQN_DS_MAP Dqn_DSMap_Init(Dqn_isize size); -template -void Dqn_DSMap_Free(Dqn_DSMap *map, Dqn_ZeroMem zero_mem); +DQN_DS_MAP_API +void Dqn_DSMap_Free(DQN_DS_MAP *map, Dqn_ZeroMem zero_mem); -template -Dqn_DSMapItem *Dqn_DSMap_Find(Dqn_DSMap *map, uint64_t hash); +DQN_DS_MAP_API +Dqn_DSMapItem *Dqn_DSMap_Find(DQN_DS_MAP *map, K const &key); enum Dqn_DSMapFindOnly { Dqn_DSMapFindOnly_No, Dqn_DSMapFindOnly_Yes }; enum Dqn_DSMapItemCreated { Dqn_DSMapItemCreated_No, Dqn_DSMapItemCreated_Yes }; -template -Dqn_DSMapItem *Dqn_DSMap_FindOrAdd(Dqn_DSMap *map, uint64_t hash, Dqn_DSMapFindOnly find_only, Dqn_DSMapItemCreated *created); +DQN_DS_MAP_API +Dqn_DSMapItem *Dqn_DSMap_FindOrAdd(DQN_DS_MAP *map, K const &key, Dqn_DSMapFindOnly find_only, Dqn_DSMapItemCreated *created); -template -Dqn_DSMapItem *Dqn_DSMap_Add(Dqn_DSMap *map, uint64_t hash, T const &value); +DQN_DS_MAP_API +Dqn_DSMapItem *Dqn_DSMap_Add(DQN_DS_MAP *map, K const &key, V const &value); -template -Dqn_DSMapItem *Dqn_DSMap_Get(Dqn_DSMap *map, uint64_t hash); +DQN_DS_MAP_API +Dqn_DSMapItem *Dqn_DSMap_Make(DQN_DS_MAP *map, K const &key); -template -void Dqn_DSMap_Erase(Dqn_DSMap *map, uint64_t hash, Dqn_ZeroMem zero_mem); +DQN_DS_MAP_API +Dqn_DSMapItem *Dqn_DSMap_Get(DQN_DS_MAP *map, K const &key); + +DQN_DS_MAP_API +void Dqn_DSMap_Erase(DQN_DS_MAP *map, K const &key, Dqn_ZeroMem zero_mem); #endif // !defined(DQN_NO_DSMAP) // ---------------------------------+-----------------------------+--------------------------------- @@ -1869,25 +1888,37 @@ struct Dqn_List Dqn_ListChunk *tail; }; -template DQN_API Dqn_List Dqn_List_InitWithArena(Dqn_Arena *arena, Dqn_isize chunk_size = 128); - -// Produce an iterator for the data in the list -/* - Dqn_List list = {}; - for (Dqn_ListIterator it = {}; Dqn_List_Iterate(&list, &it, 0);) - { - int *item = it.data; - } -*/ -// start_index: The index to start iterating from -template DQN_API bool Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *iterator, Dqn_isize start_index); - -// at_chunk: (Optional) The chunk that the index belongs to will be set in this parameter if given -// return: The element, or null pointer if it is not a valid index. -template DQN_API T *Dqn_List_At(Dqn_List *list, Dqn_isize index, Dqn_ListChunk *at_chunk); +template DQN_API +Dqn_List Dqn_List_InitWithArena(Dqn_Arena *arena, Dqn_isize chunk_size = 128); #define Dqn_List_Make(list, count) Dqn_List_Make_(DQN_LEAK_TRACE list, count) -template DQN_API T *Dqn_List_Make_(DQN_LEAK_TRACE_FUNCTION Dqn_List *list, Dqn_isize count); +template DQN_API +T *Dqn_List_Make_(DQN_LEAK_TRACE_FUNCTION Dqn_List *list, Dqn_isize count); + +#define Dqn_List_Add(list, count) Dqn_List_Add_(DQN_LEAK_TRACE list, count) +template DQN_API +T *Dqn_List_Add_(DQN_LEAK_TRACE_FUNCTION Dqn_List *list, Dqn_isize count); + +/// @param at_chunk[out] (Optional) The chunk that the index belongs to will +/// be set in this parameter if given +/// @return The element, or null pointer if it is not a valid index. +template DQN_API +T *Dqn_List_At(Dqn_List *list, Dqn_isize index, Dqn_ListChunk *at_chunk); + +/// Produce an iterator for the data in the list +/// +/// @param[in] start_index The index to start iterating from +/// +/// @begincode +/// Dqn_List list = {}; +/// for (Dqn_ListIterator it = {}; Dqn_List_Iterate(&list, &it, 0);) +/// { +/// int *item = it.data; +/// } +/// @endcode +/// +template DQN_API +bool Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *it, Dqn_isize start_index); // ---------------------------------+-----------------------------+--------------------------------- // [SECT-MATH] Math | DQN_NO_MATH | v2i, V2, V3, V4, Mat4, Rect, RectI32, Lerp @@ -2547,14 +2578,14 @@ DQN_API uint64_t Dqn_Date_EpochTime(); // ---------------------------------+-----------------------------+--------------------------------- // [SECT-WIND] Dqn_Win | | Windows OS helpers // ---------------------------------+-----------------------------+--------------------------------- -struct Dqn_Win_ErrorMsg +struct Dqn_WinErrorMsg { unsigned long code; char data[DQN_KILOBYTES(64) - 1]; // Maximum error size unsigned long size; }; -DQN_API void Dqn_Win_LastErrorToBuffer(Dqn_Win_ErrorMsg *msg); -DQN_API Dqn_Win_ErrorMsg Dqn_Win_LastError(); +DQN_API void Dqn_Win_LastErrorToBuffer(Dqn_WinErrorMsg *msg); +DQN_API Dqn_WinErrorMsg Dqn_Win_LastError(); /// Call once at application start-up to ensure that the application is DPI /// aware on Windows and ensure that application UI is scaled up appropriately @@ -3114,11 +3145,11 @@ DQN_API uint64_t Dqn_FNV1A64_Iterate(void const *bytes, Dqn_isize size, uint64_t /// algorithms are optimized for their respective platforms. You can still /// compile and run any of them on any platform, but your performance with the /// non-native version will be less than optimal. -struct Dqn_MurmurHash3128 { uint64_t e[2]; }; +struct Dqn_MurmurHash3 { uint64_t e[2]; }; -DQN_API uint32_t Dqn_MurmurHash3x86_32 (void const *key, int len, uint32_t seed); -DQN_API Dqn_MurmurHash3128 Dqn_MurmurHash3x64_128(void const *key, int len, uint32_t seed); -#define DQN_MURMUR_HASH3_U128_AS_U64(key, len, seed) (Dqn_MurmurHash3x64_128(key, len, seed).e[0]) +DQN_API uint32_t Dqn_MurmurHash3_x86U32 (void const *key, int len, uint32_t seed); +DQN_API Dqn_MurmurHash3 Dqn_MurmurHash3_x64U128(void const *key, int len, uint32_t seed); +#define Dqn_MurmurHash3_x64U128AsU64(key, len, seed) (Dqn_MurmurHash3_x64U128(key, len, seed).e[0]) // NOTE: Dqn_Map Template Implementation // ------------------------------------------------------------------------------------------------- @@ -3235,34 +3266,48 @@ void Dqn_Map_Erase(Dqn_Map *map, uint64_t hash, Dqn_ZeroMem zero_mem) // NOTE: Dqn_DSMap Template Implementation // ------------------------------------------------------------------------------------------------- #if !defined(DQN_NO_DSMAP) -template -Dqn_DSMap Dqn_DSMap_Init(Dqn_isize size) +DQN_DS_MAP_API +DQN_DS_MAP Dqn_DSMap_Init(Dqn_isize size) { - Dqn_DSMap result = {}; + DQN_DS_MAP result = {}; if (Dqn_Safe_AssertF(size > 0 && (size & (size - 1)) == 0, "Non-zero & power of 2 table size required")) { - result.slots = Dqn_Arena_NewArray(&result.arena, Dqn_DSMapItem, size, Dqn_ZeroMem_Yes); + // TODO: We need to use placement new for C++ crap + result.slots = (Dqn_DSMapItem *)Dqn_Arena_Allocate_(DQN_LEAK_TRACE &result.arena, + sizeof(Dqn_DSMapItem) * size, + alignof(Dqn_DSMapItem), + Dqn_ZeroMem_Yes); if (result.slots) result.size = size; } return result; } -template -void Dqn_DSMap_Free(Dqn_DSMap *map, Dqn_ZeroMem zero_mem) +DQN_DS_MAP_API +void Dqn_DSMap_Free(DQN_DS_MAP *map, Dqn_ZeroMem zero_mem) { Dqn_Arena_Free(&map->arena, zero_mem); *map = {}; } -template -Dqn_DSMapItem *Dqn_DSMap_Find(Dqn_DSMap *map, uint64_t hash) +DQN_DS_MAP_API +Dqn_DSMapItem *Dqn_DSMap_Find(DQN_DS_MAP *map, uint64_t hash) { - Dqn_DSMapItem *result = Dqn_DSMap_FindOrAdd(map, hash, true /*find_only*/); + Dqn_DSMapItem *result = Dqn_DSMap_FindOrAdd(map, hash, true /*find_only*/); return result; } -template -Dqn_DSMapItem *Dqn_DSMap_FindOrAdd(Dqn_DSMap *map, uint64_t hash, Dqn_DSMapFindOnly find_only, Dqn_DSMapItemCreated *created) +DQN_FILE_SCOPE uint64_t Dqn_DSMap_KeyHash_(Dqn_DSMapHashProc *hash_function, void const *key, int key_size) +{ + uint64_t result; + if (hash_function) + result = hash_function(&key, key_size); + else + result = Dqn_MurmurHash3_x64U128AsU64(&key, key_size, 0xc8f451ac/*seed*/); + return result; +} + +DQN_DS_MAP_API +Dqn_DSMapItem *Dqn_DSMap_FindOrAdd(DQN_DS_MAP *map, K const &key, Dqn_DSMapFindOnly find_only, Dqn_DSMapItemCreated *created) { if (created) *created = Dqn_DSMapItemCreated_No; @@ -3274,26 +3319,27 @@ Dqn_DSMapItem *Dqn_DSMap_FindOrAdd(Dqn_DSMap *map, uint64_t hash, Dqn_DSMa if (find_only) return nullptr; else - *map = Dqn_DSMap_Init(DQN_DSMAP_MIN_SIZE); + *map = Dqn_DSMap_Init(DQN_DSMAP_MIN_SIZE); } // NOTE: Search for a vacant entry, linear probe if hash collision - Dqn_isize index = hash % map->size; - Dqn_DSMapItem *result = map->slots + index; - while (result->occupied && result->hash != hash) { + uint64_t hash = Dqn_DSMap_KeyHash_(map->hash_function, &key, sizeof(key)); + Dqn_isize index = hash % map->size; + Dqn_DSMapItem *result = map->slots + index; + while (result->occupied && result->key != key) { index = (index + 1) % map->size; result = map->slots + index; } if (result->occupied) { - DQN_ASSERT_MSG(result->hash == hash, + DQN_ASSERT_MSG(result->key == key, "We have a max load factor of 70%% so we should never get an occupied slot that doesn't match " "the hash we were searching for"); } else if (find_only == Dqn_DSMapFindOnly_Yes) { result = nullptr; } else { // NOTE: Fill the entry - result->hash = hash; + result->key = key; result->occupied = true; if (created) @@ -3312,13 +3358,13 @@ Dqn_DSMapItem *Dqn_DSMap_FindOrAdd(Dqn_DSMap *map, uint64_t hash, Dqn_DSMa Dqn_f32 load_factor = ++map->count / DQN_CAST(Dqn_f32)map->size; if (load_factor >= 0.7f) { - auto new_map = Dqn_DSMap_Init(map->size << 1); - for (Dqn_DSMapItem *entry = map->link_head; entry; entry = entry->next) { + auto new_map = Dqn_DSMap_Init(map->size << 1); + for (Dqn_DSMapItem *entry = map->link_head; entry; entry = entry->next) { if (!entry->occupied) continue; - Dqn_DSMapItem *new_entry = Dqn_DSMap_Add(&new_map, entry->hash, entry->value); - if (new_entry->hash == hash) + Dqn_DSMapItem *new_entry = Dqn_DSMap_Add(&new_map, entry->key, entry->value); + if (new_entry->key == key) result = new_entry; } @@ -3330,31 +3376,39 @@ Dqn_DSMapItem *Dqn_DSMap_FindOrAdd(Dqn_DSMap *map, uint64_t hash, Dqn_DSMa return result; } -template -Dqn_DSMapItem *Dqn_DSMap_Add(Dqn_DSMap *map, uint64_t hash, T const &value) +DQN_DS_MAP_API +Dqn_DSMapItem *Dqn_DSMap_Add(DQN_DS_MAP *map, K const &key, V const &value) { - Dqn_DSMapItem *result = Dqn_DSMap_FindOrAdd(map, hash, Dqn_DSMapFindOnly_No, nullptr /*added*/); + Dqn_DSMapItem *result = Dqn_DSMap_FindOrAdd(map, key, Dqn_DSMapFindOnly_No, nullptr /*added*/); if (result) result->value = value; return result; } -template -Dqn_DSMapItem *Dqn_DSMap_Get(Dqn_DSMap *map, uint64_t hash) +DQN_DS_MAP_API +Dqn_DSMapItem *Dqn_DSMap_Make(DQN_DS_MAP *map, K const &key) { - Dqn_DSMapItem *result = Dqn_DSMap_FindOrAdd(map, hash, Dqn_DSMapFindOnly_Yes, nullptr /*added*/); + Dqn_DSMapItem *result = Dqn_DSMap_FindOrAdd(map, key, Dqn_DSMapFindOnly_No, nullptr /*added*/); return result; } -template -void Dqn_DSMap_Erase(Dqn_DSMap *map, uint64_t hash, Dqn_ZeroMem zero_mem) +DQN_DS_MAP_API +Dqn_DSMapItem *Dqn_DSMap_Get(DQN_DS_MAP *map, K const &key) +{ + Dqn_DSMapItem *result = Dqn_DSMap_FindOrAdd(map, key, Dqn_DSMapFindOnly_Yes, nullptr /*added*/); + return result; +} + +DQN_DS_MAP_API +void Dqn_DSMap_Erase(DQN_DS_MAP *map, K const &key, Dqn_ZeroMem zero_mem) { if (!map) return; - Dqn_isize index = hash % map->size; - Dqn_DSMapItem *result = map->slots + index; + uint64_t hash = Dqn_DSMap_KeyHash_(map, &key, sizeof(key)); + Dqn_isize index = hash % map->size; + Dqn_DSMapItem *result = map->slots + index; if (!result || !result->occupied) return; @@ -3372,8 +3426,8 @@ void Dqn_DSMap_Erase(Dqn_DSMap *map, uint64_t hash, Dqn_ZeroMem zero_mem) Dqn_isize start_index = index; Dqn_isize probe_index = index; for (;;) { - probe_index = (probe_index + 1) % map->size; - Dqn_DSMapItem *probe = map->slots + probe_index; + probe_index = (probe_index + 1) % map->size; + Dqn_DSMapItem *probe = map->slots + probe_index; if (!probe->occupied) break; @@ -3881,38 +3935,46 @@ DQN_API T *Dqn_List_Make_(DQN_LEAK_TRACE_FUNCTION Dqn_List *list, Dqn_isize c } template -DQN_API bool Dqn_List_IterateFrom(Dqn_List *list, Dqn_ListIterator *iterator, Dqn_isize start_index) +DQN_API T *Dqn_List_Add_(DQN_LEAK_TRACE_FUNCTION Dqn_List *list, T const &value) +{ + T *result = Dqn_List_Make_(list, 1); + *result = value; + return result; +} + +template +DQN_API bool Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *it, Dqn_isize start_index) { bool result = false; - if (!list || !iterator || start_index < 0 || list->chunk_size <= 0) + if (!list || !it || start_index < 0 || list->chunk_size <= 0) return result; - if (!iterator->init) { - *iterator = {}; + if (!it->init) { + *it = {}; if (start_index == 0) { - iterator->chunk = list->head; + it->chunk = list->head; } else { - Dqn_List_At(list, start_index, &iterator->chunk); + Dqn_List_At(list, start_index, &it->chunk); if (list->chunk_size > 0) - iterator->chunk_data_index = start_index % list->chunk_size; + it->chunk_data_index = start_index % list->chunk_size; } - iterator->init = true; + it->init = true; } - if (iterator->chunk) { - if (iterator->chunk_data_index >= iterator->chunk->count) { - iterator->chunk = iterator->chunk->next; - iterator->chunk_data_index = 0; + if (it->chunk) { + if (it->chunk_data_index >= it->chunk->count) { + it->chunk = it->chunk->next; + it->chunk_data_index = 0; } - if (iterator->chunk) { - iterator->data = iterator->chunk->data + iterator->chunk_data_index++; + if (it->chunk) { + it->data = it->chunk->data + it->chunk_data_index++; result = true; } } - if (!iterator->chunk) + if (!it->chunk) DQN_ASSERT(result == false); return result; } @@ -4676,7 +4738,7 @@ DQN_API bool Dqn_CString8_Eq(char const *lhs, char const *rhs, Dqn_isize lhs_siz bool result = lhs_size == rhs_size; if (result) { - if (eq_case == Dqn_CString8EqCase::Sensitive) { + if (eq_case == Dqn_CString8EqCase_Sensitive) { result = (DQN_MEMCMP(lhs, rhs, DQN_CAST(size_t)lhs_size) == 0); } else { for (Dqn_isize index = 0; index < lhs_size && result; index++) @@ -4689,7 +4751,7 @@ DQN_API bool Dqn_CString8_Eq(char const *lhs, char const *rhs, Dqn_isize lhs_siz DQN_API bool Dqn_CString8_EqInsensitive(char const *lhs, char const *rhs, Dqn_isize lhs_size, Dqn_isize rhs_size) { - bool result = Dqn_CString8_Eq(lhs, rhs, lhs_size, rhs_size, Dqn_CString8EqCase::Insensitive); + bool result = Dqn_CString8_Eq(lhs, rhs, lhs_size, rhs_size, Dqn_CString8EqCase_Insensitive); return result; } @@ -4708,7 +4770,7 @@ DQN_API bool Dqn_CString8_StartsWith(char const *string, char const *prefix, Dqn DQN_API bool Dqn_CString8_StartsWithInsensitive(char const *string, char const *prefix, Dqn_isize string_size, Dqn_isize prefix_size) { - bool result = Dqn_CString8_StartsWith(string, prefix, string_size, prefix_size, Dqn_CString8EqCase::Insensitive); + bool result = Dqn_CString8_StartsWith(string, prefix, string_size, prefix_size, Dqn_CString8EqCase_Insensitive); return result; } @@ -4729,7 +4791,7 @@ DQN_API bool Dqn_CString8_EndsWith(char const *string, char const *suffix, Dqn_i DQN_API bool Dqn_CString8_EndsWithInsensitive(char const *string, char const *suffix, Dqn_isize string_size, Dqn_isize suffix_size) { - bool result = Dqn_CString8_EndsWith(string, suffix, string_size, suffix_size, Dqn_CString8EqCase::Insensitive); + bool result = Dqn_CString8_EndsWith(string, suffix, string_size, suffix_size, Dqn_CString8EqCase_Insensitive); return result; } @@ -4813,11 +4875,11 @@ DQN_API char const *Dqn_CString8_TrimByteOrderMark(char const *string, Dqn_isize char const UTF32_BOM_LE[] = "\xFF\xFE\x00\x00"; Dqn_isize result_size = string_size; - result = Dqn_CString8_TrimPrefix(result, UTF8_BOM, result_size, DQN_CHAR_COUNT(UTF8_BOM), Dqn_CString8EqCase::Sensitive, &result_size); - result = Dqn_CString8_TrimPrefix(result, UTF16_BOM_BE, result_size, DQN_CHAR_COUNT(UTF16_BOM_BE), Dqn_CString8EqCase::Sensitive, &result_size); - result = Dqn_CString8_TrimPrefix(result, UTF16_BOM_LE, result_size, DQN_CHAR_COUNT(UTF16_BOM_LE), Dqn_CString8EqCase::Sensitive, &result_size); - result = Dqn_CString8_TrimPrefix(result, UTF32_BOM_BE, result_size, DQN_CHAR_COUNT(UTF32_BOM_BE), Dqn_CString8EqCase::Sensitive, &result_size); - result = Dqn_CString8_TrimPrefix(result, UTF32_BOM_LE, result_size, DQN_CHAR_COUNT(UTF32_BOM_LE), Dqn_CString8EqCase::Sensitive, &result_size); + result = Dqn_CString8_TrimPrefix(result, UTF8_BOM, result_size, DQN_CHAR_COUNT(UTF8_BOM), Dqn_CString8EqCase_Sensitive, &result_size); + result = Dqn_CString8_TrimPrefix(result, UTF16_BOM_BE, result_size, DQN_CHAR_COUNT(UTF16_BOM_BE), Dqn_CString8EqCase_Sensitive, &result_size); + result = Dqn_CString8_TrimPrefix(result, UTF16_BOM_LE, result_size, DQN_CHAR_COUNT(UTF16_BOM_LE), Dqn_CString8EqCase_Sensitive, &result_size); + result = Dqn_CString8_TrimPrefix(result, UTF32_BOM_BE, result_size, DQN_CHAR_COUNT(UTF32_BOM_BE), Dqn_CString8EqCase_Sensitive, &result_size); + result = Dqn_CString8_TrimPrefix(result, UTF32_BOM_LE, result_size, DQN_CHAR_COUNT(UTF32_BOM_LE), Dqn_CString8EqCase_Sensitive, &result_size); if (trimmed_size) *trimmed_size = result_size; @@ -5252,7 +5314,7 @@ DQN_API bool Dqn_String8_Eq(Dqn_String8 lhs, Dqn_String8 rhs, Dqn_CString8EqCase DQN_API bool Dqn_String8_EqInsensitive(Dqn_String8 lhs, Dqn_String8 rhs) { - bool result = Dqn_String8_Eq(lhs, rhs, Dqn_CString8EqCase::Insensitive); + bool result = Dqn_String8_Eq(lhs, rhs, Dqn_CString8EqCase_Insensitive); return result; } @@ -5264,7 +5326,7 @@ DQN_API bool Dqn_String8_StartsWith(Dqn_String8 string, Dqn_String8 prefix, Dqn_ DQN_API bool Dqn_String8_StartsWithInsensitive(Dqn_String8 string, Dqn_String8 prefix) { - bool result = Dqn_String8_StartsWith(string, prefix, Dqn_CString8EqCase::Insensitive); + bool result = Dqn_String8_StartsWith(string, prefix, Dqn_CString8EqCase_Insensitive); return result; } @@ -5276,7 +5338,7 @@ DQN_API bool Dqn_String8_EndsWith(Dqn_String8 string, Dqn_String8 suffix, Dqn_CS DQN_API bool Dqn_String8_EndsWithInsensitive(Dqn_String8 string, Dqn_String8 suffix) { - bool result = Dqn_String8_EndsWith(string, suffix, Dqn_CString8EqCase::Insensitive); + bool result = Dqn_String8_EndsWith(string, suffix, Dqn_CString8EqCase_Insensitive); return result; } @@ -5385,7 +5447,7 @@ DQN_API bool Dqn_String8_IsAllDigits(Dqn_String8 string) DQN_API bool Dqn_String8_IsAllHex(Dqn_String8 string) { - Dqn_String8 trimmed = Dqn_String8_TrimPrefix(string, DQN_STRING8("0x"), Dqn_CString8EqCase::Insensitive); + Dqn_String8 trimmed = Dqn_String8_TrimPrefix(string, DQN_STRING8("0x"), Dqn_CString8EqCase_Insensitive); bool result = Dqn_String8_IsValid(trimmed) && trimmed.size > 0; for (Dqn_isize index = 0; result && index < trimmed.size; index++) { char ch = trimmed.data[index]; @@ -5493,10 +5555,24 @@ DQN_API Dqn_String8 Dqn_String8_Replace(Dqn_String8 string, DQN_API Dqn_String8 Dqn_String8_ReplaceInsensitive(Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_isize start_index, Dqn_Arena *arena, Dqn_Arena *temp_arena) { - Dqn_String8 result = Dqn_String8_Replace(string, find, replace, start_index, arena, temp_arena, Dqn_CString8EqCase::Insensitive); + Dqn_String8 result = Dqn_String8_Replace(string, find, replace, start_index, arena, temp_arena, Dqn_CString8EqCase_Insensitive); return result; } +#if defined(__cplusplus) +bool operator==(Dqn_String8 const &lhs, Dqn_String8 const &rhs) +{ + bool result = Dqn_String8_Eq(lhs, rhs, Dqn_CString8EqCase_Sensitive); + return result; +} + +bool operator!=(Dqn_String8 const &lhs, Dqn_String8 const &rhs) +{ + bool result = !(lhs == rhs); + return result; +} +#endif + // ---------------------------------+-----------------------------+--------------------------------- // [SECT-PRIN] Dqn_Print | | Printing // ---------------------------------+-----------------------------+--------------------------------- @@ -7271,7 +7347,7 @@ DQN_API uint64_t Dqn_Date_EpochTime() // ---------------------------------+-----------------------------+--------------------------------- // [SECT-WIND] Dqn_Win | | Windows OS helpers // ---------------------------------+-----------------------------+--------------------------------- -DQN_API void Dqn_Win_LastErrorToBuffer(Dqn_Win_ErrorMsg *msg) +DQN_API void Dqn_Win_LastErrorToBuffer(Dqn_WinErrorMsg *msg) { msg->code = GetLastError(); msg->data[0] = 0; @@ -7279,8 +7355,7 @@ DQN_API void Dqn_Win_LastErrorToBuffer(Dqn_Win_ErrorMsg *msg) unsigned long flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; void *module_to_get_errors_from = nullptr; - if (msg->code >= 12000 && msg->code <= 12175) // WinINET Errors - { + if (msg->code >= 12000 && msg->code <= 12175) { // WinINET Errors flags |= FORMAT_MESSAGE_FROM_HMODULE; module_to_get_errors_from = GetModuleHandleA("wininet.dll"); } @@ -7292,11 +7367,16 @@ DQN_API void Dqn_Win_LastErrorToBuffer(Dqn_Win_ErrorMsg *msg) msg->data, // LPSTR lpBuffer, DQN_ARRAY_ICOUNT(msg->data), // unsigned long nSize, nullptr); // va_list * Arguments + + if (msg->size >= 2 && + (msg->data[msg->size - 2] == '\r' && msg->data[msg->size - 1] == '\n')) { + msg->size -= 2; + } } -DQN_API Dqn_Win_ErrorMsg Dqn_Win_LastError() +DQN_API Dqn_WinErrorMsg Dqn_Win_LastError() { - Dqn_Win_ErrorMsg result = {}; + Dqn_WinErrorMsg result = {}; Dqn_Win_LastErrorToBuffer(&result); return result; } @@ -7326,7 +7406,7 @@ DQN_API void Dqn_Win_DumpLastError_(Dqn_CallSite call_site, char const *fmt, ... { // TODO(dqn): Hmmm .. should this be a separate log or part of the above // macro. If so we need to make the logging macros more flexible. - Dqn_Win_ErrorMsg msg = Dqn_Win_LastError(); + Dqn_WinErrorMsg msg = Dqn_Win_LastError(); if (fmt) { va_list args; va_start(args, fmt); @@ -8370,10 +8450,11 @@ DQN_API bool Dqn_Fs_Copy(Dqn_String8 src, Dqn_String8 dest, bool overwrite) result = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0; if (!result) { - Dqn_Log_ErrorF("Failed to copy file [from=%.*s, to=%.*s, reason=%.*s]", - DQN_STRING_FMT(src16), - DQN_STRING_FMT(dest16), - DQN_STRING_FMT(Dqn_Win_LastError())); + Dqn_WinErrorMsg error = Dqn_Win_LastError(); + Dqn_Log_ErrorF("Copy file failed [src=\"%.*s\", dest=\"%.*s\", reason=\"%.*s\"]", + DQN_STRING_FMT(src), + DQN_STRING_FMT(dest), + DQN_STRING_FMT(error)); } #elif defined(DQN_OS_UNIX) @@ -8591,7 +8672,7 @@ DQN_API char *Dqn_Fs_ReadCString8_(DQN_LEAK_TRACE_FUNCTION char const *path, Dqn Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); Dqn_String8 path8 = Dqn_String8_Init(path, path_size); Dqn_String16 path16 = Dqn_Win_String8ToString16Allocator(path8, scratch.allocator); - Dqn_Win_ErrorMsg error_msg = {}; + Dqn_WinErrorMsg error_msg = {}; // NOTE: Get the file handle // ------------------------------------------------------------------------- @@ -8812,7 +8893,7 @@ DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, u /*HANDLE hTemplateFile*/ nullptr); if (handle == INVALID_HANDLE_VALUE) { - Dqn_Win_ErrorMsg msg = Dqn_Win_LastError(); + Dqn_WinErrorMsg msg = Dqn_Win_LastError(); result.error_size = DQN_CAST(uint16_t) Dqn_SNPrintF2DotsOnOverflow(result.error, DQN_ARRAY_UCOUNT(result.error), @@ -8896,7 +8977,7 @@ DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, char const *buffer, Dqn_isize si } if (!result) { - Dqn_Win_ErrorMsg msg = Dqn_Win_LastError(); + Dqn_WinErrorMsg msg = Dqn_Win_LastError(); file->error_size = DQN_CAST(uint16_t) Dqn_SNPrintF2DotsOnOverflow(file->error, DQN_ARRAY_UCOUNT(file->error), @@ -9302,11 +9383,10 @@ void Dqn_JSONBuilder_Bool(Dqn_JSONBuilder *builder, bool value) #endif // !defined(DQN_NO_JSON_BUILDER) -// NOTE: Hashing - Dqn_FNV1A[32|64] -// ------------------------------------------------------------------------------------------------- -// +// ---------------------------------+-----------------------------+--------------------------------- +// [SECT-FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a +// ---------------------------------+-----------------------------+--------------------------------- // Default values recommended by: http://isthe.com/chongo/tech/comp/fnv/ -// DQN_API uint32_t Dqn_FNV1A32_Iterate(void const *bytes, Dqn_isize size, uint32_t hash) { auto buffer = DQN_CAST(uint8_t const *)bytes; @@ -9335,9 +9415,9 @@ DQN_API uint64_t Dqn_FNV1A64_Hash(void const *bytes, Dqn_isize size) return result; } -// NOTE: Hashing - Dqn_MurmurHash3 -// ------------------------------------------------------------------------------------------------- - +// ---------------------------------+-----------------------------+--------------------------------- +// [SECT-MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3 +// ---------------------------------+-----------------------------+--------------------------------- #if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG) #define DQN_MMH3_ROTL32(x, y) _rotl(x, y) #define DQN_MMH3_ROTL64(x, y) _rotl64(x, y) @@ -9383,7 +9463,7 @@ DQN_FORCE_INLINE uint64_t Dqn_MurmurHash3_FMix64(uint64_t k) return k; } -DQN_API uint32_t Dqn_MurmurHash3x86_32(void const *key, int len, uint32_t seed) +DQN_API uint32_t Dqn_MurmurHash3_x86U32(void const *key, int len, uint32_t seed) { const uint8_t *data = (const uint8_t *)key; const int nblocks = len / 4; @@ -9442,7 +9522,7 @@ DQN_API uint32_t Dqn_MurmurHash3x86_32(void const *key, int len, uint32_t seed) return h1; } -DQN_API Dqn_MurmurHash3128 Dqn_MurmurHash3x64_128(void const *key, int len, uint32_t seed) +DQN_API Dqn_MurmurHash3 Dqn_MurmurHash3_x64U128(void const *key, int len, uint32_t seed) { const uint8_t *data = (const uint8_t *)key; const int nblocks = len / 16; @@ -9548,9 +9628,9 @@ DQN_API Dqn_MurmurHash3128 Dqn_MurmurHash3x64_128(void const *key, int len, uint h1 += h2; h2 += h1; - Dqn_MurmurHash3128 result = {}; - result.e[0] = h1; - result.e[1] = h2; + Dqn_MurmurHash3 result = {}; + result.e[0] = h1; + result.e[1] = h2; return result; }