123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646 |
- /**
- * WinPR: Windows Portable Runtime
- * System.Collections.Hashtable
- *
- * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include <winpr/crt.h>
- #include <winpr/collections.h>
- /**
- * This implementation is based on the public domain
- * hash table implementation made by Keith Pomakis:
- *
- * http://www.pomakis.com/hashtable/hashtable.c
- * http://www.pomakis.com/hashtable/hashtable.h
- */
- BOOL HashTable_PointerCompare(void* pointer1, void* pointer2)
- {
- return (pointer1 == pointer2);
- }
- UINT32 HashTable_PointerHash(void* pointer)
- {
- return ((UINT32)(UINT_PTR)pointer) >> 4;
- }
- BOOL HashTable_StringCompare(void* string1, void* string2)
- {
- if (!string1 || !string2)
- return (string1 == string2);
- return (strcmp((char*)string1, (char*)string2) == 0);
- }
- UINT32 HashTable_StringHash(void* key)
- {
- UINT32 c;
- UINT32 hash = 5381;
- BYTE* str = (BYTE*)key;
- /* djb2 algorithm */
- while ((c = *str++) != '\0')
- hash = (hash * 33) + c;
- return hash;
- }
- void* HashTable_StringClone(void* str)
- {
- return _strdup((char*)str);
- }
- void HashTable_StringFree(void* str)
- {
- free(str);
- }
- static int HashTable_IsProbablePrime(int oddNumber)
- {
- int i;
- for (i = 3; i < 51; i += 2)
- {
- if (oddNumber == i)
- return 1;
- else if (oddNumber % i == 0)
- return 0;
- }
- return 1; /* maybe */
- }
- static long HashTable_CalculateIdealNumOfBuckets(wHashTable* table)
- {
- int idealNumOfBuckets = table->numOfElements / ((int)table->idealRatio);
- if (idealNumOfBuckets < 5)
- idealNumOfBuckets = 5;
- else
- idealNumOfBuckets |= 0x01;
- while (!HashTable_IsProbablePrime(idealNumOfBuckets))
- idealNumOfBuckets += 2;
- return idealNumOfBuckets;
- }
- static void HashTable_Rehash(wHashTable* table, int numOfBuckets)
- {
- int index;
- UINT32 hashValue;
- wKeyValuePair* pair;
- wKeyValuePair* nextPair;
- wKeyValuePair** newBucketArray;
- if (numOfBuckets == 0)
- numOfBuckets = HashTable_CalculateIdealNumOfBuckets(table);
- if (numOfBuckets == table->numOfBuckets)
- return; /* already the right size! */
- newBucketArray = (wKeyValuePair**)calloc(numOfBuckets, sizeof(wKeyValuePair*));
- if (!newBucketArray)
- {
- /*
- * Couldn't allocate memory for the new array.
- * This isn't a fatal error; we just can't perform the rehash.
- */
- return;
- }
- for (index = 0; index < table->numOfBuckets; index++)
- {
- pair = table->bucketArray[index];
- while (pair)
- {
- nextPair = pair->next;
- hashValue = table->hash(pair->key) % numOfBuckets;
- pair->next = newBucketArray[hashValue];
- newBucketArray[hashValue] = pair;
- pair = nextPair;
- }
- }
- free(table->bucketArray);
- table->bucketArray = newBucketArray;
- table->numOfBuckets = numOfBuckets;
- }
- static void HashTable_SetIdealRatio(wHashTable* table, float idealRatio, float lowerRehashThreshold,
- float upperRehashThreshold)
- {
- table->idealRatio = idealRatio;
- table->lowerRehashThreshold = lowerRehashThreshold;
- table->upperRehashThreshold = upperRehashThreshold;
- }
- wKeyValuePair* HashTable_Get(wHashTable* table, void* key)
- {
- UINT32 hashValue;
- wKeyValuePair* pair;
- hashValue = table->hash(key) % table->numOfBuckets;
- pair = table->bucketArray[hashValue];
- while (pair && !table->keyCompare(key, pair->key))
- pair = pair->next;
- return pair;
- }
- /**
- * C equivalent of the C# Hashtable Class:
- * http://msdn.microsoft.com/en-us/library/system.collections.hashtable.aspx
- */
- /**
- * Properties
- */
- /**
- * Gets the number of key/value pairs contained in the HashTable.
- */
- int HashTable_Count(wHashTable* table)
- {
- return table->numOfElements;
- }
- /**
- * Methods
- */
- /**
- * Adds an element with the specified key and value into the HashTable.
- */
- int HashTable_Add(wHashTable* table, void* key, void* value)
- {
- int status = 0;
- UINT32 hashValue;
- wKeyValuePair* pair;
- wKeyValuePair* newPair;
- if (!key || !value)
- return -1;
- if (table->keyClone)
- {
- key = table->keyClone(key);
- if (!key)
- return -1;
- }
- if (table->valueClone)
- {
- value = table->valueClone(value);
- if (!value)
- return -1;
- }
- if (table->synchronized)
- EnterCriticalSection(&table->lock);
- hashValue = table->hash(key) % table->numOfBuckets;
- pair = table->bucketArray[hashValue];
- while (pair && !table->keyCompare(key, pair->key))
- pair = pair->next;
- if (pair)
- {
- if (pair->key != key)
- {
- if (table->keyFree)
- table->keyFree(pair->key);
- pair->key = key;
- }
- if (pair->value != value)
- {
- if (table->valueFree)
- table->valueFree(pair->value);
- pair->value = value;
- }
- }
- else
- {
- newPair = (wKeyValuePair*)malloc(sizeof(wKeyValuePair));
- if (!newPair)
- {
- status = -1;
- }
- else
- {
- newPair->key = key;
- newPair->value = value;
- newPair->next = table->bucketArray[hashValue];
- table->bucketArray[hashValue] = newPair;
- table->numOfElements++;
- if (table->upperRehashThreshold > table->idealRatio)
- {
- float elementToBucketRatio =
- (float)table->numOfElements / (float)table->numOfBuckets;
- if (elementToBucketRatio > table->upperRehashThreshold)
- HashTable_Rehash(table, 0);
- }
- }
- }
- if (table->synchronized)
- LeaveCriticalSection(&table->lock);
- return status;
- }
- /**
- * Removes the element with the specified key from the HashTable.
- */
- BOOL HashTable_Remove(wHashTable* table, void* key)
- {
- UINT32 hashValue;
- BOOL status = TRUE;
- wKeyValuePair* pair = NULL;
- wKeyValuePair* previousPair = NULL;
- if (table->synchronized)
- EnterCriticalSection(&table->lock);
- hashValue = table->hash(key) % table->numOfBuckets;
- pair = table->bucketArray[hashValue];
- while (pair && !table->keyCompare(key, pair->key))
- {
- previousPair = pair;
- pair = pair->next;
- }
- if (!pair)
- {
- status = FALSE;
- }
- else
- {
- if (table->keyFree)
- table->keyFree(pair->key);
- if (table->valueFree)
- table->valueFree(pair->value);
- if (previousPair)
- previousPair->next = pair->next;
- else
- table->bucketArray[hashValue] = pair->next;
- free(pair);
- table->numOfElements--;
- if (table->lowerRehashThreshold > 0.0)
- {
- float elementToBucketRatio = (float)table->numOfElements / (float)table->numOfBuckets;
- if (elementToBucketRatio < table->lowerRehashThreshold)
- HashTable_Rehash(table, 0);
- }
- }
- if (table->synchronized)
- LeaveCriticalSection(&table->lock);
- return status;
- }
- /**
- * Get an item value using key
- */
- void* HashTable_GetItemValue(wHashTable* table, void* key)
- {
- void* value = NULL;
- wKeyValuePair* pair;
- if (table->synchronized)
- EnterCriticalSection(&table->lock);
- pair = HashTable_Get(table, key);
- if (pair)
- value = pair->value;
- if (table->synchronized)
- LeaveCriticalSection(&table->lock);
- return value;
- }
- /**
- * Set an item value using key
- */
- BOOL HashTable_SetItemValue(wHashTable* table, void* key, void* value)
- {
- BOOL status = TRUE;
- wKeyValuePair* pair;
- if (table->valueClone && value)
- {
- value = table->valueClone(value);
- if (!value)
- return FALSE;
- }
- if (table->synchronized)
- EnterCriticalSection(&table->lock);
- pair = HashTable_Get(table, key);
- if (!pair)
- status = FALSE;
- else
- {
- if (table->valueClone && table->valueFree)
- table->valueFree(pair->value);
- pair->value = value;
- }
- if (table->synchronized)
- LeaveCriticalSection(&table->lock);
- return status;
- }
- /**
- * Removes all elements from the HashTable.
- */
- void HashTable_Clear(wHashTable* table)
- {
- int index;
- wKeyValuePair* pair;
- wKeyValuePair* nextPair;
- if (table->synchronized)
- EnterCriticalSection(&table->lock);
- for (index = 0; index < table->numOfBuckets; index++)
- {
- pair = table->bucketArray[index];
- while (pair)
- {
- nextPair = pair->next;
- if (table->keyFree)
- table->keyFree(pair->key);
- if (table->valueFree)
- table->valueFree(pair->value);
- free(pair);
- pair = nextPair;
- }
- table->bucketArray[index] = NULL;
- }
- table->numOfElements = 0;
- HashTable_Rehash(table, 5);
- if (table->synchronized)
- LeaveCriticalSection(&table->lock);
- }
- /**
- * Gets the list of keys as an array
- */
- int HashTable_GetKeys(wHashTable* table, ULONG_PTR** ppKeys)
- {
- int iKey;
- int count;
- int index;
- ULONG_PTR* pKeys;
- wKeyValuePair* pair;
- wKeyValuePair* nextPair;
- if (table->synchronized)
- EnterCriticalSection(&table->lock);
- iKey = 0;
- count = table->numOfElements;
- *ppKeys = NULL;
- if (count < 1)
- {
- if (table->synchronized)
- LeaveCriticalSection(&table->lock);
- return 0;
- }
- pKeys = (ULONG_PTR*)calloc(count, sizeof(ULONG_PTR));
- if (!pKeys)
- {
- if (table->synchronized)
- LeaveCriticalSection(&table->lock);
- return -1;
- }
- for (index = 0; index < table->numOfBuckets; index++)
- {
- pair = table->bucketArray[index];
- while (pair)
- {
- nextPair = pair->next;
- pKeys[iKey++] = (ULONG_PTR)pair->key;
- pair = nextPair;
- }
- }
- if (table->synchronized)
- LeaveCriticalSection(&table->lock);
- *ppKeys = pKeys;
- return count;
- }
- /**
- * Determines whether the HashTable contains a specific key.
- */
- BOOL HashTable_Contains(wHashTable* table, void* key)
- {
- BOOL status;
- if (table->synchronized)
- EnterCriticalSection(&table->lock);
- status = (HashTable_Get(table, key) != NULL) ? TRUE : FALSE;
- if (table->synchronized)
- LeaveCriticalSection(&table->lock);
- return status;
- }
- /**
- * Determines whether the HashTable contains a specific key.
- */
- BOOL HashTable_ContainsKey(wHashTable* table, void* key)
- {
- BOOL status;
- if (table->synchronized)
- EnterCriticalSection(&table->lock);
- status = (HashTable_Get(table, key) != NULL) ? TRUE : FALSE;
- if (table->synchronized)
- LeaveCriticalSection(&table->lock);
- return status;
- }
- /**
- * Determines whether the HashTable contains a specific value.
- */
- BOOL HashTable_ContainsValue(wHashTable* table, void* value)
- {
- int index;
- BOOL status = FALSE;
- wKeyValuePair* pair;
- if (table->synchronized)
- EnterCriticalSection(&table->lock);
- for (index = 0; index < table->numOfBuckets; index++)
- {
- pair = table->bucketArray[index];
- while (pair)
- {
- if (table->valueCompare(value, pair->value))
- {
- status = TRUE;
- break;
- }
- pair = pair->next;
- }
- if (status)
- break;
- }
- if (table->synchronized)
- LeaveCriticalSection(&table->lock);
- return status;
- }
- /**
- * Construction, Destruction
- */
- wHashTable* HashTable_New(BOOL synchronized)
- {
- wHashTable* table;
- table = (wHashTable*)calloc(1, sizeof(wHashTable));
- if (table)
- {
- table->synchronized = synchronized;
- InitializeCriticalSectionAndSpinCount(&(table->lock), 4000);
- table->numOfBuckets = 64;
- table->numOfElements = 0;
- table->bucketArray = (wKeyValuePair**)calloc(table->numOfBuckets, sizeof(wKeyValuePair*));
- if (!table->bucketArray)
- {
- free(table);
- return NULL;
- }
- table->idealRatio = 3.0;
- table->lowerRehashThreshold = 0.0;
- table->upperRehashThreshold = 15.0;
- table->hash = HashTable_PointerHash;
- table->keyCompare = HashTable_PointerCompare;
- table->valueCompare = HashTable_PointerCompare;
- table->keyClone = NULL;
- table->valueClone = NULL;
- table->keyFree = NULL;
- table->valueFree = NULL;
- }
- return table;
- }
- void HashTable_Free(wHashTable* table)
- {
- int index;
- wKeyValuePair* pair;
- wKeyValuePair* nextPair;
- if (table)
- {
- for (index = 0; index < table->numOfBuckets; index++)
- {
- pair = table->bucketArray[index];
- while (pair)
- {
- nextPair = pair->next;
- if (table->keyFree)
- table->keyFree(pair->key);
- if (table->valueFree)
- table->valueFree(pair->value);
- free(pair);
- pair = nextPair;
- }
- }
- DeleteCriticalSection(&(table->lock));
- free(table->bucketArray);
- free(table);
- }
- }
|