From 66a1af511bb417bbf3c0817ad97795f758194433 Mon Sep 17 00:00:00 2001 From: Jonathan Hoffstadt Date: Fri, 11 Apr 2025 21:56:26 -0500 Subject: [PATCH] feat: update and reset pl_ds.h --- pl_ds.h | 906 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 636 insertions(+), 270 deletions(-) diff --git a/pl_ds.h b/pl_ds.h index cfef355..6ac1d66 100644 --- a/pl_ds.h +++ b/pl_ds.h @@ -4,8 +4,8 @@ */ // library version (format XYYZZ) -#define PL_DS_VERSION "1.1.1" -#define PL_DS_VERSION_NUM 10101 +#define PL_DS_VERSION "1.0.0" +#define PL_DS_VERSION_NUM 10000 /* Index of this file: @@ -15,6 +15,7 @@ Index of this file: // [SECTION] forward declarations // [SECTION] public api (stretchy buffer) // [SECTION] public api (hashmap) +// [SECTION] public api (static hashmaps) // [SECTION] internal (stretchy buffer) // [SECTION] internal (hashmap) */ @@ -122,54 +123,67 @@ HASHMAPS Returns the CRC64 hash of a string. pl_hm_hash: - uint64_t pl_hm_hash(const void* pData, size_t szDataSize, uint64_t uSeed); + uint64_t pl_hm_hash(const void* pData, size_t dataSize, uint64_t seed); Returns the CRC64 hash of some arbitrary data. - pl_hm_resize: - void pl_hm_resize(plHashMap*, uint32_t); - Resizes the hashmap or frees it if zero is used. - pl_hm_free: - void pl_hm_free(plHashMap*); + void pl_hm_free(plHashMap64*); Frees the hashmap internal memory. pl_hm_insert: - void pl_hm_insert(plHashMap*, uint64_t ulKey, uint64_t ulValue); - Adds an entry to the hashmap where ulKey is a hashed key (usually a string) and - ulValue is the index into the value array. + void pl_hm_insert(plHashMap64*, uint64_t key, uint64_t value); + Adds an entry to the hashmap where key is a hashed key (usually a string) and + value is the index into the value array. pl_hm_remove: - void pl_hm_remove(plHashMap*, uint64_t ulKey); + void pl_hm_remove(plHashMap64*, uint64_t key); Removes an entry from the hashmap and adds the index to the free index list. pl_hm_lookup: - uint64_t pl_hm_lookup(plHashMap*, uint64_t ulKey); - Returns the index into the value array if it already exists or UINT64_MAX if not. + uint64_t pl_hm_lookup(plHashMap64*, uint64_t key); + Returns the index into the value array if it already exists or PL_DS_HASH_INVALID if not. pl_hm_get_free_index: - uint64_t pl_hm_get_free_index(plHashMap*); - Returns a free index if one exists or UINT64_MAX if not. + uint64_t pl_hm_get_free_index(plHashMap64*); + Returns a free index if one exists or PL_DS_HASH_INVALID if not. pl_hm_has_key: - bool pl_hm_has_key(plHashMap*, uint64_t); + bool pl_hm_has_key(plHashMap64*, uint64_t); Checks if key exists. + pl_hm_has_key_ex: + bool pl_hm_has_key_ex(plHashMap64*, uint64_t, uint64_t* puValue); + Checks if key exists and fills out puValue if present. + pl_hm_has_key_str: - bool pl_hm_has_key(plHashMap*, const char*); + bool pl_hm_has_key(plHashMap64*, const char*); + Same as pl_hm_has_key but performs the hash for you. + + pl_hm_has_key_str_ex: + bool pl_hm_has_key(plHashMap64*, const char*, uint64_t* puValue); Same as pl_hm_has_key but performs the hash for you. pl_hm_insert_str: - void pl_hm_insert_str(plHashMap*, const char* pcKey, uint64_t ulValue); + void pl_hm_insert_str(plHashMap64*, const char* key, uint64_t value); Same as pl__hm_insert but performs the hash for you. pl_hm_lookup_str: - uint64_t pl_hm_lookup_str(plHashMap*, const char* pcKey); + uint64_t pl_hm_lookup_str(plHashMap64*, const char* key); Same as pl_hm_lookup but performs the hash for you. pl_hm_remove_str: - void pl_hm_remove_str(plHashMap*, const char* pcKey); + void pl_hm_remove_str(plHashMap64*, const char* key); Same as pl_hm_remove but performs the hash for you. + pl_hm_size: + uint32_t pl_hm_lookup_str(plHashMap64*); + Returns the number of elements in the hashmap. + +DEFINES + + * PL_DS_HASH_INVALID + * PL_DS_HASH32_INVALID + COMPILE TIME OPTIONS * Change allocators by defining both: @@ -212,6 +226,9 @@ COMPILE TIME OPTIONS #define PL_DS_HASHMAP_INITIAL_SIZE 1024 #endif +#define PL_DS_HASH_INVALID UINT64_MAX +#define PL_DS_HASH32_INVALID UINT32_MAX + //----------------------------------------------------------------------------- // [SECTION] includes //----------------------------------------------------------------------------- @@ -299,41 +316,181 @@ COMPILE TIME OPTIONS // [SECTION] public api (hashmap) //----------------------------------------------------------------------------- -#define pl_hm_resize(ptHashMap, uBucketCount) \ - pl__hm_resize(&(ptHashMap), (uBucketCount), __FILE__, __LINE__) +typedef struct _plHashMap32 +{ + uint32_t _uItemCount; + uint32_t _uBucketCapacity; + uint64_t* _auKeys; // stored keys used for rehashing during growth -#define pl_hm_insert(ptHashMap, ulKey, ulValue) \ - pl__hm_insert(&(ptHashMap), (ulKey), (ulValue), __FILE__, __LINE__) + // specific to 32bit + uint32_t* _auValueBucket; // indices into value array (user held) + uint32_t* _sbuFreeIndices; // free list of available indices +} plHashMap32; -#define pl_hm_free(ptHashMap) \ - pl__hm_free(&(ptHashMap)) +typedef struct _plHashMap64 +{ + uint32_t _uItemCount; + uint32_t _uBucketCapacity; + uint64_t* _auKeys; // stored keys used for rehashing during growth -#define pl_hm_remove(ptHashMap, ulKey) \ - pl__hm_remove(&(ptHashMap), (ulKey)) + // specific to 64bit + uint64_t* _auValueBucket; // indices into value array (user held) + uint64_t* _sbuFreeIndices; // free list of available indices +} plHashMap64; -#define pl_hm_remove_str(ptHashMap, pcKey) \ - pl_hm_remove((ptHashMap), pl_hm_hash_str((pcKey))) +typedef plHashMap64 plHashMap; -#define pl_hm_lookup(ptHashMap, ulKey) \ - pl__hm_lookup(&(ptHashMap), (ulKey)) +// general +static inline uint64_t pl_hm_hash (const void* data, size_t dataSize, uint64_t seed); +static inline uint64_t pl_hm_hash_str(const char*, uint64_t seed); -#define pl_hm_get_free_index(ptHashMap) \ - pl__hm_get_free_index(&(ptHashMap)) +#define pl_hm_size(PLHM) \ + ((PLHM) ? (PLHM)->_uItemCount : 0) -#define pl_hm_has_key(ptHashMap, ulKey) \ - pl__hm_has_key(&(ptHashMap), (ulKey)) +#define pl_hm32_size pl_hm_size +#define pl_hm64_size pl_hm_size +#define pl_hm_insert pl_hm64_insert +#define pl_hm_insert_str pl_hm64_insert_str +#define pl_hm_lookup pl_hm64_lookup +#define pl_hm_has_key pl_hm64_has_key +#define pl_hm_has_key_ex pl_hm64_has_key_ex +#define pl_hm_get_free_index pl_hm64_get_free_index +#define pl_hm_free pl_hm64_free +#define pl_hm_remove pl_hm64_remove +#define pl_hm_lookup_str pl_hm64_lookup_str +#define pl_hm_remove_str pl_hm64_remove_str +#define pl_hm_has_key_str pl_hm64_has_key_str +#define pl_hm_has_key_str_ex pl_hm64_has_key_str_ex -#define pl_hm_has_key_str(ptHashMap, pcKey) \ - pl_hm_has_key((ptHashMap), pl_hm_hash_str((pcKey))) +// 64 bit +#define pl_hm64_insert(ptHashMap, uKey, uValue) \ + pl__hm_insert((ptHashMap), (uKey), (uValue), __FILE__, __LINE__) -#define pl_hm_lookup_str(ptHashMap, pcKey) \ - pl_hm_lookup((ptHashMap), pl_hm_hash_str((pcKey))) +#define pl_hm64_insert_str(ptHashMap, pcKey, uValue) \ + pl_hm64_insert((ptHashMap), (pl_hm_hash_str((pcKey), 0)), (uValue)) -#define pl_hm_insert_str(ptHashMap, pcKey, ulValue) \ - pl_hm_insert((ptHashMap), pl_hm_hash_str((pcKey)), (ulValue)) +static inline uint64_t pl_hm64_lookup (const plHashMap64*, uint64_t key); +static inline bool pl_hm64_has_key (const plHashMap64*, uint64_t key); +static inline bool pl_hm64_has_key_ex (const plHashMap64*, uint64_t key, uint64_t* valueOut); +static inline uint64_t pl_hm64_get_free_index(plHashMap64*); +static inline void pl_hm64_free (plHashMap64*); +static inline void pl_hm64_remove (plHashMap64*, uint64_t key); +static inline uint64_t pl_hm64_lookup_str (const plHashMap64*, const char* key); +static inline void pl_hm64_remove_str (plHashMap64*, const char* key); +static inline bool pl_hm64_has_key_str (const plHashMap64*, const char* key); +static inline bool pl_hm64_has_key_str_ex(const plHashMap64*, const char* key, uint64_t* valueOut); -static inline uint64_t pl_hm_hash_str(const char* pcKey); -static inline uint64_t pl_hm_hash (const void* pData, size_t szDataSize, uint64_t uSeed); +// 32 bit +#define pl_hm32_insert(ptHashMap, uKey, uValue) \ + pl__hm_insert32((ptHashMap), (uKey), (uValue), __FILE__, __LINE__) + +#define pl_hm32_insert_str(ptHashMap, pcKey, uValue) \ + pl_hm32_insert((ptHashMap), pl_hm_hash_str((pcKey), 0), (uValue)) + +static inline uint32_t pl_hm32_lookup (const plHashMap32*, uint64_t key); +static inline bool pl_hm32_has_key (const plHashMap32*, uint64_t key); +static inline bool pl_hm32_has_key_ex (const plHashMap32*, uint64_t key, uint32_t* valueOut); +static inline uint32_t pl_hm32_get_free_index(plHashMap32*); +static inline void pl_hm32_free (plHashMap32*); +static inline void pl_hm32_remove (plHashMap32*, uint64_t key); +static inline uint32_t pl_hm32_lookup_str (const plHashMap32*, const char* key); +static inline void pl_hm32_remove_str (plHashMap32*, const char* key); +static inline bool pl_hm32_has_key_str (const plHashMap32*, const char* key); +static inline bool pl_hm32_has_key_str_ex(const plHashMap32*, const char* key, uint32_t* valueOut); + +//----------------------------------------------------------------------------- +// [SECTION] public api (static hashmaps) +//----------------------------------------------------------------------------- + +typedef struct _plHashMapStatic64 +{ + uint64_t* auKeys; // stored keys used for rehashing during growth + uint64_t* auValueBucket; // indices into value array (user held) + uint32_t uBucketCount; +} plHashMapStatic64; + +typedef struct _plHashMapStatic32 +{ + uint64_t* auKeys; // stored keys used for rehashing during growth + uint32_t* auValueBucket; // indices into value array (user held) + uint32_t uBucketCount; +} plHashMapStatic32; + +static inline void +pl_hms_clear(plHashMapStatic64* ptHashmap) +{ + memset(ptHashmap->auKeys, 0xff, sizeof(*ptHashmap->auKeys) * ptHashmap->uBucketCount); + memset(ptHashmap->auValueBucket, 0xff, sizeof(*ptHashmap->auValueBucket) * ptHashmap->uBucketCount); +} + +static inline void +pl_hms_clear32(plHashMapStatic32* ptHashmap) +{ + memset(ptHashmap->auKeys, 0xff, sizeof(*ptHashmap->auKeys) * ptHashmap->uBucketCount); + memset(ptHashmap->auValueBucket, 0xff, sizeof(*ptHashmap->auValueBucket) * ptHashmap->uBucketCount); +} + +static inline void +pl_hms_set(plHashMapStatic64* ptHashmap, uint64_t uKey, uint64_t uValue) +{ + uint64_t uBucketIndex = uKey % ptHashmap->uBucketCount; + while(ptHashmap->auKeys[uBucketIndex] != uKey && ptHashmap->auKeys[uBucketIndex] != PL_DS_HASH_INVALID) + uBucketIndex = (uBucketIndex + 1) % ptHashmap->uBucketCount; + ptHashmap->auKeys[uBucketIndex] = uKey; + ptHashmap->auValueBucket[uBucketIndex] = uValue; +} + +static inline void +pl_hms_set32(plHashMapStatic32* ptHashmap, uint64_t uKey, uint32_t uValue) +{ + uint64_t uBucketIndex = uKey % ptHashmap->uBucketCount; + while(ptHashmap->auKeys[uBucketIndex] != uKey && ptHashmap->auKeys[uBucketIndex] != PL_DS_HASH_INVALID) + uBucketIndex = (uBucketIndex + 1) % ptHashmap->uBucketCount; + ptHashmap->auKeys[uBucketIndex] = uKey; + ptHashmap->auValueBucket[uBucketIndex] = uValue; +} + +static inline uint64_t +pl_hms_get(const plHashMapStatic64* ptHashmap, uint64_t uKey) +{ + uint64_t uBucketIndex = uKey % ptHashmap->uBucketCount; + while (ptHashmap->auKeys[uBucketIndex] != uKey && ptHashmap->auKeys[uBucketIndex] != PL_DS_HASH_INVALID) + uBucketIndex = (uBucketIndex + 1) % ptHashmap->uBucketCount; + return ptHashmap->auKeys[uBucketIndex] == PL_DS_HASH_INVALID ? PL_DS_HASH_INVALID : ptHashmap->auValueBucket[uBucketIndex]; +} + +static inline uint32_t +pl_hms_get32(const plHashMapStatic32* ptHashmap, uint64_t uKey) +{ + uint64_t uBucketIndex = uKey % ptHashmap->uBucketCount; + while (ptHashmap->auKeys[uBucketIndex] != uKey && ptHashmap->auKeys[uBucketIndex] != PL_DS_HASH_INVALID) + uBucketIndex = (uBucketIndex + 1) % ptHashmap->uBucketCount; + return ptHashmap->auKeys[uBucketIndex] == PL_DS_HASH_INVALID ? PL_DS_HASH32_INVALID : ptHashmap->auValueBucket[uBucketIndex]; +} + +static inline void +pl_hms_set_str(plHashMapStatic64* ptHashmap, const char* pcKey, uint64_t uValue) +{ + pl_hms_set(ptHashmap, pl_hm_hash_str(pcKey, 0), uValue); +} + +static inline void +pl_hms_set_str32(plHashMapStatic32* ptHashmap, const char* pcKey, uint32_t uValue) +{ + pl_hms_set32(ptHashmap, pl_hm_hash_str(pcKey, 0), uValue); +} + +static inline uint64_t +pl_hms_get_str(const plHashMapStatic64* ptHashmap, const char* pcKey) +{ + return pl_hms_get(ptHashmap, pl_hm_hash_str(pcKey, 0)); +} + +static inline uint32_t +pl_hms_get_str32(const plHashMapStatic32* ptHashmap, const char* pcKey) +{ + return pl_hms_get32(ptHashmap, pl_hm_hash_str(pcKey, 0)); +} //----------------------------------------------------------------------------- // [SECTION] internal (stretchy buffer) @@ -417,168 +574,10 @@ pl__sb_sprintf(char** ppcBuffer, const char* pcFormat, ...) // [SECTION] internal (hashmap) //----------------------------------------------------------------------------- -typedef struct plHashMap -{ - int _iMemoryOwned; // did we allocate this - uint32_t _uItemCount; - uint32_t _uBucketCount; - uint64_t* _aulKeys; // stored keys used for rehashing during growth - uint64_t* _aulValueIndices; // indices into value array (user held) - uint64_t* _sbulFreeIndices; // free list of available indices -} plHashMap; - -static inline uint64_t pl__hm_lookup (plHashMap** ptHashMap, uint64_t ulKey); -static inline uint64_t pl__hm_get_free_index(plHashMap** ptHashMap); -static inline bool pl__hm_has_key (plHashMap** pptHashMap, uint64_t ulKey); -static inline void pl__hm_resize (plHashMap** pptHashMap, uint32_t uBucketCount, const char* pcFile, int iLine); -static inline void pl__hm_free (plHashMap** pptHashMap) { pl__hm_resize(pptHashMap, 0, "", 0);} -static inline void pl__hm_insert (plHashMap** pptHashMap, uint64_t ulKey, uint64_t ulValue, const char* pcFile, int iLine); -static inline void pl__hm_remove (plHashMap** pptHashMap, uint64_t ulKey); - -static inline size_t -pl__ds_get_next_power_of_2(size_t n) -{ - size_t ulResult = 1; - if (n && !(n & (n - 1))) - ulResult = n; - while (ulResult < n) - ulResult <<= 1; - return ulResult; -} - -static inline void -pl__hm_resize(plHashMap** pptHashMap, uint32_t uBucketCount, const char* pcFile, int iLine) -{ - plHashMap* ptHashMap = *pptHashMap; - - if(ptHashMap == NULL && uBucketCount == 0) - return; - - if(ptHashMap == NULL) - { - ptHashMap = PL_DS_ALLOC(sizeof(plHashMap)); - memset(ptHashMap, 0, sizeof(plHashMap)); - ptHashMap->_iMemoryOwned = 1; - *pptHashMap = ptHashMap; - } - const uint32_t uOldBucketCount = ptHashMap->_uBucketCount; - uint64_t* sbulOldValueIndices = ptHashMap->_aulValueIndices; - uint64_t* aulOldKeys = ptHashMap->_aulKeys; - - - ptHashMap->_uBucketCount = uBucketCount < PL_DS_HASHMAP_INITIAL_SIZE ? PL_DS_HASHMAP_INITIAL_SIZE : (uint32_t)pl__ds_get_next_power_of_2(uBucketCount); - - if(uBucketCount > 0) - { - - ptHashMap->_aulValueIndices = (uint64_t*)PL_DS_ALLOC_INDIRECT(sizeof(uint64_t) * ptHashMap->_uBucketCount, pcFile, iLine); - ptHashMap->_aulKeys = (uint64_t*)PL_DS_ALLOC_INDIRECT(sizeof(uint64_t) * ptHashMap->_uBucketCount, pcFile, iLine); - memset(ptHashMap->_aulValueIndices, 0xff, sizeof(uint64_t) * ptHashMap->_uBucketCount); - memset(ptHashMap->_aulKeys, 0xff, sizeof(uint64_t) * ptHashMap->_uBucketCount); - - uint64_t mask = uOldBucketCount - 1; - - for(uint32_t i = 0; i < uOldBucketCount; i++) - { - const uint64_t ulKey = aulOldKeys[i]; - uint64_t ulOldModKey = ulKey & mask; - - - while(aulOldKeys[ulOldModKey] != ulKey && aulOldKeys[ulOldModKey] != UINT64_MAX) - ulOldModKey = (ulOldModKey + 1) & mask; - - const uint64_t ulValue = sbulOldValueIndices[ulOldModKey]; - ptHashMap->_uItemCount--; - pl__hm_insert(pptHashMap, ulKey, ulValue, pcFile, iLine); - } - } - else - { - ptHashMap->_aulValueIndices = NULL; - ptHashMap->_aulKeys = NULL; - pl_sb_free(ptHashMap->_sbulFreeIndices); - ptHashMap->_uItemCount = 0; - if(ptHashMap->_iMemoryOwned) - { - PL_DS_FREE(ptHashMap); - *pptHashMap = NULL; - } - } - - if(sbulOldValueIndices) - { - PL_DS_FREE(sbulOldValueIndices); - } - if(aulOldKeys) - { - PL_DS_FREE(aulOldKeys); - } -} - -static inline void -pl__hm_insert(plHashMap** pptHashMap, uint64_t ulKey, uint64_t ulValue, const char* pcFile, int iLine) -{ - plHashMap* ptHashMap = *pptHashMap; - - if(ptHashMap == NULL) - { - ptHashMap = PL_DS_ALLOC_INDIRECT(sizeof(plHashMap), pcFile, iLine); - memset(ptHashMap, 0, sizeof(plHashMap)); - ptHashMap->_iMemoryOwned = 1; - *pptHashMap = ptHashMap; - } - - if(ptHashMap->_uBucketCount == 0) - pl__hm_resize(pptHashMap, PL_DS_HASHMAP_INITIAL_SIZE, pcFile, iLine); - else if(((float)ptHashMap->_uItemCount / (float)ptHashMap->_uBucketCount) > 0.60f) - pl__hm_resize(pptHashMap, ptHashMap->_uBucketCount * 2, pcFile, iLine); - - uint64_t mask = ptHashMap->_uBucketCount - 1; - uint64_t ulModKey = ulKey & mask; - - uint64_t ulExistingKey = pl__hm_lookup(pptHashMap, ulKey); - if(ulExistingKey == UINT64_MAX) - { - while(ptHashMap->_aulKeys[ulModKey] != ulKey && ptHashMap->_aulKeys[ulModKey] != UINT64_MAX) - { - ulModKey = (ulModKey + 1) & mask; - if(ptHashMap->_aulKeys[ulModKey] == UINT64_MAX - 1) - break; - } - - ptHashMap->_aulKeys[ulModKey] = ulKey; - ptHashMap->_aulValueIndices[ulModKey] = ulValue; - ptHashMap->_uItemCount++; - } - else - { - while(ptHashMap->_aulKeys[ulModKey] != ulKey && ptHashMap->_aulKeys[ulModKey] != UINT64_MAX) - ulModKey = (ulModKey + 1) & mask; - - ptHashMap->_aulValueIndices[ulModKey] = ulValue; - } -} - -static inline void -pl__hm_remove(plHashMap** pptHashMap, uint64_t ulKey) -{ - plHashMap* ptHashMap = *pptHashMap; - if(ptHashMap == NULL) - return; - - uint64_t mask = ptHashMap->_uBucketCount - 1; - uint64_t ulModKey = ulKey & mask; - - while(ptHashMap->_aulKeys[ulModKey] != ulKey && ptHashMap->_aulKeys[ulModKey] != UINT64_MAX) - ulModKey = (ulModKey + 1) & mask; - - const uint64_t ulValue = ptHashMap->_aulValueIndices[ulModKey]; - pl_sb_push(ptHashMap->_sbulFreeIndices, ulValue); - - ptHashMap->_aulValueIndices[ulModKey] = UINT64_MAX; - ptHashMap->_aulKeys[ulModKey] = UINT64_MAX - 1; - ptHashMap->_uItemCount--; -} +static inline uint64_t pl__hm_lookup64(const plHashMap64*, uint64_t uKey, uint32_t* puBucketIndexOut, uint64_t* puValueOut); +static inline void pl__hm_insert (plHashMap64*, uint64_t uKey, uint64_t uValue, const char* pcFile, int iLine); +static inline uint32_t pl__hm_lookup32(const plHashMap32*, uint64_t uKey, uint32_t* puBucketIndexOut, uint32_t* puValueOut); +static inline void pl__hm_insert32(plHashMap32*, uint64_t uKey, uint32_t uValue, const char* pcFile, int iLine); static const uint64_t __gauCrc64LookupTableDS[256] = { @@ -616,11 +615,397 @@ static const uint64_t __gauCrc64LookupTableDS[256] = 0x9480000000000000ULL, 0x9530000000000000ULL, 0x97E0000000000000ULL, 0x9650000000000000ULL, 0x9240000000000000ULL, 0x93F0000000000000ULL, 0x9120000000000000ULL, 0x9090000000000000ULL }; +static inline size_t +pl__ds_get_next_power_of_2(size_t n) +{ + size_t uResult = 1; + if (n && !(n & (n - 1))) + uResult = n; + while (uResult < n) + uResult <<= 1; + return uResult; +} + +static inline uint32_t +pl__hm_get_existing_bucket_index(const uint64_t* auKeys, uint32_t uBucketCount, uint64_t uKey) +{ + // this function assumes the key,pair definitely exists + + const uint32_t uMask = uBucketCount - 1; // assumes bucket count is power of 2 + uint32_t uBucketIndex = uKey & uMask; + + const uint32_t uOriginalBucketIndex = uBucketIndex; + + while(auKeys[uBucketIndex] != uKey && auKeys[uBucketIndex] != PL_DS_HASH_INVALID) + { + uBucketIndex = (uBucketIndex + 1) & uMask; + + PL_DS_ASSERT(uBucketIndex != uOriginalBucketIndex && "should not be possible"); + + // not found and we did a full wrap around + if(uBucketIndex == uOriginalBucketIndex) + { + uBucketIndex = UINT32_MAX; + break; + } + } + + return uBucketIndex; +} + static inline uint64_t -pl_hm_hash_str(const char* pcKey) +pl_hm_get_free_index(plHashMap64* ptHashMap) { - uint64_t uCrc = 0; + uint64_t uResult = PL_DS_HASH_INVALID; + if(pl_sb_size(ptHashMap->_sbuFreeIndices) > 0) + { + uResult = pl_sb_pop(ptHashMap->_sbuFreeIndices); + } + return uResult; +} + +static inline uint32_t +pl_hm32_get_free_index(plHashMap32* ptHashMap) +{ + + uint32_t uResult = PL_DS_HASH32_INVALID; + if(pl_sb_size(ptHashMap->_sbuFreeIndices) > 0) + { + uResult = pl_sb_pop(ptHashMap->_sbuFreeIndices); + } + return uResult; +} + +static inline uint64_t +pl__hm_lookup64(const plHashMap64* ptHashMap, uint64_t uKey, uint32_t* puBucketIndexOut, uint64_t* puValueOut) +{ + + // early exit checks + if(ptHashMap == NULL || ptHashMap->_uBucketCapacity == 0) + return PL_DS_HASH_INVALID; + + const uint32_t uMask = ptHashMap->_uBucketCapacity - 1; // assumes bucket count is power of 2 + uint32_t uBucketIndex = uKey & uMask; + const uint32_t uOriginalBucketIndex = uBucketIndex; // to check for full wrap around + + // find where a value would be and handle collisions with linear probing + while(ptHashMap->_auKeys[uBucketIndex] != uKey && ptHashMap->_auKeys[uBucketIndex] != PL_DS_HASH_INVALID) + { + uBucketIndex = (uBucketIndex + 1) & uMask; + + // not find and we did a full wrap around + if(uBucketIndex == uOriginalBucketIndex) + return PL_DS_HASH_INVALID; + } + + if(puBucketIndexOut) + *puBucketIndexOut = uBucketIndex; + + if(puValueOut) + *puValueOut = ptHashMap->_auValueBucket[uBucketIndex]; + + // exists, so return associated value + return ptHashMap->_auValueBucket[uBucketIndex]; +} + +static inline uint32_t +pl__hm_lookup32(const plHashMap32* ptHashMap, uint64_t uKey, uint32_t* puBucketIndexOut, uint32_t* puValueOut) +{ + + // early exit checks + if(ptHashMap == NULL || ptHashMap->_uBucketCapacity == 0) + return PL_DS_HASH32_INVALID; + + const uint32_t uMask = ptHashMap->_uBucketCapacity - 1; // assumes bucket count is power of 2 + uint32_t uBucketIndex = uKey & uMask; + const uint32_t uOriginalBucketIndex = uBucketIndex; // to check for full wrap around + + // find where a value would be and handle collisions with linear probing + while(ptHashMap->_auKeys[uBucketIndex] != uKey && ptHashMap->_auKeys[uBucketIndex] != PL_DS_HASH_INVALID) + { + uBucketIndex = (uBucketIndex + 1) & uMask; + + // not find and we did a full wrap around + if(uBucketIndex == uOriginalBucketIndex) + return PL_DS_HASH32_INVALID; + } + + if(puBucketIndexOut) + *puBucketIndexOut = uBucketIndex; + + if(puValueOut) + *puValueOut = ptHashMap->_auValueBucket[uBucketIndex]; + + // exists, so return associated value + return ptHashMap->_auValueBucket[uBucketIndex]; +} + +static inline uint64_t +pl_hm64_lookup(const plHashMap64* ptHashMap, uint64_t uKey) +{ + return pl__hm_lookup64(ptHashMap, uKey, NULL, NULL); +} + +static inline uint32_t +pl_hm32_lookup(const plHashMap32* ptHashMap, uint64_t uKey) +{ + return pl__hm_lookup32(ptHashMap, uKey, NULL, NULL); +} + +static inline bool +pl_hm64_has_key(const plHashMap64* ptHashMap, uint64_t uKey) +{ + return pl_hm64_lookup(ptHashMap, uKey) != PL_DS_HASH_INVALID; +} + +static inline bool +pl_hm32_has_key(const plHashMap32* ptHashMap, uint64_t uKey) +{ + return pl_hm32_lookup(ptHashMap, uKey) != PL_DS_HASH32_INVALID; +} + +static inline bool +pl_hm64_has_key_ex(const plHashMap64* ptHashMap, uint64_t uKey, uint64_t* puValue) +{ + return pl__hm_lookup64(ptHashMap, uKey, NULL, puValue) != PL_DS_HASH_INVALID; +} + +static inline bool +pl_hm32_has_key_ex(const plHashMap32* ptHashMap, uint64_t uKey, uint32_t* puValue) +{ + return pl__hm_lookup32(ptHashMap, uKey, NULL, puValue) != PL_DS_HASH32_INVALID; +} + +static inline void +pl__hm_resize(plHashMap64* ptHashMap, uint32_t uBucketCount, const char* pcFile, int iLine) +{ + + // store old info + const uint32_t uOldBucketCount = ptHashMap->_uBucketCapacity; + uint64_t* sbuOldBucket = ptHashMap->_auValueBucket; + uint64_t* aulOldKeys = ptHashMap->_auKeys; + + // ensure our actual bucket count is a power of 2 + ptHashMap->_uBucketCapacity = uBucketCount < PL_DS_HASHMAP_INITIAL_SIZE ? PL_DS_HASHMAP_INITIAL_SIZE : (uint32_t)pl__ds_get_next_power_of_2(uBucketCount); + + // growing + if(uBucketCount > 0) + { + + ptHashMap->_auValueBucket = (uint64_t*)PL_DS_ALLOC_INDIRECT(sizeof(uint64_t) * ptHashMap->_uBucketCapacity, pcFile, iLine); + ptHashMap->_auKeys = (uint64_t*)PL_DS_ALLOC_INDIRECT(sizeof(uint64_t) * ptHashMap->_uBucketCapacity, pcFile, iLine); + memset(ptHashMap->_auValueBucket, 0xff, sizeof(uint64_t) * ptHashMap->_uBucketCapacity); + memset(ptHashMap->_auKeys, 0xff, sizeof(uint64_t) * ptHashMap->_uBucketCapacity); + + // move old data over + for(uint32_t i = 0; i < uOldBucketCount; i++) + { + const uint64_t uKey = aulOldKeys[i]; + + if(uKey < UINT64_MAX-1) + { + uint32_t uOldBucketIndex = pl__hm_get_existing_bucket_index(aulOldKeys, uOldBucketCount, uKey); + const uint64_t uValue = sbuOldBucket[uOldBucketIndex]; + ptHashMap->_uItemCount--; + pl__hm_insert(ptHashMap, uKey, uValue, pcFile, iLine); + } + } + } + else // freeing + { + ptHashMap->_auValueBucket = NULL; + ptHashMap->_auKeys = NULL; + pl_sb_free(ptHashMap->_sbuFreeIndices); + ptHashMap->_uItemCount = 0; + ptHashMap->_uBucketCapacity = 0; + } + + if(sbuOldBucket) + { + PL_DS_FREE(sbuOldBucket); + } + if(aulOldKeys) + { + PL_DS_FREE(aulOldKeys); + } +} + +static inline void +pl__hm_resize32(plHashMap32* ptHashMap, uint32_t uBucketCount, const char* pcFile, int iLine) +{ + + // store old info + const uint32_t uOldBucketCount = ptHashMap->_uBucketCapacity; + uint32_t* sbuOldBucket = ptHashMap->_auValueBucket; + uint64_t* aulOldKeys = ptHashMap->_auKeys; + + // ensure our actual bucket count is a power of 2 + ptHashMap->_uBucketCapacity = uBucketCount < PL_DS_HASHMAP_INITIAL_SIZE ? PL_DS_HASHMAP_INITIAL_SIZE : (uint32_t)pl__ds_get_next_power_of_2(uBucketCount); + + // growing + if(uBucketCount > 0) + { + + ptHashMap->_auValueBucket = (uint32_t*)PL_DS_ALLOC_INDIRECT(sizeof(uint32_t) * ptHashMap->_uBucketCapacity, pcFile, iLine); + ptHashMap->_auKeys = (uint64_t*)PL_DS_ALLOC_INDIRECT(sizeof(uint64_t) * ptHashMap->_uBucketCapacity, pcFile, iLine); + memset(ptHashMap->_auValueBucket, 0xff, sizeof(uint32_t) * ptHashMap->_uBucketCapacity); + memset(ptHashMap->_auKeys, 0xff, sizeof(uint64_t) * ptHashMap->_uBucketCapacity); + + // move old data over + for(uint32_t i = 0; i < uOldBucketCount; i++) + { + const uint64_t uKey = aulOldKeys[i]; + + if(uKey < UINT64_MAX-1) + { + uint32_t uOldBucketIndex = pl__hm_get_existing_bucket_index(aulOldKeys, uOldBucketCount, uKey); + const uint32_t uValue = sbuOldBucket[uOldBucketIndex]; + ptHashMap->_uItemCount--; + pl__hm_insert32(ptHashMap, uKey, uValue, pcFile, iLine); + } + } + } + else // freeing + { + ptHashMap->_auValueBucket = NULL; + ptHashMap->_auKeys = NULL; + pl_sb_free(ptHashMap->_sbuFreeIndices); + ptHashMap->_uItemCount = 0; + } + + if(sbuOldBucket) + { + PL_DS_FREE(sbuOldBucket); + } + if(aulOldKeys) + { + PL_DS_FREE(aulOldKeys); + } +} + +static inline void +pl_hm64_free(plHashMap64* ptHashMap) +{ + pl__hm_resize(ptHashMap, 0, __FILE__, __LINE__); +} + +static inline void +pl_hm32_free(plHashMap32* ptHashMap) +{ + pl__hm_resize32(ptHashMap, 0, __FILE__, __LINE__); +} + +static inline void +pl__hm_insert(plHashMap64* ptHashMap, uint64_t uKey, uint64_t uValue, const char* pcFile, int iLine) +{ + + if(ptHashMap->_uBucketCapacity == 0) + pl__hm_resize(ptHashMap, PL_DS_HASHMAP_INITIAL_SIZE, pcFile, iLine); + else if(((float)ptHashMap->_uItemCount / (float)ptHashMap->_uBucketCapacity) > 0.60f) + pl__hm_resize(ptHashMap, ptHashMap->_uBucketCapacity * 2, pcFile, iLine); + + const uint32_t uMask = ptHashMap->_uBucketCapacity - 1; // assumes bucket count is power of 2 + uint32_t uBucketIndex = uKey & uMask; + + // see if key exists + uint64_t uValueIndex = pl__hm_lookup64(ptHashMap, uKey, &uBucketIndex, NULL); + + if(uValueIndex == PL_DS_HASH_INVALID) // key doesn't exist + { + // handle collisions with linear probing + while(ptHashMap->_auKeys[uBucketIndex] != uKey && ptHashMap->_auKeys[uBucketIndex] != PL_DS_HASH_INVALID) + { + uBucketIndex = (uBucketIndex + 1) & uMask; + + // check if slot is empty + if(ptHashMap->_auKeys[uBucketIndex] == UINT64_MAX-1) + break; + } + + ptHashMap->_auKeys[uBucketIndex] = uKey; + ptHashMap->_auValueBucket[uBucketIndex] = uValue; + ptHashMap->_uItemCount++; + } + else // key exists, so update value + ptHashMap->_auValueBucket[uBucketIndex] = uValue; +} + +static inline void +pl__hm_insert32(plHashMap32* ptHashMap, uint64_t uKey, uint32_t uValue, const char* pcFile, int iLine) +{ + + if(ptHashMap->_uBucketCapacity == 0) + pl__hm_resize32(ptHashMap, PL_DS_HASHMAP_INITIAL_SIZE, pcFile, iLine); + else if(((float)ptHashMap->_uItemCount / (float)ptHashMap->_uBucketCapacity) > 0.60f) + pl__hm_resize32(ptHashMap, ptHashMap->_uBucketCapacity * 2, pcFile, iLine); + + const uint32_t uMask = ptHashMap->_uBucketCapacity - 1; // assumes bucket count is power of 2 + uint32_t uBucketIndex = uKey & uMask; + + // see if key exists + uint32_t uValueIndex = pl__hm_lookup32(ptHashMap, uKey, &uBucketIndex, NULL); + + if(uValueIndex == PL_DS_HASH32_INVALID) // key doesn't exist + { + // handle collisions with linear probing + while(ptHashMap->_auKeys[uBucketIndex] != uKey && ptHashMap->_auKeys[uBucketIndex] != PL_DS_HASH_INVALID) + { + uBucketIndex = (uBucketIndex + 1) & uMask; + + // check if slot is empty + if(ptHashMap->_auKeys[uBucketIndex] == UINT64_MAX-1) + break; + } + + ptHashMap->_auKeys[uBucketIndex] = uKey; + ptHashMap->_auValueBucket[uBucketIndex] = uValue; + ptHashMap->_uItemCount++; + } + else // key exists, so update value + ptHashMap->_auValueBucket[uBucketIndex] = uValue; +} + +static inline void +pl_hm64_remove(plHashMap64* ptHashMap, uint64_t uKey) +{ + + uint32_t uBucketIndex = UINT32_MAX; + uint64_t uValueIndex = pl__hm_lookup64(ptHashMap, uKey, &uBucketIndex, NULL); + + if(uValueIndex != PL_DS_HASH_INVALID) + { + const uint64_t uValue = ptHashMap->_auValueBucket[uBucketIndex]; + pl_sb_push(ptHashMap->_sbuFreeIndices, uValue); + + ptHashMap->_auValueBucket[uBucketIndex] = PL_DS_HASH_INVALID; + ptHashMap->_auKeys[uBucketIndex] = UINT64_MAX-1; + ptHashMap->_uItemCount--; + } +} + +static inline void +pl_hm32_remove(plHashMap32* ptHashMap, uint64_t uKey) +{ + + uint32_t uBucketIndex = UINT32_MAX; + uint32_t uValueIndex = pl__hm_lookup32(ptHashMap, uKey, &uBucketIndex, NULL); + + if(uValueIndex != PL_DS_HASH32_INVALID) + { + const uint32_t uValue = ptHashMap->_auValueBucket[uBucketIndex]; + pl_sb_push(ptHashMap->_sbuFreeIndices, uValue); + + ptHashMap->_auValueBucket[uBucketIndex] = PL_DS_HASH32_INVALID; + ptHashMap->_auKeys[uBucketIndex] = UINT64_MAX-1; + ptHashMap->_uItemCount--; + } +} + +static inline uint64_t +pl_hm_hash_str(const char* pcKey, uint64_t uSeed) +{ + uint64_t uCrc = uSeed; const unsigned char* pucData = (const unsigned char*)pcKey; unsigned char c = *pucData++; @@ -633,6 +1018,18 @@ pl_hm_hash_str(const char* pcKey) return ~uCrc; } +static inline void +pl_hm64_remove_str(plHashMap64* ptHashMap, const char* pcKey) +{ + pl_hm64_remove(ptHashMap, pl_hm_hash_str(pcKey, 0)); +} + +static inline void +pl_hm32_remove_str(plHashMap32* ptHashMap, const char* pcKey) +{ + pl_hm32_remove(ptHashMap, pl_hm_hash_str(pcKey, 0)); +} + static inline uint64_t pl_hm_hash(const void* pData, size_t szDataSize, uint64_t uSeed) { @@ -643,71 +1040,40 @@ pl_hm_hash(const void* pData, size_t szDataSize, uint64_t uSeed) return ~uCrc; } -static inline uint64_t -pl__hm_lookup(plHashMap** pptHashMap, uint64_t ulKey) +static inline bool +pl_hm64_has_key_str(const plHashMap64* ptHashMap, const char* pcKey) { - plHashMap* ptHashMap = *pptHashMap; - - if(ptHashMap == NULL) - return UINT64_MAX; - if(ptHashMap->_uBucketCount == 0) - return UINT64_MAX; - - uint64_t mask = ptHashMap->_uBucketCount - 1; - uint64_t ulModKey = ulKey & mask; - const uint64_t ulOriginalModKey = ulModKey; - - while(ptHashMap->_aulKeys[ulModKey] != ulKey && ptHashMap->_aulKeys[ulModKey] != UINT64_MAX) - { - ulModKey = (ulModKey + 1) & mask; - if(ulModKey == ulOriginalModKey) - return UINT64_MAX; - } - - if(ptHashMap->_aulKeys[ulModKey] == UINT64_MAX) - return UINT64_MAX; - - return ptHashMap->_aulValueIndices[ulModKey]; -} - -static inline uint64_t -pl__hm_get_free_index(plHashMap** pptHashMap) -{ - plHashMap* ptHashMap = *pptHashMap; - - if(ptHashMap == NULL) - return UINT64_MAX; - uint64_t ulResult = UINT64_MAX; - if(pl_sb_size(ptHashMap->_sbulFreeIndices) > 0) - { - ulResult = pl_sb_pop(ptHashMap->_sbulFreeIndices); - } - return ulResult; + return pl_hm64_lookup(ptHashMap, pl_hm_hash_str(pcKey, 0)) != PL_DS_HASH_INVALID; } static inline bool -pl__hm_has_key(plHashMap** pptHashMap, uint64_t ulKey) +pl_hm32_has_key_str(const plHashMap32* ptHashMap, const char* pcKey) { - plHashMap* ptHashMap = *pptHashMap; - - if(ptHashMap == NULL) - return false; - - if(ptHashMap->_uItemCount == 0) - return false; - - uint64_t mask = ptHashMap->_uBucketCount - 1; - uint64_t ulModKey = ulKey & mask; - const uint64_t ulOriginalModKey = ulModKey; - - while(ptHashMap->_aulKeys[ulModKey] != ulKey && ptHashMap->_aulKeys[ulModKey] != UINT64_MAX) - { - ulModKey = (ulModKey + 1) & mask; - if(ulModKey == ulOriginalModKey) - return false; - } - - return ptHashMap->_aulKeys[ulModKey] != UINT64_MAX; + return pl_hm32_lookup(ptHashMap, pl_hm_hash_str(pcKey, 0)) != PL_DS_HASH32_INVALID; } -#endif // PL_DS_H +static inline bool +pl_hm64_has_key_str_ex(const plHashMap64* ptHashMap, const char* pcKey, uint64_t* puValue) +{ + return pl__hm_lookup64(ptHashMap, pl_hm_hash_str(pcKey, 0), NULL, puValue) != PL_DS_HASH_INVALID; +} + +static inline bool +pl_hm32_has_key_str_ex(const plHashMap32* ptHashMap, const char* pcKey, uint32_t* puValue) +{ + return pl__hm_lookup32(ptHashMap, pl_hm_hash_str(pcKey, 0), NULL, puValue) != PL_DS_HASH32_INVALID; +} + +static inline uint64_t +pl_hm64_lookup_str(const plHashMap64* ptHashMap, const char* pcKey) +{ + return pl__hm_lookup64(ptHashMap, pl_hm_hash_str(pcKey, 0), NULL, NULL); +} + +static inline uint32_t +pl_hm32_lookup_str(const plHashMap32* ptHashMap, const char* pcKey) +{ + return pl__hm_lookup32(ptHashMap, pl_hm_hash_str(pcKey, 0), NULL, NULL); +} + +#endif // PL_DS_H \ No newline at end of file