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_primhash.c andre@0: * andre@0: * Primitive (non-object) Hashtable Functions andre@0: * andre@0: */ andre@0: andre@0: #include "pkix_pl_primhash.h" andre@0: andre@0: /* --Private-Functions---------------------------------------- */ andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_KeyComparator_Default andre@0: * DESCRIPTION: andre@0: * andre@0: * Compares the integer pointed to by "firstKey" with the integer pointed to andre@0: * by "secondKey" for equality and stores the Boolean result at "pResult". andre@0: * This default key comparator assumes each key is a PKIX_UInt32, and it andre@0: * simply tests them for equality. andre@0: * andre@0: * PARAMETERS: andre@0: * "firstKey" andre@0: * Address of the first integer key to compare. Must be non-NULL. andre@0: * The EqualsCallback for this Object will be called. andre@0: * "secondKey" andre@0: * Address of the second integer key to compare. Must be non-NULL. andre@0: * "pResult" andre@0: * Address where Boolean will be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_KeyComparator_Default( andre@0: PKIX_UInt32 *firstKey, andre@0: PKIX_UInt32 *secondKey, andre@0: PKIX_Boolean *pResult, andre@0: void *plContext) andre@0: { andre@0: /* Assume both keys are pointers to PKIX_UInt32 */ andre@0: PKIX_UInt32 firstInt, secondInt; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "pkix_pl_KeyComparator_Default"); andre@0: PKIX_NULLCHECK_THREE(firstKey, secondKey, pResult); andre@0: andre@0: firstInt = *firstKey; andre@0: secondInt = *secondKey; andre@0: andre@0: *pResult = (firstInt == secondInt)?PKIX_TRUE:PKIX_FALSE; andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_PrimHashTable_Create andre@0: * DESCRIPTION: andre@0: * andre@0: * Creates a new PrimHashtable object with a number of buckets equal to andre@0: * "numBuckets" and stores the result at "pResult". andre@0: * andre@0: * PARAMETERS: andre@0: * "numBuckets" andre@0: * The number of hash table buckets. Must be non-zero. andre@0: * "pResult" andre@0: * Address where PrimHashTable pointer will be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_PrimHashTable_Create( andre@0: PKIX_UInt32 numBuckets, andre@0: pkix_pl_PrimHashTable **pResult, andre@0: void *plContext) andre@0: { andre@0: pkix_pl_PrimHashTable *primHashTable = NULL; andre@0: PKIX_UInt32 i; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "pkix_pl_PrimHashTable_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_Malloc andre@0: (sizeof (pkix_pl_PrimHashTable), andre@0: (void **)&primHashTable, andre@0: plContext), andre@0: PKIX_MALLOCFAILED); andre@0: andre@0: primHashTable->size = numBuckets; andre@0: andre@0: /* Allocate space for the buckets */ andre@0: PKIX_CHECK(PKIX_PL_Malloc andre@0: (numBuckets * sizeof (pkix_pl_HT_Elem*), andre@0: (void **)&primHashTable->buckets, andre@0: plContext), andre@0: PKIX_MALLOCFAILED); andre@0: andre@0: for (i = 0; i < numBuckets; i++) { andre@0: primHashTable->buckets[i] = NULL; andre@0: } andre@0: andre@0: *pResult = primHashTable; andre@0: andre@0: cleanup: andre@0: andre@0: if (PKIX_ERROR_RECEIVED){ andre@0: PKIX_FREE(primHashTable); andre@0: } andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_PrimHashTable_Add andre@0: * DESCRIPTION: andre@0: * andre@0: * Adds the value pointed to by "value" to the PrimHashTable pointed to by andre@0: * "ht" using the key pointed to by "key" and the hashCode value equal to andre@0: * "hashCode", using the function pointed to by "keyComp" to compare keys. andre@0: * Assumes the key is either a PKIX_UInt32 or a PKIX_PL_Object. If the value andre@0: * already exists in the hashtable, this function returns a non-fatal error. andre@0: * andre@0: * PARAMETERS: andre@0: * "ht" andre@0: * Address of PrimHashtable to insert into. Must be non-NULL. andre@0: * "key" andre@0: * Address of key. Typically a PKIX_UInt32 or PKIX_PL_Object. andre@0: * Must be non-NULL. andre@0: * "value" andre@0: * Address of Object to be added to PrimHashtable. Must be non-NULL. andre@0: * "hashCode" andre@0: * Hashcode value of the key. andre@0: * "keyComp" andre@0: * Address of function used to determine if two keys are equal. andre@0: * If NULL, pkix_pl_KeyComparator_Default is used. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Not Thread Safe - assumes exclusive access to "ht" andre@0: * (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a HashTable Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_PrimHashTable_Add( andre@0: pkix_pl_PrimHashTable *ht, andre@0: void *key, andre@0: void *value, andre@0: PKIX_UInt32 hashCode, andre@0: PKIX_PL_EqualsCallback keyComp, andre@0: void *plContext) andre@0: { andre@0: pkix_pl_HT_Elem **elemPtr = NULL; andre@0: pkix_pl_HT_Elem *element = NULL; andre@0: PKIX_Boolean compResult = PKIX_FALSE; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "pkix_pl_PrimHashTable_Add"); andre@0: PKIX_NULLCHECK_THREE(ht, key, value); andre@0: andre@0: for (elemPtr = &((ht->buckets)[hashCode%ht->size]), element = *elemPtr; andre@0: element != NULL; elemPtr = &(element->next), element = *elemPtr) { andre@0: andre@0: if (element->hashCode != hashCode){ andre@0: /* no possibility of a match */ andre@0: continue; andre@0: } andre@0: andre@0: if (keyComp == NULL){ andre@0: PKIX_CHECK(pkix_pl_KeyComparator_Default andre@0: ((PKIX_UInt32 *)key, andre@0: (PKIX_UInt32 *)(element->key), andre@0: &compResult, andre@0: plContext), andre@0: PKIX_COULDNOTTESTWHETHERKEYSEQUAL); andre@0: } else { andre@0: PKIX_CHECK(keyComp andre@0: ((PKIX_PL_Object *)key, andre@0: (PKIX_PL_Object *)(element->key), andre@0: &compResult, andre@0: plContext), andre@0: PKIX_COULDNOTTESTWHETHERKEYSEQUAL); andre@0: } andre@0: andre@0: if ((element->hashCode == hashCode) && andre@0: (compResult == PKIX_TRUE)){ andre@0: /* Same key already exists in the table */ andre@0: PKIX_ERROR(PKIX_ATTEMPTTOADDDUPLICATEKEY); andre@0: } andre@0: } andre@0: andre@0: /* Next Element should be NULL at this point */ andre@0: if (element != NULL) { andre@0: PKIX_ERROR(PKIX_ERRORTRAVERSINGBUCKET); andre@0: } andre@0: andre@0: /* Create a new HT_Elem */ andre@0: PKIX_CHECK(PKIX_PL_Malloc andre@0: (sizeof (pkix_pl_HT_Elem), (void **)elemPtr, plContext), andre@0: PKIX_MALLOCFAILED); andre@0: andre@0: element = *elemPtr; andre@0: andre@0: element->key = key; andre@0: element->value = value; andre@0: element->hashCode = hashCode; andre@0: element->next = NULL; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_PrimHashTable_Remove andre@0: * DESCRIPTION: andre@0: * andre@0: * Removes any objects with the key pointed to by "key" and hashCode value andre@0: * equal to "hashCode" from the PrimHashtable pointed to by "ht", using the andre@0: * function pointed to by "keyComp" to compare keys, and stores the object's andre@0: * value at "pResult". Assumes "key" is a PKIX_UInt32 or a PKIX_PL_Object. andre@0: * This function sets "pResult" to NULL if the key is not in the hashtable. andre@0: * andre@0: * PARAMETERS: andre@0: * "ht" andre@0: * Address of PrimHashtable to remove object. Must be non-NULL. andre@0: * "key" andre@0: * Address of key for lookup. Typically a PKIX_UInt32 or PKIX_PL_Object. andre@0: * Must be non-NULL. andre@0: * "value" andre@0: * Address of Object to be added to PrimHashtable. Must be non-NULL. andre@0: * "hashCode" andre@0: * Hashcode value of the key. andre@0: * "keyComp" andre@0: * Address of function used to determine if two keys are equal. andre@0: * If NULL, pkix_pl_KeyComparator_Default is used. andre@0: * "pResult" andre@0: * Address where value will be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Not Thread Safe - assumes exclusive access to "ht" andre@0: * (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a HashTable Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_PrimHashTable_Remove( andre@0: pkix_pl_PrimHashTable *ht, andre@0: void *key, andre@0: PKIX_UInt32 hashCode, andre@0: PKIX_PL_EqualsCallback keyComp, andre@0: void **pKey, andre@0: void **pValue, andre@0: void *plContext) andre@0: { andre@0: pkix_pl_HT_Elem *element = NULL; andre@0: pkix_pl_HT_Elem *prior = NULL; andre@0: PKIX_Boolean compResult; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "pkix_pl_PrimHashTable_Remove"); andre@0: PKIX_NULLCHECK_FOUR(ht, key, pKey, pValue); andre@0: andre@0: *pKey = NULL; andre@0: *pValue = NULL; andre@0: andre@0: for (element = ht->buckets[hashCode%ht->size], prior = element; andre@0: (element != NULL); andre@0: prior = element, element = element->next) { andre@0: andre@0: if (element->hashCode != hashCode){ andre@0: /* no possibility of a match */ andre@0: continue; andre@0: } andre@0: andre@0: if (keyComp == NULL){ andre@0: PKIX_CHECK(pkix_pl_KeyComparator_Default andre@0: ((PKIX_UInt32 *)key, andre@0: (PKIX_UInt32 *)(element->key), andre@0: &compResult, andre@0: plContext), andre@0: PKIX_COULDNOTTESTWHETHERKEYSEQUAL); andre@0: } else { andre@0: PKIX_CHECK(keyComp andre@0: ((PKIX_PL_Object *)key, andre@0: (PKIX_PL_Object *)(element->key), andre@0: &compResult, andre@0: plContext), andre@0: PKIX_COULDNOTTESTWHETHERKEYSEQUAL); andre@0: } andre@0: andre@0: if ((element->hashCode == hashCode) && andre@0: (compResult == PKIX_TRUE)){ andre@0: if (element != prior) { andre@0: prior->next = element->next; andre@0: } else { andre@0: ht->buckets[hashCode%ht->size] = element->next; andre@0: } andre@0: *pKey = element->key; andre@0: *pValue = element->value; andre@0: element->key = NULL; andre@0: element->value = NULL; andre@0: element->next = NULL; andre@0: PKIX_FREE(element); andre@0: goto cleanup; andre@0: } andre@0: } andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_HashTableLookup andre@0: * DESCRIPTION: andre@0: * andre@0: * Looks up object using the key pointed to by "key" and hashCode value andre@0: * equal to "hashCode" from the PrimHashtable pointed to by "ht", using the andre@0: * function pointed to by "keyComp" to compare keys, and stores the object's andre@0: * value at "pResult". Assumes "key" is a PKIX_UInt32 or a PKIX_PL_Object. andre@0: * This function sets "pResult" to NULL if the key is not in the hashtable. andre@0: * andre@0: * PARAMETERS: andre@0: * "ht" andre@0: * Address of PrimHashtable to lookup object from. Must be non-NULL. andre@0: * "key" andre@0: * Address of key for lookup. Typically a PKIX_UInt32 or PKIX_PL_Object. andre@0: * Must be non-NULL. andre@0: * "keyComp" andre@0: * Address of function used to determine if two keys are equal. andre@0: * If NULL, pkix_pl_KeyComparator_Default is used. andre@0: * "hashCode" andre@0: * Hashcode value of the key. andre@0: * "pResult" andre@0: * Address where value will be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Conditionally Thread Safe andre@0: * (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_PrimHashTable_Lookup( andre@0: pkix_pl_PrimHashTable *ht, andre@0: void *key, andre@0: PKIX_UInt32 hashCode, andre@0: PKIX_PL_EqualsCallback keyComp, andre@0: void **pResult, andre@0: void *plContext) andre@0: { andre@0: pkix_pl_HT_Elem *element = NULL; andre@0: PKIX_Boolean compResult = PKIX_FALSE; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "pkix_pl_PrimHashTable_Lookup"); andre@0: PKIX_NULLCHECK_THREE(ht, key, pResult); andre@0: andre@0: *pResult = NULL; andre@0: andre@0: for (element = (ht->buckets)[hashCode%ht->size]; andre@0: (element != NULL) && (*pResult == NULL); andre@0: element = element->next) { andre@0: andre@0: if (element->hashCode != hashCode){ andre@0: /* no possibility of a match */ andre@0: continue; andre@0: } andre@0: andre@0: if (keyComp == NULL){ andre@0: PKIX_CHECK(pkix_pl_KeyComparator_Default andre@0: ((PKIX_UInt32 *)key, andre@0: (PKIX_UInt32 *)(element->key), andre@0: &compResult, andre@0: plContext), andre@0: PKIX_COULDNOTTESTWHETHERKEYSEQUAL); andre@0: } else { andre@0: pkixErrorResult = andre@0: keyComp((PKIX_PL_Object *)key, andre@0: (PKIX_PL_Object *)(element->key), andre@0: &compResult, andre@0: plContext); andre@0: if (pkixErrorResult) { andre@0: pkixErrorClass = PKIX_FATAL_ERROR; andre@0: pkixErrorCode = PKIX_COULDNOTTESTWHETHERKEYSEQUAL; andre@0: goto cleanup; andre@0: } andre@0: } andre@0: andre@0: if ((element->hashCode == hashCode) && andre@0: (compResult == PKIX_TRUE)){ andre@0: *pResult = element->value; andre@0: goto cleanup; andre@0: } andre@0: } andre@0: andre@0: /* if we've reached here, specified key doesn't exist in hashtable */ andre@0: *pResult = NULL; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_PrimHashTable_Destroy andre@0: * andre@0: * Destroys PrimHashTable pointed to by "ht". andre@0: * andre@0: * PARAMETERS: andre@0: * "ht" andre@0: * Address of PrimHashtable to free. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Not Thread Safe - assumes exclusive access to "ht" andre@0: * (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a HashTable Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_PrimHashTable_Destroy( andre@0: pkix_pl_PrimHashTable *ht, andre@0: void *plContext) andre@0: { andre@0: pkix_pl_HT_Elem *element = NULL; andre@0: pkix_pl_HT_Elem *temp = NULL; andre@0: PKIX_UInt32 i; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "pkix_pl_PrimHashTable_Destroy"); andre@0: PKIX_NULLCHECK_ONE(ht); andre@0: andre@0: /* Free each element (list) */ andre@0: for (i = 0; i < ht->size; i++) { andre@0: for (element = ht->buckets[i]; andre@0: element != NULL; andre@0: element = temp) { andre@0: temp = element->next; andre@0: element->value = NULL; andre@0: element->key = NULL; andre@0: element->hashCode = 0; andre@0: element->next = NULL; andre@0: PKIX_FREE(element); andre@0: } andre@0: } andre@0: andre@0: /* Free the pointer to the list array */ andre@0: PKIX_FREE(ht->buckets); andre@0: ht->size = 0; andre@0: andre@0: /* Free the table itself */ andre@0: PKIX_FREE(ht); andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_PrimHashTable_GetBucketSize andre@0: * DESCRIPTION: andre@0: * andre@0: * Retruns number of entries in the bucket the "hashCode" is designated in andre@0: * the hashtable "ht" in the address "pBucketSize". andre@0: * andre@0: * PARAMETERS: andre@0: * "ht" andre@0: * Address of PrimHashtable to get entries count. Must be non-NULL. andre@0: * "hashCode" andre@0: * Hashcode value of the key. andre@0: * "pBucketSize" andre@0: * Address that an PKIX_UInt32 is returned for number of entries in the andre@0: * bucket associated with the hashCode. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Not Thread Safe - assumes exclusive access to "ht" andre@0: * (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a HashTable Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_PrimHashTable_GetBucketSize( andre@0: pkix_pl_PrimHashTable *ht, andre@0: PKIX_UInt32 hashCode, andre@0: PKIX_UInt32 *pBucketSize, andre@0: void *plContext) andre@0: { andre@0: pkix_pl_HT_Elem **elemPtr = NULL; andre@0: pkix_pl_HT_Elem *element = NULL; andre@0: PKIX_UInt32 bucketSize = 0; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "pkix_pl_PrimHashTable_GetBucketSize"); andre@0: PKIX_NULLCHECK_TWO(ht, pBucketSize); andre@0: andre@0: for (elemPtr = &((ht->buckets)[hashCode%ht->size]), element = *elemPtr; andre@0: element != NULL; elemPtr = &(element->next), element = *elemPtr) { andre@0: bucketSize++; andre@0: } andre@0: andre@0: *pBucketSize = bucketSize; andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_PrimHashTable_RemoveFIFO andre@0: * DESCRIPTION: andre@0: * andre@0: * Remove the first entry in the bucket the "hashCode" is designated in andre@0: * the hashtable "ht". Since new entry is added at end of the link list andre@0: * the first one is the oldest (FI) therefore removed first (FO). andre@0: * andre@0: * PARAMETERS: andre@0: * "ht" andre@0: * Address of PrimHashtable to get entries count. Must be non-NULL. andre@0: * "hashCode" andre@0: * Hashcode value of the key. andre@0: * "pKey" andre@0: * Address of key of the entry deleted. Must be non-NULL. andre@0: * "pValue" andre@0: * Address of Value of the entry deleted. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Not Thread Safe - assumes exclusive access to "ht" andre@0: * (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a HashTable Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_PrimHashTable_RemoveFIFO( andre@0: pkix_pl_PrimHashTable *ht, andre@0: PKIX_UInt32 hashCode, andre@0: void **pKey, andre@0: void **pValue, andre@0: void *plContext) andre@0: { andre@0: pkix_pl_HT_Elem *element = NULL; andre@0: andre@0: PKIX_ENTER(HASHTABLE, "pkix_pl_PrimHashTable_Remove"); andre@0: PKIX_NULLCHECK_THREE(ht, pKey, pValue); andre@0: andre@0: element = (ht->buckets)[hashCode%ht->size]; andre@0: andre@0: if (element != NULL) { andre@0: andre@0: *pKey = element->key; andre@0: *pValue = element->value; andre@0: ht->buckets[hashCode%ht->size] = element->next; andre@0: element->key = NULL; andre@0: element->value = NULL; andre@0: element->next = NULL; andre@0: PKIX_FREE(element); andre@0: } andre@0: andre@0: PKIX_RETURN(HASHTABLE); andre@0: }