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;
184static inline BOOL HashTable_Equals(wHashTable* table,
const wKeyValuePair* pair,
const void* key)
189 return table->key.fnObjectEquals(key, pair->key);
192static inline wKeyValuePair* HashTable_Get(wHashTable* table,
const void* key)
194 UINT32 hashValue = 0;
195 wKeyValuePair* pair =
nullptr;
201 hashValue = table->hash(key) % table->numOfBuckets;
202 pair = table->bucketArray[hashValue];
204 while (pair && !HashTable_Equals(table, pair, key))
210static inline void disposeKey(wHashTable* table,
void* key)
213 if (table->key.fnObjectFree)
214 table->key.fnObjectFree(key);
217static inline void disposeValue(wHashTable* table,
void* value)
220 if (table->value.fnObjectFree)
221 table->value.fnObjectFree(value);
224static inline void disposePair(wHashTable* table, wKeyValuePair* pair)
229 disposeKey(table, pair->key);
230 disposeValue(table, pair->value);
234static inline void setKey(wHashTable* table, wKeyValuePair* pair,
const void* key)
239 disposeKey(table, pair->key);
240 if (table->key.fnObjectNew)
241 pair->key = table->key.fnObjectNew(key);
254static inline void setValue(wHashTable* table, wKeyValuePair* pair,
const void* value)
259 disposeValue(table, pair->value);
260 if (table->value.fnObjectNew)
261 pair->value = table->value.fnObjectNew(value);
270 pair->value = cnv.pv;
287size_t HashTable_Count(wHashTable* table)
290 return table->numOfElements;
300#if defined(WITH_WINPR_DEPRECATED)
301int HashTable_Add(wHashTable* table,
const void* key,
const void* value)
303 if (!HashTable_Insert(table, key, value))
309BOOL HashTable_Insert(wHashTable* table,
const void* key,
const void* value)
312 UINT32 hashValue = 0;
313 wKeyValuePair* pair =
nullptr;
314 wKeyValuePair* newPair =
nullptr;
320 if (table->synchronized)
321 EnterCriticalSection(&table->lock);
323 hashValue = table->hash(key) % table->numOfBuckets;
324 pair = table->bucketArray[hashValue];
326 while (pair && !HashTable_Equals(table, pair, key))
331 if (pair->markedForRemove)
334 table->pendingRemoves--;
335 pair->markedForRemove = FALSE;
336 table->numOfElements++;
339 if (pair->key != key)
341 setKey(table, pair, key);
344 if (pair->value != value)
346 setValue(table, pair, value);
352 newPair = (wKeyValuePair*)calloc(1,
sizeof(wKeyValuePair));
356 setKey(table, newPair, key);
357 setValue(table, newPair, value);
358 newPair->next = table->bucketArray[hashValue];
359 newPair->markedForRemove = FALSE;
360 table->bucketArray[hashValue] = newPair;
361 table->numOfElements++;
363 if (!table->foreachRecursionLevel && table->upperRehashThreshold > table->idealRatio)
365 float elementToBucketRatio =
366 (float)table->numOfElements / (
float)table->numOfBuckets;
368 if (elementToBucketRatio > table->upperRehashThreshold)
369 HashTable_Rehash(table, 0);
375 if (table->synchronized)
376 LeaveCriticalSection(&table->lock);
385BOOL HashTable_Remove(wHashTable* table,
const void* key)
387 UINT32 hashValue = 0;
389 wKeyValuePair* pair =
nullptr;
390 wKeyValuePair* previousPair =
nullptr;
396 if (table->synchronized)
397 EnterCriticalSection(&table->lock);
399 hashValue = table->hash(key) % table->numOfBuckets;
400 pair = table->bucketArray[hashValue];
402 while (pair && !HashTable_Equals(table, pair, key))
414 if (table->foreachRecursionLevel)
417 pair->markedForRemove = TRUE;
418 table->pendingRemoves++;
419 table->numOfElements--;
424 previousPair->next = pair->next;
426 table->bucketArray[hashValue] = pair->next;
428 disposePair(table, pair);
429 table->numOfElements--;
431 if (!table->foreachRecursionLevel && table->lowerRehashThreshold > 0.0f)
433 float elementToBucketRatio = (float)table->numOfElements / (
float)table->numOfBuckets;
435 if (elementToBucketRatio < table->lowerRehashThreshold)
436 HashTable_Rehash(table, 0);
440 if (table->synchronized)
441 LeaveCriticalSection(&table->lock);
450void* HashTable_GetItemValue(wHashTable* table,
const void* key)
452 void* value =
nullptr;
453 wKeyValuePair* pair =
nullptr;
459 if (table->synchronized)
460 EnterCriticalSection(&table->lock);
462 pair = HashTable_Get(table, key);
464 if (pair && !pair->markedForRemove)
467 if (table->synchronized)
468 LeaveCriticalSection(&table->lock);
477BOOL HashTable_SetItemValue(wHashTable* table,
const void* key,
const void* value)
480 wKeyValuePair* pair =
nullptr;
486 if (table->synchronized)
487 EnterCriticalSection(&table->lock);
489 pair = HashTable_Get(table, key);
491 if (!pair || pair->markedForRemove)
495 setValue(table, pair, value);
498 if (table->synchronized)
499 LeaveCriticalSection(&table->lock);
508void HashTable_Clear(wHashTable* table)
510 wKeyValuePair* nextPair =
nullptr;
514 if (table->synchronized)
515 EnterCriticalSection(&table->lock);
517 for (
size_t index = 0; index < table->numOfBuckets; index++)
519 wKeyValuePair* pair = table->bucketArray[index];
523 nextPair = pair->next;
525 if (table->foreachRecursionLevel)
528 pair->markedForRemove = TRUE;
529 table->pendingRemoves++;
533 disposePair(table, pair);
538 table->bucketArray[index] =
nullptr;
541 table->numOfElements = 0;
542 if (table->foreachRecursionLevel == 0)
543 HashTable_Rehash(table, 5);
545 if (table->synchronized)
546 LeaveCriticalSection(&table->lock);
553size_t HashTable_GetKeys(wHashTable* table, ULONG_PTR** ppKeys)
557 ULONG_PTR* pKeys =
nullptr;
558 wKeyValuePair* nextPair =
nullptr;
562 if (table->synchronized)
563 EnterCriticalSection(&table->lock);
566 count = table->numOfElements;
572 if (table->synchronized)
573 LeaveCriticalSection(&table->lock);
578 pKeys = (ULONG_PTR*)calloc(count,
sizeof(ULONG_PTR));
582 if (table->synchronized)
583 LeaveCriticalSection(&table->lock);
588 for (
size_t index = 0; index < table->numOfBuckets; index++)
590 wKeyValuePair* pair = table->bucketArray[index];
594 nextPair = pair->next;
595 if (!pair->markedForRemove)
596 pKeys[iKey++] = (ULONG_PTR)pair->key;
601 if (table->synchronized)
602 LeaveCriticalSection(&table->lock);
611BOOL HashTable_Foreach(wHashTable* table, HASH_TABLE_FOREACH_FN fn, VOID* arg)
618 if (table->synchronized)
619 EnterCriticalSection(&table->lock);
621 table->foreachRecursionLevel++;
622 for (
size_t index = 0; index < table->numOfBuckets; index++)
624 for (wKeyValuePair* pair = table->bucketArray[index]; pair; pair = pair->next)
626 if (!pair->markedForRemove && !fn(pair->key, pair->value, arg))
633 table->foreachRecursionLevel--;
635 if (!table->foreachRecursionLevel && table->pendingRemoves)
638 wKeyValuePair** prevPtr =
nullptr;
639 for (
size_t index = 0; index < table->numOfBuckets; index++)
641 wKeyValuePair* nextPair =
nullptr;
642 prevPtr = &table->bucketArray[index];
643 for (wKeyValuePair* pair = table->bucketArray[index]; pair;)
645 nextPair = pair->next;
647 if (pair->markedForRemove)
649 disposePair(table, pair);
654 prevPtr = &pair->next;
659 table->pendingRemoves = 0;
663 if (table->synchronized)
664 LeaveCriticalSection(&table->lock);
672BOOL HashTable_Contains(wHashTable* table,
const void* key)
675 wKeyValuePair* pair =
nullptr;
681 if (table->synchronized)
682 EnterCriticalSection(&table->lock);
684 pair = HashTable_Get(table, key);
685 status = (pair && !pair->markedForRemove);
687 if (table->synchronized)
688 LeaveCriticalSection(&table->lock);
697BOOL HashTable_ContainsKey(wHashTable* table,
const void* key)
700 wKeyValuePair* pair =
nullptr;
706 if (table->synchronized)
707 EnterCriticalSection(&table->lock);
709 pair = HashTable_Get(table, key);
710 status = (pair && !pair->markedForRemove);
712 if (table->synchronized)
713 LeaveCriticalSection(&table->lock);
722BOOL HashTable_ContainsValue(wHashTable* table,
const void* value)
730 if (table->synchronized)
731 EnterCriticalSection(&table->lock);
733 for (
size_t index = 0; index < table->numOfBuckets; index++)
735 wKeyValuePair* pair = table->bucketArray[index];
739 if (!pair->markedForRemove && HashTable_Equals(table, pair, value))
752 if (table->synchronized)
753 LeaveCriticalSection(&table->lock);
762wHashTable* HashTable_New(BOOL
synchronized)
764 wHashTable* table = (wHashTable*)calloc(1,
sizeof(wHashTable));
769 table->synchronized =
synchronized;
770 if (!InitializeCriticalSectionAndSpinCount(&(table->lock), 4000))
772 table->numOfBuckets = 64;
773 table->numOfElements = 0;
774 table->bucketArray = (wKeyValuePair**)calloc(table->numOfBuckets,
sizeof(wKeyValuePair*));
776 if (!table->bucketArray)
779 table->idealRatio = 3.0f;
780 table->lowerRehashThreshold = 0.0f;
781 table->upperRehashThreshold = 15.0f;
782 table->hash = HashTable_PointerHash;
783 table->key.fnObjectEquals = HashTable_PointerCompare;
784 table->value.fnObjectEquals = HashTable_PointerCompare;
788 WINPR_PRAGMA_DIAG_PUSH
789 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
790 HashTable_Free(table);
791 WINPR_PRAGMA_DIAG_POP
795void HashTable_Free(wHashTable* table)
797 wKeyValuePair* pair =
nullptr;
798 wKeyValuePair* nextPair =
nullptr;
803 if (table->bucketArray)
805 for (
size_t index = 0; index < table->numOfBuckets; index++)
807 pair = table->bucketArray[index];
811 nextPair = pair->next;
813 disposePair(table, pair);
817 free((
void*)table->bucketArray);
819 DeleteCriticalSection(&(table->lock));
824void HashTable_Lock(wHashTable* table)
827 EnterCriticalSection(&table->lock);
830void HashTable_Unlock(wHashTable* table)
833 LeaveCriticalSection(&table->lock);
836wObject* HashTable_KeyObject(wHashTable* table)
842wObject* HashTable_ValueObject(wHashTable* table)
845 return &table->value;
848BOOL HashTable_SetHashFunction(wHashTable* table, HASH_TABLE_HASH_FN fn)
852 return fn !=
nullptr;
855BOOL HashTable_SetupForStringData(wHashTable* table, BOOL stringValues)
859 if (!HashTable_SetHashFunction(table, HashTable_StringHash))
862 obj = HashTable_KeyObject(table);
869 obj = HashTable_ValueObject(table);
This struct contains function pointer to initialize/free objects.
OBJECT_FREE_FN fnObjectFree
OBJECT_EQUALS_FN fnObjectEquals
OBJECT_NEW_FN fnObjectNew