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: /* andre@0: * hash.c andre@0: * andre@0: * This is merely a couple wrappers around NSPR's PLHashTable, using andre@0: * the identity hash and arena-aware allocators. andre@0: * This is a copy of ckfw/hash.c, with modifications to use NSS types andre@0: * (not Cryptoki types). Would like for this to be a single implementation, andre@0: * but doesn't seem like it will work. andre@0: */ andre@0: andre@0: #ifndef BASE_H andre@0: #include "base.h" andre@0: #endif /* BASE_H */ andre@0: andre@0: #include "prbit.h" andre@0: andre@0: /* andre@0: * nssHash andre@0: * andre@0: * nssHash_Create andre@0: * nssHash_Destroy andre@0: * nssHash_Add andre@0: * nssHash_Remove andre@0: * nssHash_Count andre@0: * nssHash_Exists andre@0: * nssHash_Lookup andre@0: * nssHash_Iterate andre@0: */ andre@0: andre@0: struct nssHashStr { andre@0: NSSArena *arena; andre@0: PRBool i_alloced_arena; andre@0: PRLock *mutex; andre@0: andre@0: /* andre@0: * The invariant that mutex protects is: andre@0: * The count accurately reflects the hashtable state. andre@0: */ andre@0: andre@0: PLHashTable *plHashTable; andre@0: PRUint32 count; andre@0: }; andre@0: andre@0: static PLHashNumber andre@0: nss_identity_hash andre@0: ( andre@0: const void *key andre@0: ) andre@0: { andre@0: PRUint32 i = (PRUint32)key; andre@0: PR_ASSERT(sizeof(PLHashNumber) == sizeof(PRUint32)); andre@0: return (PLHashNumber)i; andre@0: } andre@0: andre@0: static PLHashNumber andre@0: nss_item_hash andre@0: ( andre@0: const void *key andre@0: ) andre@0: { andre@0: unsigned int i; andre@0: PLHashNumber h; andre@0: NSSItem *it = (NSSItem *)key; andre@0: h = 0; andre@0: for (i=0; isize; i++) andre@0: h = PR_ROTATE_LEFT32(h, 4) ^ ((unsigned char *)it->data)[i]; andre@0: return h; andre@0: } andre@0: andre@0: static int andre@0: nss_compare_items(const void *v1, const void *v2) andre@0: { andre@0: PRStatus ignore; andre@0: return (int)nssItem_Equal((NSSItem *)v1, (NSSItem *)v2, &ignore); andre@0: } andre@0: andre@0: /* andre@0: * nssHash_create andre@0: * andre@0: */ andre@0: NSS_IMPLEMENT nssHash * andre@0: nssHash_Create andre@0: ( andre@0: NSSArena *arenaOpt, andre@0: PRUint32 numBuckets, andre@0: PLHashFunction keyHash, andre@0: PLHashComparator keyCompare, andre@0: PLHashComparator valueCompare andre@0: ) andre@0: { andre@0: nssHash *rv; andre@0: NSSArena *arena; andre@0: PRBool i_alloced; andre@0: andre@0: #ifdef NSSDEBUG andre@0: if( arenaOpt && PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { andre@0: nss_SetError(NSS_ERROR_INVALID_POINTER); andre@0: return (nssHash *)NULL; andre@0: } andre@0: #endif /* NSSDEBUG */ andre@0: andre@0: if (arenaOpt) { andre@0: arena = arenaOpt; andre@0: i_alloced = PR_FALSE; andre@0: } else { andre@0: arena = nssArena_Create(); andre@0: i_alloced = PR_TRUE; andre@0: } andre@0: andre@0: rv = nss_ZNEW(arena, nssHash); andre@0: if( (nssHash *)NULL == rv ) { andre@0: goto loser; andre@0: } andre@0: andre@0: rv->mutex = PZ_NewLock(nssILockOther); andre@0: if( (PZLock *)NULL == rv->mutex ) { andre@0: goto loser; andre@0: } andre@0: andre@0: rv->plHashTable = PL_NewHashTable(numBuckets, andre@0: keyHash, keyCompare, valueCompare, andre@0: &nssArenaHashAllocOps, arena); andre@0: if( (PLHashTable *)NULL == rv->plHashTable ) { andre@0: (void)PZ_DestroyLock(rv->mutex); andre@0: goto loser; andre@0: } andre@0: andre@0: rv->count = 0; andre@0: rv->arena = arena; andre@0: rv->i_alloced_arena = i_alloced; andre@0: andre@0: return rv; andre@0: loser: andre@0: (void)nss_ZFreeIf(rv); andre@0: return (nssHash *)NULL; andre@0: } andre@0: andre@0: /* andre@0: * nssHash_CreatePointer andre@0: * andre@0: */ andre@0: NSS_IMPLEMENT nssHash * andre@0: nssHash_CreatePointer andre@0: ( andre@0: NSSArena *arenaOpt, andre@0: PRUint32 numBuckets andre@0: ) andre@0: { andre@0: return nssHash_Create(arenaOpt, numBuckets, andre@0: nss_identity_hash, PL_CompareValues, PL_CompareValues); andre@0: } andre@0: andre@0: /* andre@0: * nssHash_CreateString andre@0: * andre@0: */ andre@0: NSS_IMPLEMENT nssHash * andre@0: nssHash_CreateString andre@0: ( andre@0: NSSArena *arenaOpt, andre@0: PRUint32 numBuckets andre@0: ) andre@0: { andre@0: return nssHash_Create(arenaOpt, numBuckets, andre@0: PL_HashString, PL_CompareStrings, PL_CompareStrings); andre@0: } andre@0: andre@0: /* andre@0: * nssHash_CreateItem andre@0: * andre@0: */ andre@0: NSS_IMPLEMENT nssHash * andre@0: nssHash_CreateItem andre@0: ( andre@0: NSSArena *arenaOpt, andre@0: PRUint32 numBuckets andre@0: ) andre@0: { andre@0: return nssHash_Create(arenaOpt, numBuckets, andre@0: nss_item_hash, nss_compare_items, PL_CompareValues); andre@0: } andre@0: andre@0: /* andre@0: * nssHash_Destroy andre@0: * andre@0: */ andre@0: NSS_IMPLEMENT void andre@0: nssHash_Destroy andre@0: ( andre@0: nssHash *hash andre@0: ) andre@0: { andre@0: (void)PZ_DestroyLock(hash->mutex); andre@0: PL_HashTableDestroy(hash->plHashTable); andre@0: if (hash->i_alloced_arena) { andre@0: nssArena_Destroy(hash->arena); andre@0: } else { andre@0: nss_ZFreeIf(hash); andre@0: } andre@0: } andre@0: andre@0: /* andre@0: * nssHash_Add andre@0: * andre@0: */ andre@0: NSS_IMPLEMENT PRStatus andre@0: nssHash_Add andre@0: ( andre@0: nssHash *hash, andre@0: const void *key, andre@0: const void *value andre@0: ) andre@0: { andre@0: PRStatus error = PR_FAILURE; andre@0: PLHashEntry *he; andre@0: andre@0: PZ_Lock(hash->mutex); andre@0: andre@0: he = PL_HashTableAdd(hash->plHashTable, key, (void *)value); andre@0: if( (PLHashEntry *)NULL == he ) { andre@0: nss_SetError(NSS_ERROR_NO_MEMORY); andre@0: } else if (he->value != value) { andre@0: nss_SetError(NSS_ERROR_HASH_COLLISION); andre@0: } else { andre@0: hash->count++; andre@0: error = PR_SUCCESS; andre@0: } andre@0: andre@0: (void)PZ_Unlock(hash->mutex); andre@0: andre@0: return error; andre@0: } andre@0: andre@0: /* andre@0: * nssHash_Remove andre@0: * andre@0: */ andre@0: NSS_IMPLEMENT void andre@0: nssHash_Remove andre@0: ( andre@0: nssHash *hash, andre@0: const void *it andre@0: ) andre@0: { andre@0: PRBool found; andre@0: andre@0: PZ_Lock(hash->mutex); andre@0: andre@0: found = PL_HashTableRemove(hash->plHashTable, it); andre@0: if( found ) { andre@0: hash->count--; andre@0: } andre@0: andre@0: (void)PZ_Unlock(hash->mutex); andre@0: return; andre@0: } andre@0: andre@0: /* andre@0: * nssHash_Count andre@0: * andre@0: */ andre@0: NSS_IMPLEMENT PRUint32 andre@0: nssHash_Count andre@0: ( andre@0: nssHash *hash andre@0: ) andre@0: { andre@0: PRUint32 count; andre@0: andre@0: PZ_Lock(hash->mutex); andre@0: andre@0: count = hash->count; andre@0: andre@0: (void)PZ_Unlock(hash->mutex); andre@0: andre@0: return count; andre@0: } andre@0: andre@0: /* andre@0: * nssHash_Exists andre@0: * andre@0: */ andre@0: NSS_IMPLEMENT PRBool andre@0: nssHash_Exists andre@0: ( andre@0: nssHash *hash, andre@0: const void *it andre@0: ) andre@0: { andre@0: void *value; andre@0: andre@0: PZ_Lock(hash->mutex); andre@0: andre@0: value = PL_HashTableLookup(hash->plHashTable, it); andre@0: andre@0: (void)PZ_Unlock(hash->mutex); andre@0: andre@0: if( (void *)NULL == value ) { andre@0: return PR_FALSE; andre@0: } else { andre@0: return PR_TRUE; andre@0: } andre@0: } andre@0: andre@0: /* andre@0: * nssHash_Lookup andre@0: * andre@0: */ andre@0: NSS_IMPLEMENT void * andre@0: nssHash_Lookup andre@0: ( andre@0: nssHash *hash, andre@0: const void *it andre@0: ) andre@0: { andre@0: void *rv; andre@0: andre@0: PZ_Lock(hash->mutex); andre@0: andre@0: rv = PL_HashTableLookup(hash->plHashTable, it); andre@0: andre@0: (void)PZ_Unlock(hash->mutex); andre@0: andre@0: return rv; andre@0: } andre@0: andre@0: struct arg_str { andre@0: nssHashIterator fcn; andre@0: void *closure; andre@0: }; andre@0: andre@0: static PRIntn andre@0: nss_hash_enumerator andre@0: ( andre@0: PLHashEntry *he, andre@0: PRIntn index, andre@0: void *arg andre@0: ) andre@0: { andre@0: struct arg_str *as = (struct arg_str *)arg; andre@0: as->fcn(he->key, he->value, as->closure); andre@0: return HT_ENUMERATE_NEXT; andre@0: } andre@0: andre@0: /* andre@0: * nssHash_Iterate andre@0: * andre@0: * NOTE that the iteration function will be called with the hashtable locked. andre@0: */ andre@0: NSS_IMPLEMENT void andre@0: nssHash_Iterate andre@0: ( andre@0: nssHash *hash, andre@0: nssHashIterator fcn, andre@0: void *closure andre@0: ) andre@0: { andre@0: struct arg_str as; andre@0: as.fcn = fcn; andre@0: as.closure = closure; andre@0: andre@0: PZ_Lock(hash->mutex); andre@0: andre@0: PL_HashTableEnumerateEntries(hash->plHashTable, nss_hash_enumerator, &as); andre@0: andre@0: (void)PZ_Unlock(hash->mutex); andre@0: andre@0: return; andre@0: }