20#include <winpr/config.h>
23#include <winpr/assert.h>
25#include <winpr/collections.h>
35typedef struct s_wKeyValuePair wKeyValuePair;
54 float lowerRehashThreshold;
55 float upperRehashThreshold;
56 wKeyValuePair** bucketArray;
58 HASH_TABLE_HASH_FN hash;
62 DWORD foreachRecursionLevel;
66BOOL HashTable_PointerCompare(
const void* pointer1,
const void* pointer2)
68 return (pointer1 == pointer2);
71UINT32 HashTable_PointerHash(
const void* pointer)
73 return ((UINT32)(UINT_PTR)pointer) >> 4;
76BOOL HashTable_StringCompare(
const void* string1,
const void* string2)
78 if (!string1 || !string2)
79 return (string1 == string2);
81 return (strcmp((
const char*)string1, (
const char*)string2) == 0);
84UINT32 HashTable_StringHash(
const void* key)
88 const BYTE* str = (
const BYTE*)key;
91 while ((c = *str++) !=
'\0')
92 hash = (hash * 33) + c;
97void* HashTable_StringClone(
const void* str)
99 return winpr_ObjectStringClone(str);
102void HashTable_StringFree(
void* str)
104 winpr_ObjectStringFree(str);
108static inline BOOL HashTable_IsProbablePrime(
size_t oddNumber)
110 for (
size_t i = 3; i < 51; i += 2)
114 else if (oddNumber % i == 0)
122static inline size_t HashTable_CalculateIdealNumOfBuckets(wHashTable* table)
126 const float numOfElements = (float)table->numOfElements;
127 const float tmp = (numOfElements / table->idealRatio);
128 size_t idealNumOfBuckets = (size_t)tmp;
130 if (idealNumOfBuckets < 5)
131 idealNumOfBuckets = 5;
133 idealNumOfBuckets |= 0x01;
135 while (!HashTable_IsProbablePrime(idealNumOfBuckets))
136 idealNumOfBuckets += 2;
138 return idealNumOfBuckets;
141static inline void HashTable_Rehash(wHashTable* table,
size_t numOfBuckets)
143 UINT32 hashValue = 0;
144 wKeyValuePair* nextPair =
nullptr;
145 wKeyValuePair** newBucketArray =
nullptr;
148 if (numOfBuckets == 0)
149 numOfBuckets = HashTable_CalculateIdealNumOfBuckets(table);
151 if (numOfBuckets == table->numOfBuckets)
154 newBucketArray = (wKeyValuePair**)calloc(numOfBuckets,
sizeof(wKeyValuePair*));
165 for (
size_t index = 0; index < table->numOfBuckets; index++)
167 wKeyValuePair* pair = table->bucketArray[index];
171 nextPair = pair->next;
172 hashValue = table->hash(pair->key) % numOfBuckets;
173 pair->next = newBucketArray[hashValue];
174 newBucketArray[hashValue] = pair;
179 free((
void*)table->bucketArray);
180 table->bucketArray = newBucketArray;
181 table->numOfBuckets = numOfBuckets;
185static inline BOOL HashTable_Equals(wHashTable* table,
const wKeyValuePair* pair,
const void* key)
190 return table->key.fnObjectEquals(key, pair->key);
194static inline wKeyValuePair* HashTable_Get(wHashTable* table,
const void* key)
196 UINT32 hashValue = 0;
197 wKeyValuePair* pair =
nullptr;
203 hashValue = table->hash(key) % table->numOfBuckets;
204 pair = table->bucketArray[hashValue];
206 while (pair && !HashTable_Equals(table, pair, key))
212static inline void disposeKey(wHashTable* table,
void* key)
215 if (table->key.fnObjectFree)
216 table->key.fnObjectFree(key);
219static inline void disposeValue(wHashTable* table,
void* value)
222 if (table->value.fnObjectFree)
223 table->value.fnObjectFree(value);
226static inline void disposePair(wHashTable* table, wKeyValuePair* pair)
231 disposeKey(table, pair->key);
232 disposeValue(table, pair->value);
236static inline void setKey(wHashTable* table, wKeyValuePair* pair,
const void* key)
241 disposeKey(table, pair->key);
242 if (table->key.fnObjectNew)
243 pair->key = table->key.fnObjectNew(key);
256static inline void setValue(wHashTable* table, wKeyValuePair* pair,
const void* value)
261 disposeValue(table, pair->value);
262 if (table->value.fnObjectNew)
263 pair->value = table->value.fnObjectNew(value);
272 pair->value = cnv.pv;
289size_t HashTable_Count(wHashTable* table)
292 return table->numOfElements;
302#if defined(WITH_WINPR_DEPRECATED)
303int HashTable_Add(wHashTable* table,
const void* key,
const void* value)
305 if (!HashTable_Insert(table, key, value))
311BOOL HashTable_Insert(wHashTable* table,
const void* key,
const void* value)
314 UINT32 hashValue = 0;
315 wKeyValuePair* pair =
nullptr;
316 wKeyValuePair* newPair =
nullptr;
322 if (table->synchronized)
323 EnterCriticalSection(&table->lock);
325 hashValue = table->hash(key) % table->numOfBuckets;
326 pair = table->bucketArray[hashValue];
328 while (pair && !HashTable_Equals(table, pair, key))
333 if (pair->markedForRemove)
336 table->pendingRemoves--;
337 pair->markedForRemove = FALSE;
338 table->numOfElements++;
341 if (pair->key != key)
343 setKey(table, pair, key);
346 if (pair->value != value)
348 setValue(table, pair, value);
354 newPair = (wKeyValuePair*)calloc(1,
sizeof(wKeyValuePair));
358 setKey(table, newPair, key);
359 setValue(table, newPair, value);
360 newPair->next = table->bucketArray[hashValue];
361 newPair->markedForRemove = FALSE;
362 table->bucketArray[hashValue] = newPair;
363 table->numOfElements++;
365 if (!table->foreachRecursionLevel && table->upperRehashThreshold > table->idealRatio)
367 float elementToBucketRatio =
368 (float)table->numOfElements / (
float)table->numOfBuckets;
370 if (elementToBucketRatio > table->upperRehashThreshold)
371 HashTable_Rehash(table, 0);
377 if (table->synchronized)
378 LeaveCriticalSection(&table->lock);
387BOOL HashTable_Remove(wHashTable* table,
const void* key)
389 UINT32 hashValue = 0;
391 wKeyValuePair* pair =
nullptr;
392 wKeyValuePair* previousPair =
nullptr;
398 if (table->synchronized)
399 EnterCriticalSection(&table->lock);
401 hashValue = table->hash(key) % table->numOfBuckets;
402 pair = table->bucketArray[hashValue];
404 while (pair && !HashTable_Equals(table, pair, key))
416 if (table->foreachRecursionLevel)
419 pair->markedForRemove = TRUE;
420 table->pendingRemoves++;
421 table->numOfElements--;
426 previousPair->next = pair->next;
428 table->bucketArray[hashValue] = pair->next;
430 disposePair(table, pair);
431 table->numOfElements--;
433 if (!table->foreachRecursionLevel && table->lowerRehashThreshold > 0.0f)
435 float elementToBucketRatio = (float)table->numOfElements / (
float)table->numOfBuckets;
437 if (elementToBucketRatio < table->lowerRehashThreshold)
438 HashTable_Rehash(table, 0);
442 if (table->synchronized)
443 LeaveCriticalSection(&table->lock);
452void* HashTable_GetItemValue(wHashTable* table,
const void* key)
454 void* value =
nullptr;
455 wKeyValuePair* pair =
nullptr;
461 if (table->synchronized)
462 EnterCriticalSection(&table->lock);
464 pair = HashTable_Get(table, key);
466 if (pair && !pair->markedForRemove)
469 if (table->synchronized)
470 LeaveCriticalSection(&table->lock);
479BOOL HashTable_SetItemValue(wHashTable* table,
const void* key,
const void* value)
482 wKeyValuePair* pair =
nullptr;
488 if (table->synchronized)
489 EnterCriticalSection(&table->lock);
491 pair = HashTable_Get(table, key);
493 if (!pair || pair->markedForRemove)
497 setValue(table, pair, value);
500 if (table->synchronized)
501 LeaveCriticalSection(&table->lock);
510void HashTable_Clear(wHashTable* table)
512 wKeyValuePair* nextPair =
nullptr;
516 if (table->synchronized)
517 EnterCriticalSection(&table->lock);
519 for (
size_t index = 0; index < table->numOfBuckets; index++)
521 wKeyValuePair* pair = table->bucketArray[index];
525 nextPair = pair->next;
527 if (table->foreachRecursionLevel)
530 pair->markedForRemove = TRUE;
531 table->pendingRemoves++;
535 disposePair(table, pair);
540 table->bucketArray[index] =
nullptr;
543 table->numOfElements = 0;
544 if (table->foreachRecursionLevel == 0)
545 HashTable_Rehash(table, 5);
547 if (table->synchronized)
548 LeaveCriticalSection(&table->lock);
555size_t HashTable_GetKeys(wHashTable* table, ULONG_PTR** ppKeys)
559 ULONG_PTR* pKeys =
nullptr;
560 wKeyValuePair* nextPair =
nullptr;
564 if (table->synchronized)
565 EnterCriticalSection(&table->lock);
568 count = table->numOfElements;
574 if (table->synchronized)
575 LeaveCriticalSection(&table->lock);
580 pKeys = (ULONG_PTR*)calloc(count,
sizeof(ULONG_PTR));
584 if (table->synchronized)
585 LeaveCriticalSection(&table->lock);
590 for (
size_t index = 0; index < table->numOfBuckets; index++)
592 wKeyValuePair* pair = table->bucketArray[index];
596 nextPair = pair->next;
597 if (!pair->markedForRemove)
598 pKeys[iKey++] = (ULONG_PTR)pair->key;
603 if (table->synchronized)
604 LeaveCriticalSection(&table->lock);
613BOOL HashTable_Foreach(wHashTable* table, HASH_TABLE_FOREACH_FN fn, VOID* arg)
620 if (table->synchronized)
621 EnterCriticalSection(&table->lock);
623 table->foreachRecursionLevel++;
624 for (
size_t index = 0; index < table->numOfBuckets; index++)
626 for (wKeyValuePair* pair = table->bucketArray[index]; pair; pair = pair->next)
628 if (!pair->markedForRemove && !fn(pair->key, pair->value, arg))
635 table->foreachRecursionLevel--;
637 if (!table->foreachRecursionLevel && table->pendingRemoves)
640 wKeyValuePair** prevPtr =
nullptr;
641 for (
size_t index = 0; index < table->numOfBuckets; index++)
643 wKeyValuePair* nextPair =
nullptr;
644 prevPtr = &table->bucketArray[index];
645 for (wKeyValuePair* pair = table->bucketArray[index]; pair;)
647 nextPair = pair->next;
649 if (pair->markedForRemove)
651 disposePair(table, pair);
656 prevPtr = &pair->next;
661 table->pendingRemoves = 0;
665 if (table->synchronized)
666 LeaveCriticalSection(&table->lock);
674BOOL HashTable_Contains(wHashTable* table,
const void* key)
677 wKeyValuePair* pair =
nullptr;
683 if (table->synchronized)
684 EnterCriticalSection(&table->lock);
686 pair = HashTable_Get(table, key);
687 status = (pair && !pair->markedForRemove);
689 if (table->synchronized)
690 LeaveCriticalSection(&table->lock);
699BOOL HashTable_ContainsKey(wHashTable* table,
const void* key)
702 wKeyValuePair* pair =
nullptr;
708 if (table->synchronized)
709 EnterCriticalSection(&table->lock);
711 pair = HashTable_Get(table, key);
712 status = (pair && !pair->markedForRemove);
714 if (table->synchronized)
715 LeaveCriticalSection(&table->lock);
724BOOL HashTable_ContainsValue(wHashTable* table,
const void* value)
732 if (table->synchronized)
733 EnterCriticalSection(&table->lock);
735 for (
size_t index = 0; index < table->numOfBuckets; index++)
737 wKeyValuePair* pair = table->bucketArray[index];
741 if (!pair->markedForRemove && HashTable_Equals(table, pair, value))
754 if (table->synchronized)
755 LeaveCriticalSection(&table->lock);
764wHashTable* HashTable_New(BOOL
synchronized)
766 wHashTable* table = (wHashTable*)calloc(1,
sizeof(wHashTable));
771 table->synchronized =
synchronized;
772 if (!InitializeCriticalSectionAndSpinCount(&(table->lock), 4000))
774 table->numOfBuckets = 64;
775 table->numOfElements = 0;
776 table->bucketArray = (wKeyValuePair**)calloc(table->numOfBuckets,
sizeof(wKeyValuePair*));
778 if (!table->bucketArray)
781 table->idealRatio = 3.0f;
782 table->lowerRehashThreshold = 0.0f;
783 table->upperRehashThreshold = 15.0f;
784 table->hash = HashTable_PointerHash;
785 table->key.fnObjectEquals = HashTable_PointerCompare;
786 table->value.fnObjectEquals = HashTable_PointerCompare;
790 WINPR_PRAGMA_DIAG_PUSH
791 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
792 HashTable_Free(table);
793 WINPR_PRAGMA_DIAG_POP
797void HashTable_Free(wHashTable* table)
799 wKeyValuePair* pair =
nullptr;
800 wKeyValuePair* nextPair =
nullptr;
805 if (table->bucketArray)
807 for (
size_t index = 0; index < table->numOfBuckets; index++)
809 pair = table->bucketArray[index];
813 nextPair = pair->next;
815 disposePair(table, pair);
819 free((
void*)table->bucketArray);
821 DeleteCriticalSection(&(table->lock));
826void HashTable_Lock(wHashTable* table)
829 EnterCriticalSection(&table->lock);
832void HashTable_Unlock(wHashTable* table)
835 LeaveCriticalSection(&table->lock);
838wObject* HashTable_KeyObject(wHashTable* table)
844wObject* HashTable_ValueObject(wHashTable* table)
847 return &table->value;
850BOOL HashTable_SetHashFunction(wHashTable* table, HASH_TABLE_HASH_FN fn)
854 return fn !=
nullptr;
857BOOL HashTable_SetupForStringData(wHashTable* table, BOOL stringValues)
861 if (!HashTable_SetHashFunction(table, HashTable_StringHash))
864 obj = HashTable_KeyObject(table);
871 obj = HashTable_ValueObject(table);
This struct contains function pointer to initialize/free objects.
OBJECT_FREE_FN fnObjectFree
WINPR_ATTR_NODISCARD OBJECT_EQUALS_FN fnObjectEquals
WINPR_ATTR_NODISCARD OBJECT_NEW_FN fnObjectNew