andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: /* andre@0: * pkix_pl_hashtable.c andre@0: * andre@0: * Hashtable Object Functions andre@0: * andre@0: */ andre@0: andre@0: #include "pkix_pl_hashtable.h" andre@0: andre@0: /* --Private-Structure-------------------------------------------- */ andre@0: andre@0: struct PKIX_PL_HashTableStruct { andre@0: pkix_pl_PrimHashTable *primHash; andre@0: PKIX_PL_Mutex *tableLock; andre@0: PKIX_UInt32 maxEntriesPerBucket; andre@0: }; andre@0: andre@0: /* --Private-Functions-------------------------------------------- */ andre@0: andre@0: #define PKIX_MUTEX_UNLOCK(mutex) \ andre@0: do { \ andre@0: if (mutex && lockedMutex == (PKIX_PL_Mutex *)(mutex)) { \ andre@0: pkixTempResult = \ andre@0: PKIX_PL_Mutex_Unlock((mutex), plContext); \ andre@0: PORT_Assert(pkixTempResult == NULL); \ andre@0: if (pkixTempResult) { \ andre@0: PKIX_DoAddError(&stdVars, pkixTempResult, plContext); \ andre@0: pkixTempResult = NULL; \ andre@0: } \ andre@0: lockedMutex = NULL; \ andre@0: } else { \ andre@0: PORT_Assert(lockedMutex == NULL); \ andre@0: }\ andre@0: } while (0) andre@0: andre@0: andre@0: #define PKIX_MUTEX_LOCK(mutex) \ andre@0: do { \ andre@0: if (mutex){ \ andre@0: PORT_Assert(lockedMutex == NULL); \ andre@0: PKIX_CHECK(PKIX_PL_Mutex_Lock((mutex), plContext), \ andre@0: PKIX_MUTEXLOCKFAILED); \ andre@0: lockedMutex = (mutex); \ andre@0: } \ andre@0: } while (0) andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_HashTable_Destroy andre@0: * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h) andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_HashTable_Destroy( andre@0: PKIX_PL_Object *object, andre@0: void *plContext) andre@0: { andre@0: PKIX_PL_HashTable *ht = NULL; andre@0: pkix_pl_HT_Elem *item = NULL; andre@0: PKIX_UInt32 i; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "pkix_pl_HashTable_Destroy"); andre@0: PKIX_NULLCHECK_ONE(object); andre@0: andre@0: PKIX_CHECK(pkix_CheckType(object, PKIX_HASHTABLE_TYPE, plContext), andre@0: PKIX_OBJECTNOTHASHTABLE); andre@0: andre@0: ht = (PKIX_PL_HashTable*) object; andre@0: andre@0: /* DecRef every object in the primitive hash table */ andre@0: for (i = 0; i < ht->primHash->size; i++) { andre@0: for (item = ht->primHash->buckets[i]; andre@0: item != NULL; andre@0: item = item->next) { andre@0: PKIX_DECREF(item->key); andre@0: PKIX_DECREF(item->value); andre@0: } andre@0: } andre@0: andre@0: PKIX_CHECK(pkix_pl_PrimHashTable_Destroy(ht->primHash, plContext), andre@0: PKIX_PRIMHASHTABLEDESTROYFAILED); andre@0: andre@0: PKIX_DECREF(ht->tableLock); andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_HashTable_RegisterSelf andre@0: * DESCRIPTION: andre@0: * Registers PKIX_HASHTABLE_TYPE and its related functions with systemClasses[] andre@0: * THREAD SAFETY: andre@0: * Not Thread Safe - for performance and complexity reasons andre@0: * andre@0: * Since this function is only called by PKIX_PL_Initialize, which should andre@0: * only be called once, it is acceptable that this function is not andre@0: * thread-safe. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_HashTable_RegisterSelf( andre@0: void *plContext) andre@0: { andre@0: andre@0: extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES]; andre@0: pkix_ClassTable_Entry entry; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "pkix_pl_HashTable_RegisterSelf"); andre@0: andre@0: entry.description = "HashTable"; andre@0: entry.objCounter = 0; andre@0: entry.typeObjectSize = sizeof(PKIX_PL_HashTable); andre@0: entry.destructor = pkix_pl_HashTable_Destroy; andre@0: entry.equalsFunction = NULL; andre@0: entry.hashcodeFunction = NULL; andre@0: entry.toStringFunction = NULL; andre@0: entry.comparator = NULL; andre@0: entry.duplicateFunction = NULL; andre@0: andre@0: systemClasses[PKIX_HASHTABLE_TYPE] = entry; andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: andre@0: /* --Public-Functions------------------------------------------------------- */ andre@0: andre@0: /* andre@0: * FUNCTION: PKIX_PL_HashTable_Create (see comments in pkix_pl_system.h) andre@0: */ andre@0: PKIX_Error * andre@0: PKIX_PL_HashTable_Create( andre@0: PKIX_UInt32 numBuckets, andre@0: PKIX_UInt32 maxEntriesPerBucket, andre@0: PKIX_PL_HashTable **pResult, andre@0: void *plContext) andre@0: { andre@0: PKIX_PL_HashTable *hashTable = NULL; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "PKIX_PL_HashTable_Create"); andre@0: PKIX_NULLCHECK_ONE(pResult); andre@0: andre@0: if (numBuckets == 0) { andre@0: PKIX_ERROR(PKIX_NUMBUCKETSEQUALSZERO); andre@0: } andre@0: andre@0: /* Allocate a new hashtable */ andre@0: PKIX_CHECK(PKIX_PL_Object_Alloc andre@0: (PKIX_HASHTABLE_TYPE, andre@0: sizeof (PKIX_PL_HashTable), andre@0: (PKIX_PL_Object **)&hashTable, andre@0: plContext), andre@0: PKIX_COULDNOTCREATEHASHTABLEOBJECT); andre@0: andre@0: /* Create the underlying primitive hash table type */ andre@0: PKIX_CHECK(pkix_pl_PrimHashTable_Create andre@0: (numBuckets, &hashTable->primHash, plContext), andre@0: PKIX_PRIMHASHTABLECREATEFAILED); andre@0: andre@0: /* Create a lock for this table */ andre@0: PKIX_CHECK(PKIX_PL_Mutex_Create(&hashTable->tableLock, plContext), andre@0: PKIX_ERRORCREATINGTABLELOCK); andre@0: andre@0: hashTable->maxEntriesPerBucket = maxEntriesPerBucket; andre@0: andre@0: *pResult = hashTable; andre@0: andre@0: cleanup: andre@0: andre@0: if (PKIX_ERROR_RECEIVED){ andre@0: PKIX_DECREF(hashTable); andre@0: } andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: PKIX_PL_HashTable_Add (see comments in pkix_pl_system.h) andre@0: */ andre@0: PKIX_Error * andre@0: PKIX_PL_HashTable_Add( andre@0: PKIX_PL_HashTable *ht, andre@0: PKIX_PL_Object *key, andre@0: PKIX_PL_Object *value, andre@0: void *plContext) andre@0: { andre@0: PKIX_PL_Mutex *lockedMutex = NULL; andre@0: PKIX_PL_Object *deletedKey = NULL; andre@0: PKIX_PL_Object *deletedValue = NULL; andre@0: PKIX_UInt32 hashCode; andre@0: PKIX_PL_EqualsCallback keyComp; andre@0: PKIX_UInt32 bucketSize = 0; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "PKIX_PL_HashTable_Add"); andre@0: andre@0: #if !defined(PKIX_OBJECT_LEAK_TEST) andre@0: PKIX_NULLCHECK_THREE(ht, key, value); andre@0: #else andre@0: PKIX_NULLCHECK_TWO(key, value); andre@0: andre@0: if (ht == NULL) { andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: #endif andre@0: /* Insert into primitive hashtable */ andre@0: andre@0: PKIX_CHECK(PKIX_PL_Object_Hashcode(key, &hashCode, plContext), andre@0: PKIX_OBJECTHASHCODEFAILED); andre@0: andre@0: PKIX_CHECK(pkix_pl_Object_RetrieveEqualsCallback andre@0: (key, &keyComp, plContext), andre@0: PKIX_OBJECTRETRIEVEEQUALSCALLBACKFAILED); andre@0: andre@0: PKIX_MUTEX_LOCK(ht->tableLock); andre@0: andre@0: PKIX_CHECK(pkix_pl_PrimHashTable_GetBucketSize andre@0: (ht->primHash, andre@0: hashCode, andre@0: &bucketSize, andre@0: plContext), andre@0: PKIX_PRIMHASHTABLEGETBUCKETSIZEFAILED); andre@0: andre@0: if (ht->maxEntriesPerBucket != 0 && andre@0: bucketSize >= ht->maxEntriesPerBucket) { andre@0: /* drop the last one in the bucket */ andre@0: PKIX_CHECK(pkix_pl_PrimHashTable_RemoveFIFO andre@0: (ht->primHash, andre@0: hashCode, andre@0: (void **) &deletedKey, andre@0: (void **) &deletedValue, andre@0: plContext), andre@0: PKIX_PRIMHASHTABLEGETBUCKETSIZEFAILED); andre@0: PKIX_DECREF(deletedKey); andre@0: PKIX_DECREF(deletedValue); andre@0: } andre@0: andre@0: PKIX_CHECK(pkix_pl_PrimHashTable_Add andre@0: (ht->primHash, andre@0: (void *)key, andre@0: (void *)value, andre@0: hashCode, andre@0: keyComp, andre@0: plContext), andre@0: PKIX_PRIMHASHTABLEADDFAILED); andre@0: andre@0: PKIX_INCREF(key); andre@0: PKIX_INCREF(value); andre@0: PKIX_MUTEX_UNLOCK(ht->tableLock); andre@0: andre@0: /* andre@0: * we don't call PKIX_PL_InvalidateCache here b/c we have andre@0: * not implemented toString or hashcode for this Object andre@0: */ andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_MUTEX_UNLOCK(ht->tableLock); andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: PKIX_PL_HashTable_Remove (see comments in pkix_pl_system.h) andre@0: */ andre@0: PKIX_Error * andre@0: PKIX_PL_HashTable_Remove( andre@0: PKIX_PL_HashTable *ht, andre@0: PKIX_PL_Object *key, andre@0: void *plContext) andre@0: { andre@0: PKIX_PL_Mutex *lockedMutex = NULL; andre@0: PKIX_PL_Object *origKey = NULL; andre@0: PKIX_PL_Object *value = NULL; andre@0: PKIX_UInt32 hashCode; andre@0: PKIX_PL_EqualsCallback keyComp; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "PKIX_PL_HashTable_Remove"); andre@0: andre@0: #if !defined(PKIX_OBJECT_LEAK_TEST) andre@0: PKIX_NULLCHECK_TWO(ht, key); andre@0: #else andre@0: PKIX_NULLCHECK_ONE(key); andre@0: andre@0: if (ht == NULL) { andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: #endif andre@0: andre@0: PKIX_CHECK(PKIX_PL_Object_Hashcode(key, &hashCode, plContext), andre@0: PKIX_OBJECTHASHCODEFAILED); andre@0: andre@0: PKIX_CHECK(pkix_pl_Object_RetrieveEqualsCallback andre@0: (key, &keyComp, plContext), andre@0: PKIX_OBJECTRETRIEVEEQUALSCALLBACKFAILED); andre@0: andre@0: PKIX_MUTEX_LOCK(ht->tableLock); andre@0: andre@0: /* Remove from primitive hashtable */ andre@0: PKIX_CHECK(pkix_pl_PrimHashTable_Remove andre@0: (ht->primHash, andre@0: (void *)key, andre@0: hashCode, andre@0: keyComp, andre@0: (void **)&origKey, andre@0: (void **)&value, andre@0: plContext), andre@0: PKIX_PRIMHASHTABLEREMOVEFAILED); andre@0: andre@0: PKIX_MUTEX_UNLOCK(ht->tableLock); andre@0: andre@0: PKIX_DECREF(origKey); andre@0: PKIX_DECREF(value); andre@0: andre@0: /* andre@0: * we don't call PKIX_PL_InvalidateCache here b/c we have andre@0: * not implemented toString or hashcode for this Object andre@0: */ andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_MUTEX_UNLOCK(ht->tableLock); andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: PKIX_PL_HashTable_Lookup (see comments in pkix_pl_system.h) andre@0: */ andre@0: PKIX_Error * andre@0: PKIX_PL_HashTable_Lookup( andre@0: PKIX_PL_HashTable *ht, andre@0: PKIX_PL_Object *key, andre@0: PKIX_PL_Object **pResult, andre@0: void *plContext) andre@0: { andre@0: PKIX_PL_Mutex *lockedMutex = NULL; andre@0: PKIX_UInt32 hashCode; andre@0: PKIX_PL_EqualsCallback keyComp; andre@0: PKIX_PL_Object *result = NULL; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "PKIX_PL_HashTable_Lookup"); andre@0: andre@0: #if !defined(PKIX_OBJECT_LEAK_TEST) andre@0: PKIX_NULLCHECK_THREE(ht, key, pResult); andre@0: #else andre@0: PKIX_NULLCHECK_TWO(key, pResult); andre@0: andre@0: if (ht == NULL) { andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: #endif andre@0: andre@0: PKIX_CHECK(PKIX_PL_Object_Hashcode(key, &hashCode, plContext), andre@0: PKIX_OBJECTHASHCODEFAILED); andre@0: andre@0: PKIX_CHECK(pkix_pl_Object_RetrieveEqualsCallback andre@0: (key, &keyComp, plContext), andre@0: PKIX_OBJECTRETRIEVEEQUALSCALLBACKFAILED); andre@0: andre@0: PKIX_MUTEX_LOCK(ht->tableLock); andre@0: andre@0: /* Lookup in primitive hashtable */ andre@0: PKIX_CHECK(pkix_pl_PrimHashTable_Lookup andre@0: (ht->primHash, andre@0: (void *)key, andre@0: hashCode, andre@0: keyComp, andre@0: (void **)&result, andre@0: plContext), andre@0: PKIX_PRIMHASHTABLELOOKUPFAILED); andre@0: andre@0: PKIX_INCREF(result); andre@0: PKIX_MUTEX_UNLOCK(ht->tableLock); andre@0: andre@0: *pResult = result; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_MUTEX_UNLOCK(ht->tableLock); andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: }