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