| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| /* |
| * hash.c |
| * |
| * This is merely a couple wrappers around NSPR's PLHashTable, using |
| * the identity hash and arena-aware allocators. The reason I did |
| * this is that hash tables are used in a few places throughout the |
| * NSS Cryptoki Framework in a fairly stereotyped way, and this allows |
| * me to pull the commonalities into one place. Should we ever want |
| * to change the implementation, it's all right here. |
| */ |
| |
| #ifndef CK_T |
| #include "ck.h" |
| #endif /* CK_T */ |
| |
| /* |
| * nssCKFWHash |
| * |
| * nssCKFWHash_Create |
| * nssCKFWHash_Destroy |
| * nssCKFWHash_Add |
| * nssCKFWHash_Remove |
| * nssCKFWHash_Count |
| * nssCKFWHash_Exists |
| * nssCKFWHash_Lookup |
| * nssCKFWHash_Iterate |
| */ |
| |
| struct nssCKFWHashStr { |
| NSSCKFWMutex *mutex; |
| |
| /* |
| * The invariant that mutex protects is: |
| * The count accurately reflects the hashtable state. |
| */ |
| |
| PLHashTable *plHashTable; |
| CK_ULONG count; |
| }; |
| |
| static PLHashNumber |
| nss_ckfw_identity_hash( |
| const void *key) |
| { |
| return (PLHashNumber)((char *)key - (char *)NULL); |
| } |
| |
| /* |
| * nssCKFWHash_Create |
| * |
| */ |
| NSS_IMPLEMENT nssCKFWHash * |
| nssCKFWHash_Create( |
| NSSCKFWInstance *fwInstance, |
| NSSArena *arena, |
| CK_RV *pError) |
| { |
| nssCKFWHash *rv; |
| |
| #ifdef NSSDEBUG |
| if (!pError) { |
| return (nssCKFWHash *)NULL; |
| } |
| |
| if (PR_SUCCESS != nssArena_verifyPointer(arena)) { |
| *pError = CKR_ARGUMENTS_BAD; |
| return (nssCKFWHash *)NULL; |
| } |
| #endif /* NSSDEBUG */ |
| |
| rv = nss_ZNEW(arena, nssCKFWHash); |
| if (!rv) { |
| *pError = CKR_HOST_MEMORY; |
| return (nssCKFWHash *)NULL; |
| } |
| |
| rv->mutex = nssCKFWInstance_CreateMutex(fwInstance, arena, pError); |
| if (!rv->mutex) { |
| if (CKR_OK == *pError) { |
| *pError = CKR_GENERAL_ERROR; |
| } |
| (void)nss_ZFreeIf(rv); |
| return (nssCKFWHash *)NULL; |
| } |
| |
| rv->plHashTable = PL_NewHashTable(0, nss_ckfw_identity_hash, |
| PL_CompareValues, PL_CompareValues, &nssArenaHashAllocOps, arena); |
| if (!rv->plHashTable) { |
| (void)nssCKFWMutex_Destroy(rv->mutex); |
| (void)nss_ZFreeIf(rv); |
| *pError = CKR_HOST_MEMORY; |
| return (nssCKFWHash *)NULL; |
| } |
| |
| rv->count = 0; |
| |
| return rv; |
| } |
| |
| /* |
| * nssCKFWHash_Destroy |
| * |
| */ |
| NSS_IMPLEMENT void |
| nssCKFWHash_Destroy( |
| nssCKFWHash *hash) |
| { |
| (void)nssCKFWMutex_Destroy(hash->mutex); |
| PL_HashTableDestroy(hash->plHashTable); |
| (void)nss_ZFreeIf(hash); |
| } |
| |
| /* |
| * nssCKFWHash_Add |
| * |
| */ |
| NSS_IMPLEMENT CK_RV |
| nssCKFWHash_Add( |
| nssCKFWHash *hash, |
| const void *key, |
| const void *value) |
| { |
| CK_RV error = CKR_OK; |
| PLHashEntry *he; |
| |
| error = nssCKFWMutex_Lock(hash->mutex); |
| if (CKR_OK != error) { |
| return error; |
| } |
| |
| he = PL_HashTableAdd(hash->plHashTable, key, (void *)value); |
| if (!he) { |
| error = CKR_HOST_MEMORY; |
| } else { |
| hash->count++; |
| } |
| |
| (void)nssCKFWMutex_Unlock(hash->mutex); |
| |
| return error; |
| } |
| |
| /* |
| * nssCKFWHash_Remove |
| * |
| */ |
| NSS_IMPLEMENT void |
| nssCKFWHash_Remove( |
| nssCKFWHash *hash, |
| const void *it) |
| { |
| PRBool found; |
| |
| if (CKR_OK != nssCKFWMutex_Lock(hash->mutex)) { |
| return; |
| } |
| |
| found = PL_HashTableRemove(hash->plHashTable, it); |
| if (found) { |
| hash->count--; |
| } |
| |
| (void)nssCKFWMutex_Unlock(hash->mutex); |
| return; |
| } |
| |
| /* |
| * nssCKFWHash_Count |
| * |
| */ |
| NSS_IMPLEMENT CK_ULONG |
| nssCKFWHash_Count( |
| nssCKFWHash *hash) |
| { |
| CK_ULONG count; |
| |
| if (CKR_OK != nssCKFWMutex_Lock(hash->mutex)) { |
| return (CK_ULONG)0; |
| } |
| |
| count = hash->count; |
| |
| (void)nssCKFWMutex_Unlock(hash->mutex); |
| |
| return count; |
| } |
| |
| /* |
| * nssCKFWHash_Exists |
| * |
| */ |
| NSS_IMPLEMENT CK_BBOOL |
| nssCKFWHash_Exists( |
| nssCKFWHash *hash, |
| const void *it) |
| { |
| void *value; |
| |
| if (CKR_OK != nssCKFWMutex_Lock(hash->mutex)) { |
| return CK_FALSE; |
| } |
| |
| value = PL_HashTableLookup(hash->plHashTable, it); |
| |
| (void)nssCKFWMutex_Unlock(hash->mutex); |
| |
| if (!value) { |
| return CK_FALSE; |
| } else { |
| return CK_TRUE; |
| } |
| } |
| |
| /* |
| * nssCKFWHash_Lookup |
| * |
| */ |
| NSS_IMPLEMENT void * |
| nssCKFWHash_Lookup( |
| nssCKFWHash *hash, |
| const void *it) |
| { |
| void *rv; |
| |
| if (CKR_OK != nssCKFWMutex_Lock(hash->mutex)) { |
| return (void *)NULL; |
| } |
| |
| rv = PL_HashTableLookup(hash->plHashTable, it); |
| |
| (void)nssCKFWMutex_Unlock(hash->mutex); |
| |
| return rv; |
| } |
| |
| struct arg_str { |
| nssCKFWHashIterator fcn; |
| void *closure; |
| }; |
| |
| static PRIntn |
| nss_ckfwhash_enumerator( |
| PLHashEntry *he, |
| PRIntn index, |
| void *arg) |
| { |
| struct arg_str *as = (struct arg_str *)arg; |
| as->fcn(he->key, he->value, as->closure); |
| return HT_ENUMERATE_NEXT; |
| } |
| |
| /* |
| * nssCKFWHash_Iterate |
| * |
| * NOTE that the iteration function will be called with the hashtable locked. |
| */ |
| NSS_IMPLEMENT void |
| nssCKFWHash_Iterate( |
| nssCKFWHash *hash, |
| nssCKFWHashIterator fcn, |
| void *closure) |
| { |
| struct arg_str as; |
| as.fcn = fcn; |
| as.closure = closure; |
| |
| if (CKR_OK != nssCKFWMutex_Lock(hash->mutex)) { |
| return; |
| } |
| |
| PL_HashTableEnumerateEntries(hash->plHashTable, nss_ckfwhash_enumerator, &as); |
| |
| (void)nssCKFWMutex_Unlock(hash->mutex); |
| |
| return; |
| } |