1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/lib/ds/plhash.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,483 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * PL hash table package. 1.11 + */ 1.12 +#include "plhash.h" 1.13 +#include "prbit.h" 1.14 +#include "prlog.h" 1.15 +#include "prmem.h" 1.16 +#include "prtypes.h" 1.17 +#include <stdlib.h> 1.18 +#include <string.h> 1.19 + 1.20 +/* Compute the number of buckets in ht */ 1.21 +#define NBUCKETS(ht) (1 << (PL_HASH_BITS - (ht)->shift)) 1.22 + 1.23 +/* The smallest table has 16 buckets */ 1.24 +#define MINBUCKETSLOG2 4 1.25 +#define MINBUCKETS (1 << MINBUCKETSLOG2) 1.26 + 1.27 +/* Compute the maximum entries given n buckets that we will tolerate, ~90% */ 1.28 +#define OVERLOADED(n) ((n) - ((n) >> 3)) 1.29 + 1.30 +/* Compute the number of entries below which we shrink the table by half */ 1.31 +#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0) 1.32 + 1.33 +/* 1.34 +** Stubs for default hash allocator ops. 1.35 +*/ 1.36 +static void * PR_CALLBACK 1.37 +DefaultAllocTable(void *pool, PRSize size) 1.38 +{ 1.39 + return PR_MALLOC(size); 1.40 +} 1.41 + 1.42 +static void PR_CALLBACK 1.43 +DefaultFreeTable(void *pool, void *item) 1.44 +{ 1.45 + PR_Free(item); 1.46 +} 1.47 + 1.48 +static PLHashEntry * PR_CALLBACK 1.49 +DefaultAllocEntry(void *pool, const void *key) 1.50 +{ 1.51 + return PR_NEW(PLHashEntry); 1.52 +} 1.53 + 1.54 +static void PR_CALLBACK 1.55 +DefaultFreeEntry(void *pool, PLHashEntry *he, PRUintn flag) 1.56 +{ 1.57 + if (flag == HT_FREE_ENTRY) 1.58 + PR_Free(he); 1.59 +} 1.60 + 1.61 +static PLHashAllocOps defaultHashAllocOps = { 1.62 + DefaultAllocTable, DefaultFreeTable, 1.63 + DefaultAllocEntry, DefaultFreeEntry 1.64 +}; 1.65 + 1.66 +PR_IMPLEMENT(PLHashTable *) 1.67 +PL_NewHashTable(PRUint32 n, PLHashFunction keyHash, 1.68 + PLHashComparator keyCompare, PLHashComparator valueCompare, 1.69 + const PLHashAllocOps *allocOps, void *allocPriv) 1.70 +{ 1.71 + PLHashTable *ht; 1.72 + PRSize nb; 1.73 + 1.74 + if (n <= MINBUCKETS) { 1.75 + n = MINBUCKETSLOG2; 1.76 + } else { 1.77 + n = PR_CeilingLog2(n); 1.78 + if ((PRInt32)n < 0) 1.79 + return 0; 1.80 + } 1.81 + 1.82 + if (!allocOps) allocOps = &defaultHashAllocOps; 1.83 + 1.84 + ht = (PLHashTable*)((*allocOps->allocTable)(allocPriv, sizeof *ht)); 1.85 + if (!ht) 1.86 + return 0; 1.87 + memset(ht, 0, sizeof *ht); 1.88 + ht->shift = PL_HASH_BITS - n; 1.89 + n = 1 << n; 1.90 + nb = n * sizeof(PLHashEntry *); 1.91 + ht->buckets = (PLHashEntry**)((*allocOps->allocTable)(allocPriv, nb)); 1.92 + if (!ht->buckets) { 1.93 + (*allocOps->freeTable)(allocPriv, ht); 1.94 + return 0; 1.95 + } 1.96 + memset(ht->buckets, 0, nb); 1.97 + 1.98 + ht->keyHash = keyHash; 1.99 + ht->keyCompare = keyCompare; 1.100 + ht->valueCompare = valueCompare; 1.101 + ht->allocOps = allocOps; 1.102 + ht->allocPriv = allocPriv; 1.103 + return ht; 1.104 +} 1.105 + 1.106 +PR_IMPLEMENT(void) 1.107 +PL_HashTableDestroy(PLHashTable *ht) 1.108 +{ 1.109 + PRUint32 i, n; 1.110 + PLHashEntry *he, *next; 1.111 + const PLHashAllocOps *allocOps = ht->allocOps; 1.112 + void *allocPriv = ht->allocPriv; 1.113 + 1.114 + n = NBUCKETS(ht); 1.115 + for (i = 0; i < n; i++) { 1.116 + for (he = ht->buckets[i]; he; he = next) { 1.117 + next = he->next; 1.118 + (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY); 1.119 + } 1.120 + } 1.121 +#ifdef DEBUG 1.122 + memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]); 1.123 +#endif 1.124 + (*allocOps->freeTable)(allocPriv, ht->buckets); 1.125 +#ifdef DEBUG 1.126 + memset(ht, 0xDB, sizeof *ht); 1.127 +#endif 1.128 + (*allocOps->freeTable)(allocPriv, ht); 1.129 +} 1.130 + 1.131 +/* 1.132 +** Multiplicative hash, from Knuth 6.4. 1.133 +*/ 1.134 +#define GOLDEN_RATIO 0x9E3779B9U /* 2/(1+sqrt(5))*(2^32) */ 1.135 + 1.136 +PR_IMPLEMENT(PLHashEntry **) 1.137 +PL_HashTableRawLookup(PLHashTable *ht, PLHashNumber keyHash, const void *key) 1.138 +{ 1.139 + PLHashEntry *he, **hep, **hep0; 1.140 + PLHashNumber h; 1.141 + 1.142 +#ifdef HASHMETER 1.143 + ht->nlookups++; 1.144 +#endif 1.145 + h = keyHash * GOLDEN_RATIO; 1.146 + h >>= ht->shift; 1.147 + hep = hep0 = &ht->buckets[h]; 1.148 + while ((he = *hep) != 0) { 1.149 + if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) { 1.150 + /* Move to front of chain if not already there */ 1.151 + if (hep != hep0) { 1.152 + *hep = he->next; 1.153 + he->next = *hep0; 1.154 + *hep0 = he; 1.155 + } 1.156 + return hep0; 1.157 + } 1.158 + hep = &he->next; 1.159 +#ifdef HASHMETER 1.160 + ht->nsteps++; 1.161 +#endif 1.162 + } 1.163 + return hep; 1.164 +} 1.165 + 1.166 +/* 1.167 +** Same as PL_HashTableRawLookup but doesn't reorder the hash entries. 1.168 +*/ 1.169 +PR_IMPLEMENT(PLHashEntry **) 1.170 +PL_HashTableRawLookupConst(PLHashTable *ht, PLHashNumber keyHash, 1.171 + const void *key) 1.172 +{ 1.173 + PLHashEntry *he, **hep; 1.174 + PLHashNumber h; 1.175 + 1.176 +#ifdef HASHMETER 1.177 + ht->nlookups++; 1.178 +#endif 1.179 + h = keyHash * GOLDEN_RATIO; 1.180 + h >>= ht->shift; 1.181 + hep = &ht->buckets[h]; 1.182 + while ((he = *hep) != 0) { 1.183 + if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) { 1.184 + break; 1.185 + } 1.186 + hep = &he->next; 1.187 +#ifdef HASHMETER 1.188 + ht->nsteps++; 1.189 +#endif 1.190 + } 1.191 + return hep; 1.192 +} 1.193 + 1.194 +PR_IMPLEMENT(PLHashEntry *) 1.195 +PL_HashTableRawAdd(PLHashTable *ht, PLHashEntry **hep, 1.196 + PLHashNumber keyHash, const void *key, void *value) 1.197 +{ 1.198 + PRUint32 i, n; 1.199 + PLHashEntry *he, *next, **oldbuckets; 1.200 + PRSize nb; 1.201 + 1.202 + /* Grow the table if it is overloaded */ 1.203 + n = NBUCKETS(ht); 1.204 + if (ht->nentries >= OVERLOADED(n)) { 1.205 + oldbuckets = ht->buckets; 1.206 + nb = 2 * n * sizeof(PLHashEntry *); 1.207 + ht->buckets = (PLHashEntry**) 1.208 + ((*ht->allocOps->allocTable)(ht->allocPriv, nb)); 1.209 + if (!ht->buckets) { 1.210 + ht->buckets = oldbuckets; 1.211 + return 0; 1.212 + } 1.213 + memset(ht->buckets, 0, nb); 1.214 +#ifdef HASHMETER 1.215 + ht->ngrows++; 1.216 +#endif 1.217 + ht->shift--; 1.218 + 1.219 + for (i = 0; i < n; i++) { 1.220 + for (he = oldbuckets[i]; he; he = next) { 1.221 + next = he->next; 1.222 + hep = PL_HashTableRawLookup(ht, he->keyHash, he->key); 1.223 + PR_ASSERT(*hep == 0); 1.224 + he->next = 0; 1.225 + *hep = he; 1.226 + } 1.227 + } 1.228 +#ifdef DEBUG 1.229 + memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]); 1.230 +#endif 1.231 + (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets); 1.232 + hep = PL_HashTableRawLookup(ht, keyHash, key); 1.233 + } 1.234 + 1.235 + /* Make a new key value entry */ 1.236 + he = (*ht->allocOps->allocEntry)(ht->allocPriv, key); 1.237 + if (!he) 1.238 + return 0; 1.239 + he->keyHash = keyHash; 1.240 + he->key = key; 1.241 + he->value = value; 1.242 + he->next = *hep; 1.243 + *hep = he; 1.244 + ht->nentries++; 1.245 + return he; 1.246 +} 1.247 + 1.248 +PR_IMPLEMENT(PLHashEntry *) 1.249 +PL_HashTableAdd(PLHashTable *ht, const void *key, void *value) 1.250 +{ 1.251 + PLHashNumber keyHash; 1.252 + PLHashEntry *he, **hep; 1.253 + 1.254 + keyHash = (*ht->keyHash)(key); 1.255 + hep = PL_HashTableRawLookup(ht, keyHash, key); 1.256 + if ((he = *hep) != 0) { 1.257 + /* Hit; see if values match */ 1.258 + if ((*ht->valueCompare)(he->value, value)) { 1.259 + /* key,value pair is already present in table */ 1.260 + return he; 1.261 + } 1.262 + if (he->value) 1.263 + (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE); 1.264 + he->value = value; 1.265 + return he; 1.266 + } 1.267 + return PL_HashTableRawAdd(ht, hep, keyHash, key, value); 1.268 +} 1.269 + 1.270 +PR_IMPLEMENT(void) 1.271 +PL_HashTableRawRemove(PLHashTable *ht, PLHashEntry **hep, PLHashEntry *he) 1.272 +{ 1.273 + PRUint32 i, n; 1.274 + PLHashEntry *next, **oldbuckets; 1.275 + PRSize nb; 1.276 + 1.277 + *hep = he->next; 1.278 + (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY); 1.279 + 1.280 + /* Shrink table if it's underloaded */ 1.281 + n = NBUCKETS(ht); 1.282 + if (--ht->nentries < UNDERLOADED(n)) { 1.283 + oldbuckets = ht->buckets; 1.284 + nb = n * sizeof(PLHashEntry*) / 2; 1.285 + ht->buckets = (PLHashEntry**)( 1.286 + (*ht->allocOps->allocTable)(ht->allocPriv, nb)); 1.287 + if (!ht->buckets) { 1.288 + ht->buckets = oldbuckets; 1.289 + return; 1.290 + } 1.291 + memset(ht->buckets, 0, nb); 1.292 +#ifdef HASHMETER 1.293 + ht->nshrinks++; 1.294 +#endif 1.295 + ht->shift++; 1.296 + 1.297 + for (i = 0; i < n; i++) { 1.298 + for (he = oldbuckets[i]; he; he = next) { 1.299 + next = he->next; 1.300 + hep = PL_HashTableRawLookup(ht, he->keyHash, he->key); 1.301 + PR_ASSERT(*hep == 0); 1.302 + he->next = 0; 1.303 + *hep = he; 1.304 + } 1.305 + } 1.306 +#ifdef DEBUG 1.307 + memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]); 1.308 +#endif 1.309 + (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets); 1.310 + } 1.311 +} 1.312 + 1.313 +PR_IMPLEMENT(PRBool) 1.314 +PL_HashTableRemove(PLHashTable *ht, const void *key) 1.315 +{ 1.316 + PLHashNumber keyHash; 1.317 + PLHashEntry *he, **hep; 1.318 + 1.319 + keyHash = (*ht->keyHash)(key); 1.320 + hep = PL_HashTableRawLookup(ht, keyHash, key); 1.321 + if ((he = *hep) == 0) 1.322 + return PR_FALSE; 1.323 + 1.324 + /* Hit; remove element */ 1.325 + PL_HashTableRawRemove(ht, hep, he); 1.326 + return PR_TRUE; 1.327 +} 1.328 + 1.329 +PR_IMPLEMENT(void *) 1.330 +PL_HashTableLookup(PLHashTable *ht, const void *key) 1.331 +{ 1.332 + PLHashNumber keyHash; 1.333 + PLHashEntry *he, **hep; 1.334 + 1.335 + keyHash = (*ht->keyHash)(key); 1.336 + hep = PL_HashTableRawLookup(ht, keyHash, key); 1.337 + if ((he = *hep) != 0) { 1.338 + return he->value; 1.339 + } 1.340 + return 0; 1.341 +} 1.342 + 1.343 +/* 1.344 +** Same as PL_HashTableLookup but doesn't reorder the hash entries. 1.345 +*/ 1.346 +PR_IMPLEMENT(void *) 1.347 +PL_HashTableLookupConst(PLHashTable *ht, const void *key) 1.348 +{ 1.349 + PLHashNumber keyHash; 1.350 + PLHashEntry *he, **hep; 1.351 + 1.352 + keyHash = (*ht->keyHash)(key); 1.353 + hep = PL_HashTableRawLookupConst(ht, keyHash, key); 1.354 + if ((he = *hep) != 0) { 1.355 + return he->value; 1.356 + } 1.357 + return 0; 1.358 +} 1.359 + 1.360 +/* 1.361 +** Iterate over the entries in the hash table calling func for each 1.362 +** entry found. Stop if "f" says to (return value & PR_ENUMERATE_STOP). 1.363 +** Return a count of the number of elements scanned. 1.364 +*/ 1.365 +PR_IMPLEMENT(int) 1.366 +PL_HashTableEnumerateEntries(PLHashTable *ht, PLHashEnumerator f, void *arg) 1.367 +{ 1.368 + PLHashEntry *he, **hep; 1.369 + PRUint32 i, nbuckets; 1.370 + int rv, n = 0; 1.371 + PLHashEntry *todo = 0; 1.372 + 1.373 + nbuckets = NBUCKETS(ht); 1.374 + for (i = 0; i < nbuckets; i++) { 1.375 + hep = &ht->buckets[i]; 1.376 + while ((he = *hep) != 0) { 1.377 + rv = (*f)(he, n, arg); 1.378 + n++; 1.379 + if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) { 1.380 + *hep = he->next; 1.381 + if (rv & HT_ENUMERATE_REMOVE) { 1.382 + he->next = todo; 1.383 + todo = he; 1.384 + } 1.385 + } else { 1.386 + hep = &he->next; 1.387 + } 1.388 + if (rv & HT_ENUMERATE_STOP) { 1.389 + goto out; 1.390 + } 1.391 + } 1.392 + } 1.393 + 1.394 +out: 1.395 + hep = &todo; 1.396 + while ((he = *hep) != 0) { 1.397 + PL_HashTableRawRemove(ht, hep, he); 1.398 + } 1.399 + return n; 1.400 +} 1.401 + 1.402 +#ifdef HASHMETER 1.403 +#include <math.h> 1.404 +#include <stdio.h> 1.405 + 1.406 +PR_IMPLEMENT(void) 1.407 +PL_HashTableDumpMeter(PLHashTable *ht, PLHashEnumerator dump, FILE *fp) 1.408 +{ 1.409 + double mean, variance; 1.410 + PRUint32 nchains, nbuckets; 1.411 + PRUint32 i, n, maxChain, maxChainLen; 1.412 + PLHashEntry *he; 1.413 + 1.414 + variance = 0; 1.415 + nchains = 0; 1.416 + maxChainLen = 0; 1.417 + nbuckets = NBUCKETS(ht); 1.418 + for (i = 0; i < nbuckets; i++) { 1.419 + he = ht->buckets[i]; 1.420 + if (!he) 1.421 + continue; 1.422 + nchains++; 1.423 + for (n = 0; he; he = he->next) 1.424 + n++; 1.425 + variance += n * n; 1.426 + if (n > maxChainLen) { 1.427 + maxChainLen = n; 1.428 + maxChain = i; 1.429 + } 1.430 + } 1.431 + mean = (double)ht->nentries / nchains; 1.432 + variance = fabs(variance / nchains - mean * mean); 1.433 + 1.434 + fprintf(fp, "\nHash table statistics:\n"); 1.435 + fprintf(fp, " number of lookups: %u\n", ht->nlookups); 1.436 + fprintf(fp, " number of entries: %u\n", ht->nentries); 1.437 + fprintf(fp, " number of grows: %u\n", ht->ngrows); 1.438 + fprintf(fp, " number of shrinks: %u\n", ht->nshrinks); 1.439 + fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps 1.440 + / ht->nlookups); 1.441 + fprintf(fp, "mean hash chain length: %g\n", mean); 1.442 + fprintf(fp, " standard deviation: %g\n", sqrt(variance)); 1.443 + fprintf(fp, " max hash chain length: %u\n", maxChainLen); 1.444 + fprintf(fp, " max hash chain: [%u]\n", maxChain); 1.445 + 1.446 + for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++) 1.447 + if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT) 1.448 + break; 1.449 +} 1.450 +#endif /* HASHMETER */ 1.451 + 1.452 +PR_IMPLEMENT(int) 1.453 +PL_HashTableDump(PLHashTable *ht, PLHashEnumerator dump, FILE *fp) 1.454 +{ 1.455 + int count; 1.456 + 1.457 + count = PL_HashTableEnumerateEntries(ht, dump, fp); 1.458 +#ifdef HASHMETER 1.459 + PL_HashTableDumpMeter(ht, dump, fp); 1.460 +#endif 1.461 + return count; 1.462 +} 1.463 + 1.464 +PR_IMPLEMENT(PLHashNumber) 1.465 +PL_HashString(const void *key) 1.466 +{ 1.467 + PLHashNumber h; 1.468 + const PRUint8 *s; 1.469 + 1.470 + h = 0; 1.471 + for (s = (const PRUint8*)key; *s; s++) 1.472 + h = PR_ROTATE_LEFT32(h, 4) ^ *s; 1.473 + return h; 1.474 +} 1.475 + 1.476 +PR_IMPLEMENT(int) 1.477 +PL_CompareStrings(const void *v1, const void *v2) 1.478 +{ 1.479 + return strcmp((const char*)v1, (const char*)v2) == 0; 1.480 +} 1.481 + 1.482 +PR_IMPLEMENT(int) 1.483 +PL_CompareValues(const void *v1, const void *v2) 1.484 +{ 1.485 + return v1 == v2; 1.486 +}