20 #include <winpr/config.h>
22 #include <winpr/crt.h>
23 #include <winpr/assert.h>
25 #include <winpr/collections.h>
35 typedef struct s_wKeyValuePair wKeyValuePair;
37 struct s_wKeyValuePair
54 float lowerRehashThreshold;
55 float upperRehashThreshold;
56 wKeyValuePair** bucketArray;
58 HASH_TABLE_HASH_FN hash;
62 DWORD foreachRecursionLevel;
66 BOOL HashTable_PointerCompare(
const void* pointer1,
const void* pointer2)
68 return (pointer1 == pointer2);
71 UINT32 HashTable_PointerHash(
const void* pointer)
73 return ((UINT32)(UINT_PTR)pointer) >> 4;
76 BOOL 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);
84 UINT32 HashTable_StringHash(
const void* key)
88 const BYTE* str = (
const BYTE*)key;
91 while ((c = *str++) !=
'\0')
92 hash = (hash * 33) + c;
97 void* HashTable_StringClone(
const void* str)
99 return winpr_ObjectStringClone(str);
102 void HashTable_StringFree(
void* str)
104 winpr_ObjectStringFree(str);
107 static INLINE BOOL HashTable_IsProbablePrime(
size_t oddNumber)
109 for (
size_t i = 3; i < 51; i += 2)
113 else if (oddNumber % i == 0)
120 static INLINE
size_t HashTable_CalculateIdealNumOfBuckets(wHashTable* table)
124 const float numOfElements = (float)table->numOfElements;
125 const float tmp = (numOfElements / table->idealRatio);
126 size_t idealNumOfBuckets = (size_t)tmp;
128 if (idealNumOfBuckets < 5)
129 idealNumOfBuckets = 5;
131 idealNumOfBuckets |= 0x01;
133 while (!HashTable_IsProbablePrime(idealNumOfBuckets))
134 idealNumOfBuckets += 2;
136 return idealNumOfBuckets;
139 static INLINE
void HashTable_Rehash(wHashTable* table,
size_t numOfBuckets)
141 UINT32 hashValue = 0;
142 wKeyValuePair* nextPair = NULL;
143 wKeyValuePair** newBucketArray = NULL;
146 if (numOfBuckets == 0)
147 numOfBuckets = HashTable_CalculateIdealNumOfBuckets(table);
149 if (numOfBuckets == table->numOfBuckets)
152 newBucketArray = (wKeyValuePair**)calloc(numOfBuckets,
sizeof(wKeyValuePair*));
163 for (
size_t index = 0; index < table->numOfBuckets; index++)
165 wKeyValuePair* pair = table->bucketArray[index];
169 nextPair = pair->next;
170 hashValue = table->hash(pair->key) % numOfBuckets;
171 pair->next = newBucketArray[hashValue];
172 newBucketArray[hashValue] = pair;
177 free(table->bucketArray);
178 table->bucketArray = newBucketArray;
179 table->numOfBuckets = numOfBuckets;
182 static INLINE BOOL HashTable_Equals(wHashTable* table,
const wKeyValuePair* pair,
const void* key)
187 return table->key.fnObjectEquals(key, pair->key);
190 static INLINE wKeyValuePair* HashTable_Get(wHashTable* table,
const void* key)
192 UINT32 hashValue = 0;
193 wKeyValuePair* pair = NULL;
199 hashValue = table->hash(key) % table->numOfBuckets;
200 pair = table->bucketArray[hashValue];
202 while (pair && !HashTable_Equals(table, pair, key))
208 static INLINE
void disposeKey(wHashTable* table,
void* key)
211 if (table->key.fnObjectFree)
212 table->key.fnObjectFree(key);
215 static INLINE
void disposeValue(wHashTable* table,
void* value)
218 if (table->value.fnObjectFree)
219 table->value.fnObjectFree(value);
222 static INLINE
void disposePair(wHashTable* table, wKeyValuePair* pair)
227 disposeKey(table, pair->key);
228 disposeValue(table, pair->value);
232 static INLINE
void setKey(wHashTable* table, wKeyValuePair* pair,
const void* key)
237 disposeKey(table, pair->key);
238 if (table->key.fnObjectNew)
239 pair->key = table->key.fnObjectNew(key);
252 static INLINE
void setValue(wHashTable* table, wKeyValuePair* pair,
const void* value)
257 disposeValue(table, pair->value);
258 if (table->value.fnObjectNew)
259 pair->value = table->value.fnObjectNew(value);
268 pair->value = cnv.pv;
285 size_t HashTable_Count(wHashTable* table)
288 return table->numOfElements;
298 #if defined(WITH_WINPR_DEPRECATED)
299 int HashTable_Add(wHashTable* table,
const void* key,
const void* value)
301 if (!HashTable_Insert(table, key, value))
307 BOOL HashTable_Insert(wHashTable* table,
const void* key,
const void* value)
310 UINT32 hashValue = 0;
311 wKeyValuePair* pair = NULL;
312 wKeyValuePair* newPair = NULL;
318 if (table->synchronized)
319 EnterCriticalSection(&table->lock);
321 hashValue = table->hash(key) % table->numOfBuckets;
322 pair = table->bucketArray[hashValue];
324 while (pair && !HashTable_Equals(table, pair, key))
329 if (pair->markedForRemove)
332 table->pendingRemoves--;
333 pair->markedForRemove = FALSE;
334 table->numOfElements++;
337 if (pair->key != key)
339 setKey(table, pair, key);
342 if (pair->value != value)
344 setValue(table, pair, value);
350 newPair = (wKeyValuePair*)calloc(1,
sizeof(wKeyValuePair));
354 setKey(table, newPair, key);
355 setValue(table, newPair, value);
356 newPair->next = table->bucketArray[hashValue];
357 newPair->markedForRemove = FALSE;
358 table->bucketArray[hashValue] = newPair;
359 table->numOfElements++;
361 if (!table->foreachRecursionLevel && table->upperRehashThreshold > table->idealRatio)
363 float elementToBucketRatio =
364 (float)table->numOfElements / (
float)table->numOfBuckets;
366 if (elementToBucketRatio > table->upperRehashThreshold)
367 HashTable_Rehash(table, 0);
373 if (table->synchronized)
374 LeaveCriticalSection(&table->lock);
383 BOOL HashTable_Remove(wHashTable* table,
const void* key)
385 UINT32 hashValue = 0;
387 wKeyValuePair* pair = NULL;
388 wKeyValuePair* previousPair = NULL;
394 if (table->synchronized)
395 EnterCriticalSection(&table->lock);
397 hashValue = table->hash(key) % table->numOfBuckets;
398 pair = table->bucketArray[hashValue];
400 while (pair && !HashTable_Equals(table, pair, key))
412 if (table->foreachRecursionLevel)
415 pair->markedForRemove = TRUE;
416 table->pendingRemoves++;
417 table->numOfElements--;
422 previousPair->next = pair->next;
424 table->bucketArray[hashValue] = pair->next;
426 disposePair(table, pair);
427 table->numOfElements--;
429 if (!table->foreachRecursionLevel && table->lowerRehashThreshold > 0.0f)
431 float elementToBucketRatio = (float)table->numOfElements / (
float)table->numOfBuckets;
433 if (elementToBucketRatio < table->lowerRehashThreshold)
434 HashTable_Rehash(table, 0);
438 if (table->synchronized)
439 LeaveCriticalSection(&table->lock);
448 void* HashTable_GetItemValue(wHashTable* table,
const void* key)
451 wKeyValuePair* pair = NULL;
457 if (table->synchronized)
458 EnterCriticalSection(&table->lock);
460 pair = HashTable_Get(table, key);
462 if (pair && !pair->markedForRemove)
465 if (table->synchronized)
466 LeaveCriticalSection(&table->lock);
475 BOOL HashTable_SetItemValue(wHashTable* table,
const void* key,
const void* value)
478 wKeyValuePair* pair = NULL;
484 if (table->synchronized)
485 EnterCriticalSection(&table->lock);
487 pair = HashTable_Get(table, key);
489 if (!pair || pair->markedForRemove)
493 setValue(table, pair, value);
496 if (table->synchronized)
497 LeaveCriticalSection(&table->lock);
506 void HashTable_Clear(wHashTable* table)
508 wKeyValuePair* nextPair = NULL;
512 if (table->synchronized)
513 EnterCriticalSection(&table->lock);
515 for (
size_t index = 0; index < table->numOfBuckets; index++)
517 wKeyValuePair* pair = table->bucketArray[index];
521 nextPair = pair->next;
523 if (table->foreachRecursionLevel)
526 pair->markedForRemove = TRUE;
527 table->pendingRemoves++;
531 disposePair(table, pair);
536 table->bucketArray[index] = NULL;
539 table->numOfElements = 0;
540 if (table->foreachRecursionLevel == 0)
541 HashTable_Rehash(table, 5);
543 if (table->synchronized)
544 LeaveCriticalSection(&table->lock);
551 size_t HashTable_GetKeys(wHashTable* table, ULONG_PTR** ppKeys)
555 ULONG_PTR* pKeys = NULL;
556 wKeyValuePair* nextPair = NULL;
560 if (table->synchronized)
561 EnterCriticalSection(&table->lock);
564 count = table->numOfElements;
570 if (table->synchronized)
571 LeaveCriticalSection(&table->lock);
576 pKeys = (ULONG_PTR*)calloc(count,
sizeof(ULONG_PTR));
580 if (table->synchronized)
581 LeaveCriticalSection(&table->lock);
586 for (
size_t index = 0; index < table->numOfBuckets; index++)
588 wKeyValuePair* pair = table->bucketArray[index];
592 nextPair = pair->next;
593 if (!pair->markedForRemove)
594 pKeys[iKey++] = (ULONG_PTR)pair->key;
599 if (table->synchronized)
600 LeaveCriticalSection(&table->lock);
609 BOOL HashTable_Foreach(wHashTable* table, HASH_TABLE_FOREACH_FN fn, VOID* arg)
616 if (table->synchronized)
617 EnterCriticalSection(&table->lock);
619 table->foreachRecursionLevel++;
620 for (
size_t index = 0; index < table->numOfBuckets; index++)
622 for (wKeyValuePair* pair = table->bucketArray[index]; pair; pair = pair->next)
624 if (!pair->markedForRemove && !fn(pair->key, pair->value, arg))
631 table->foreachRecursionLevel--;
633 if (!table->foreachRecursionLevel && table->pendingRemoves)
636 wKeyValuePair** prevPtr = NULL;
637 for (
size_t index = 0; index < table->numOfBuckets; index++)
639 wKeyValuePair* nextPair = NULL;
640 prevPtr = &table->bucketArray[index];
641 for (wKeyValuePair* pair = table->bucketArray[index]; pair;)
643 nextPair = pair->next;
645 if (pair->markedForRemove)
647 disposePair(table, pair);
652 prevPtr = &pair->next;
657 table->pendingRemoves = 0;
661 if (table->synchronized)
662 LeaveCriticalSection(&table->lock);
670 BOOL HashTable_Contains(wHashTable* table,
const void* key)
673 wKeyValuePair* pair = NULL;
679 if (table->synchronized)
680 EnterCriticalSection(&table->lock);
682 pair = HashTable_Get(table, key);
683 status = (pair && !pair->markedForRemove);
685 if (table->synchronized)
686 LeaveCriticalSection(&table->lock);
695 BOOL HashTable_ContainsKey(wHashTable* table,
const void* key)
698 wKeyValuePair* pair = NULL;
704 if (table->synchronized)
705 EnterCriticalSection(&table->lock);
707 pair = HashTable_Get(table, key);
708 status = (pair && !pair->markedForRemove);
710 if (table->synchronized)
711 LeaveCriticalSection(&table->lock);
720 BOOL HashTable_ContainsValue(wHashTable* table,
const void* value)
728 if (table->synchronized)
729 EnterCriticalSection(&table->lock);
731 for (
size_t index = 0; index < table->numOfBuckets; index++)
733 wKeyValuePair* pair = table->bucketArray[index];
737 if (!pair->markedForRemove && HashTable_Equals(table, pair, value))
750 if (table->synchronized)
751 LeaveCriticalSection(&table->lock);
760 wHashTable* HashTable_New(BOOL
synchronized)
762 wHashTable* table = (wHashTable*)calloc(1,
sizeof(wHashTable));
767 table->synchronized =
synchronized;
768 InitializeCriticalSectionAndSpinCount(&(table->lock), 4000);
769 table->numOfBuckets = 64;
770 table->numOfElements = 0;
771 table->bucketArray = (wKeyValuePair**)calloc(table->numOfBuckets,
sizeof(wKeyValuePair*));
773 if (!table->bucketArray)
776 table->idealRatio = 3.0f;
777 table->lowerRehashThreshold = 0.0f;
778 table->upperRehashThreshold = 15.0f;
779 table->hash = HashTable_PointerHash;
780 table->key.fnObjectEquals = HashTable_PointerCompare;
781 table->value.fnObjectEquals = HashTable_PointerCompare;
785 WINPR_PRAGMA_DIAG_PUSH
786 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
787 HashTable_Free(table);
788 WINPR_PRAGMA_DIAG_POP
792 void HashTable_Free(wHashTable* table)
794 wKeyValuePair* pair = NULL;
795 wKeyValuePair* nextPair = NULL;
800 if (table->bucketArray)
802 for (
size_t index = 0; index < table->numOfBuckets; index++)
804 pair = table->bucketArray[index];
808 nextPair = pair->next;
810 disposePair(table, pair);
814 free(table->bucketArray);
816 DeleteCriticalSection(&(table->lock));
821 void HashTable_Lock(wHashTable* table)
824 EnterCriticalSection(&table->lock);
827 void HashTable_Unlock(wHashTable* table)
830 LeaveCriticalSection(&table->lock);
833 wObject* HashTable_KeyObject(wHashTable* table)
839 wObject* HashTable_ValueObject(wHashTable* table)
842 return &table->value;
845 BOOL HashTable_SetHashFunction(wHashTable* table, HASH_TABLE_HASH_FN fn)
852 BOOL HashTable_SetupForStringData(wHashTable* table, BOOL stringValues)
856 if (!HashTable_SetHashFunction(table, HashTable_StringHash))
859 obj = HashTable_KeyObject(table);
860 obj->fnObjectEquals = HashTable_StringCompare;
861 obj->fnObjectNew = HashTable_StringClone;
862 obj->fnObjectFree = HashTable_StringFree;
866 obj = HashTable_ValueObject(table);
867 obj->fnObjectEquals = HashTable_StringCompare;
868 obj->fnObjectNew = HashTable_StringClone;
869 obj->fnObjectFree = HashTable_StringFree;
This struct contains function pointer to initialize/free objects.