plhash.c (11652B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* 7 * PL hash table package. 8 */ 9 #include "plhash.h" 10 #include "prbit.h" 11 #include "prlog.h" 12 #include "prmem.h" 13 #include "prtypes.h" 14 #include <stdlib.h> 15 #include <string.h> 16 17 /* Compute the number of buckets in ht */ 18 #define NBUCKETS(ht) (1 << (PL_HASH_BITS - (ht)->shift)) 19 20 /* The smallest table has 16 buckets */ 21 #define MINBUCKETSLOG2 4 22 #define MINBUCKETS (1 << MINBUCKETSLOG2) 23 24 /* Compute the maximum entries given n buckets that we will tolerate, ~90% */ 25 #define OVERLOADED(n) ((n) - ((n) >> 3)) 26 27 /* Compute the number of entries below which we shrink the table by half */ 28 #define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0) 29 30 /* 31 ** Stubs for default hash allocator ops. 32 */ 33 static void* PR_CALLBACK DefaultAllocTable(void* pool, PRSize size) { 34 return PR_MALLOC(size); 35 } 36 37 static void PR_CALLBACK DefaultFreeTable(void* pool, void* item) { 38 PR_Free(item); 39 } 40 41 static PLHashEntry* PR_CALLBACK DefaultAllocEntry(void* pool, const void* key) { 42 return PR_NEW(PLHashEntry); 43 } 44 45 static void PR_CALLBACK DefaultFreeEntry(void* pool, PLHashEntry* he, 46 PRUintn flag) { 47 if (flag == HT_FREE_ENTRY) { 48 PR_Free(he); 49 } 50 } 51 52 static PLHashAllocOps defaultHashAllocOps = { 53 DefaultAllocTable, DefaultFreeTable, DefaultAllocEntry, DefaultFreeEntry}; 54 55 PR_IMPLEMENT(PLHashTable*) 56 PL_NewHashTable(PRUint32 n, PLHashFunction keyHash, PLHashComparator keyCompare, 57 PLHashComparator valueCompare, const PLHashAllocOps* allocOps, 58 void* allocPriv) { 59 PLHashTable* ht; 60 PRSize nb; 61 62 if (n <= MINBUCKETS) { 63 n = MINBUCKETSLOG2; 64 } else { 65 n = PR_CeilingLog2(n); 66 if ((PRInt32)n < 0) { 67 return 0; 68 } 69 } 70 71 if (!allocOps) { 72 allocOps = &defaultHashAllocOps; 73 } 74 75 ht = (PLHashTable*)((*allocOps->allocTable)(allocPriv, sizeof *ht)); 76 if (!ht) { 77 return 0; 78 } 79 memset(ht, 0, sizeof *ht); 80 ht->shift = PL_HASH_BITS - n; 81 n = 1 << n; 82 nb = n * sizeof(PLHashEntry*); 83 ht->buckets = (PLHashEntry**)((*allocOps->allocTable)(allocPriv, nb)); 84 if (!ht->buckets) { 85 (*allocOps->freeTable)(allocPriv, ht); 86 return 0; 87 } 88 memset(ht->buckets, 0, nb); 89 90 ht->keyHash = keyHash; 91 ht->keyCompare = keyCompare; 92 ht->valueCompare = valueCompare; 93 ht->allocOps = allocOps; 94 ht->allocPriv = allocPriv; 95 return ht; 96 } 97 98 PR_IMPLEMENT(void) 99 PL_HashTableDestroy(PLHashTable* ht) { 100 PRUint32 i, n; 101 PLHashEntry *he, *next; 102 const PLHashAllocOps* allocOps = ht->allocOps; 103 void* allocPriv = ht->allocPriv; 104 105 n = NBUCKETS(ht); 106 for (i = 0; i < n; i++) { 107 for (he = ht->buckets[i]; he; he = next) { 108 next = he->next; 109 (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY); 110 } 111 } 112 #ifdef DEBUG 113 memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]); 114 #endif 115 (*allocOps->freeTable)(allocPriv, ht->buckets); 116 #ifdef DEBUG 117 memset(ht, 0xDB, sizeof *ht); 118 #endif 119 (*allocOps->freeTable)(allocPriv, ht); 120 } 121 122 /* 123 ** Multiplicative hash, from Knuth 6.4. 124 */ 125 #define GOLDEN_RATIO 0x9E3779B9U /* 2/(1+sqrt(5))*(2^32) */ 126 127 PR_IMPLEMENT(PLHashEntry**) 128 PL_HashTableRawLookup(PLHashTable* ht, PLHashNumber keyHash, const void* key) { 129 PLHashEntry *he, **hep, **hep0; 130 PLHashNumber h; 131 132 #ifdef HASHMETER 133 ht->nlookups++; 134 #endif 135 h = keyHash * GOLDEN_RATIO; 136 h >>= ht->shift; 137 hep = hep0 = &ht->buckets[h]; 138 while ((he = *hep) != 0) { 139 if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) { 140 /* Move to front of chain if not already there */ 141 if (hep != hep0) { 142 *hep = he->next; 143 he->next = *hep0; 144 *hep0 = he; 145 } 146 return hep0; 147 } 148 hep = &he->next; 149 #ifdef HASHMETER 150 ht->nsteps++; 151 #endif 152 } 153 return hep; 154 } 155 156 /* 157 ** Same as PL_HashTableRawLookup but doesn't reorder the hash entries. 158 */ 159 PR_IMPLEMENT(PLHashEntry**) 160 PL_HashTableRawLookupConst(PLHashTable* ht, PLHashNumber keyHash, 161 const void* key) { 162 PLHashEntry *he, **hep; 163 PLHashNumber h; 164 165 #ifdef HASHMETER 166 ht->nlookups++; 167 #endif 168 h = keyHash * GOLDEN_RATIO; 169 h >>= ht->shift; 170 hep = &ht->buckets[h]; 171 while ((he = *hep) != 0) { 172 if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) { 173 break; 174 } 175 hep = &he->next; 176 #ifdef HASHMETER 177 ht->nsteps++; 178 #endif 179 } 180 return hep; 181 } 182 183 PR_IMPLEMENT(PLHashEntry*) 184 PL_HashTableRawAdd(PLHashTable* ht, PLHashEntry** hep, PLHashNumber keyHash, 185 const void* key, void* value) { 186 PRUint32 i, n; 187 PLHashEntry *he, *next, **oldbuckets; 188 PRSize nb; 189 190 /* Grow the table if it is overloaded */ 191 n = NBUCKETS(ht); 192 if (ht->nentries >= OVERLOADED(n)) { 193 oldbuckets = ht->buckets; 194 nb = 2 * n * sizeof(PLHashEntry*); 195 ht->buckets = 196 (PLHashEntry**)((*ht->allocOps->allocTable)(ht->allocPriv, nb)); 197 if (!ht->buckets) { 198 ht->buckets = oldbuckets; 199 return 0; 200 } 201 memset(ht->buckets, 0, nb); 202 #ifdef HASHMETER 203 ht->ngrows++; 204 #endif 205 ht->shift--; 206 207 for (i = 0; i < n; i++) { 208 for (he = oldbuckets[i]; he; he = next) { 209 next = he->next; 210 hep = PL_HashTableRawLookup(ht, he->keyHash, he->key); 211 PR_ASSERT(*hep == 0); 212 he->next = 0; 213 *hep = he; 214 } 215 } 216 #ifdef DEBUG 217 memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]); 218 #endif 219 (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets); 220 hep = PL_HashTableRawLookup(ht, keyHash, key); 221 } 222 223 /* Make a new key value entry */ 224 he = (*ht->allocOps->allocEntry)(ht->allocPriv, key); 225 if (!he) { 226 return 0; 227 } 228 he->keyHash = keyHash; 229 he->key = key; 230 he->value = value; 231 he->next = *hep; 232 *hep = he; 233 ht->nentries++; 234 return he; 235 } 236 237 PR_IMPLEMENT(PLHashEntry*) 238 PL_HashTableAdd(PLHashTable* ht, const void* key, void* value) { 239 PLHashNumber keyHash; 240 PLHashEntry *he, **hep; 241 242 keyHash = (*ht->keyHash)(key); 243 hep = PL_HashTableRawLookup(ht, keyHash, key); 244 if ((he = *hep) != 0) { 245 /* Hit; see if values match */ 246 if ((*ht->valueCompare)(he->value, value)) { 247 /* key,value pair is already present in table */ 248 return he; 249 } 250 if (he->value) { 251 (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE); 252 } 253 he->value = value; 254 return he; 255 } 256 return PL_HashTableRawAdd(ht, hep, keyHash, key, value); 257 } 258 259 PR_IMPLEMENT(void) 260 PL_HashTableRawRemove(PLHashTable* ht, PLHashEntry** hep, PLHashEntry* he) { 261 PRUint32 i, n; 262 PLHashEntry *next, **oldbuckets; 263 PRSize nb; 264 265 *hep = he->next; 266 (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY); 267 268 /* Shrink table if it's underloaded */ 269 n = NBUCKETS(ht); 270 if (--ht->nentries < UNDERLOADED(n)) { 271 oldbuckets = ht->buckets; 272 nb = n * sizeof(PLHashEntry*) / 2; 273 ht->buckets = 274 (PLHashEntry**)((*ht->allocOps->allocTable)(ht->allocPriv, nb)); 275 if (!ht->buckets) { 276 ht->buckets = oldbuckets; 277 return; 278 } 279 memset(ht->buckets, 0, nb); 280 #ifdef HASHMETER 281 ht->nshrinks++; 282 #endif 283 ht->shift++; 284 285 for (i = 0; i < n; i++) { 286 for (he = oldbuckets[i]; he; he = next) { 287 next = he->next; 288 hep = PL_HashTableRawLookup(ht, he->keyHash, he->key); 289 PR_ASSERT(*hep == 0); 290 he->next = 0; 291 *hep = he; 292 } 293 } 294 #ifdef DEBUG 295 memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]); 296 #endif 297 (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets); 298 } 299 } 300 301 PR_IMPLEMENT(PRBool) 302 PL_HashTableRemove(PLHashTable* ht, const void* key) { 303 PLHashNumber keyHash; 304 PLHashEntry *he, **hep; 305 306 keyHash = (*ht->keyHash)(key); 307 hep = PL_HashTableRawLookup(ht, keyHash, key); 308 if ((he = *hep) == 0) { 309 return PR_FALSE; 310 } 311 312 /* Hit; remove element */ 313 PL_HashTableRawRemove(ht, hep, he); 314 return PR_TRUE; 315 } 316 317 PR_IMPLEMENT(void*) 318 PL_HashTableLookup(PLHashTable* ht, const void* key) { 319 PLHashNumber keyHash; 320 PLHashEntry *he, **hep; 321 322 keyHash = (*ht->keyHash)(key); 323 hep = PL_HashTableRawLookup(ht, keyHash, key); 324 if ((he = *hep) != 0) { 325 return he->value; 326 } 327 return 0; 328 } 329 330 /* 331 ** Same as PL_HashTableLookup but doesn't reorder the hash entries. 332 */ 333 PR_IMPLEMENT(void*) 334 PL_HashTableLookupConst(PLHashTable* ht, const void* key) { 335 PLHashNumber keyHash; 336 PLHashEntry *he, **hep; 337 338 keyHash = (*ht->keyHash)(key); 339 hep = PL_HashTableRawLookupConst(ht, keyHash, key); 340 if ((he = *hep) != 0) { 341 return he->value; 342 } 343 return 0; 344 } 345 346 /* 347 ** Iterate over the entries in the hash table calling func for each 348 ** entry found. Stop if "f" says to (return value & PR_ENUMERATE_STOP). 349 ** Return a count of the number of elements scanned. 350 */ 351 PR_IMPLEMENT(int) 352 PL_HashTableEnumerateEntries(PLHashTable* ht, PLHashEnumerator f, void* arg) { 353 PLHashEntry *he, **hep; 354 PRUint32 i, nbuckets; 355 int rv, n = 0; 356 PLHashEntry* todo = 0; 357 358 nbuckets = NBUCKETS(ht); 359 for (i = 0; i < nbuckets; i++) { 360 hep = &ht->buckets[i]; 361 while ((he = *hep) != 0) { 362 rv = (*f)(he, n, arg); 363 n++; 364 if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) { 365 *hep = he->next; 366 if (rv & HT_ENUMERATE_REMOVE) { 367 he->next = todo; 368 todo = he; 369 } 370 } else { 371 hep = &he->next; 372 } 373 if (rv & HT_ENUMERATE_STOP) { 374 goto out; 375 } 376 } 377 } 378 379 out: 380 hep = &todo; 381 while ((he = *hep) != 0) { 382 PL_HashTableRawRemove(ht, hep, he); 383 } 384 return n; 385 } 386 387 #ifdef HASHMETER 388 # include <math.h> 389 # include <stdio.h> 390 391 PR_IMPLEMENT(void) 392 PL_HashTableDumpMeter(PLHashTable* ht, PLHashEnumerator dump, FILE* fp) { 393 double mean, variance; 394 PRUint32 nchains, nbuckets; 395 PRUint32 i, n, maxChain, maxChainLen; 396 PLHashEntry* he; 397 398 variance = 0; 399 nchains = 0; 400 maxChainLen = 0; 401 nbuckets = NBUCKETS(ht); 402 for (i = 0; i < nbuckets; i++) { 403 he = ht->buckets[i]; 404 if (!he) { 405 continue; 406 } 407 nchains++; 408 for (n = 0; he; he = he->next) { 409 n++; 410 } 411 variance += n * n; 412 if (n > maxChainLen) { 413 maxChainLen = n; 414 maxChain = i; 415 } 416 } 417 mean = (double)ht->nentries / nchains; 418 variance = fabs(variance / nchains - mean * mean); 419 420 fprintf(fp, "\nHash table statistics:\n"); 421 fprintf(fp, " number of lookups: %u\n", ht->nlookups); 422 fprintf(fp, " number of entries: %u\n", ht->nentries); 423 fprintf(fp, " number of grows: %u\n", ht->ngrows); 424 fprintf(fp, " number of shrinks: %u\n", ht->nshrinks); 425 fprintf(fp, " mean steps per hash: %g\n", 426 (double)ht->nsteps / ht->nlookups); 427 fprintf(fp, "mean hash chain length: %g\n", mean); 428 fprintf(fp, " standard deviation: %g\n", sqrt(variance)); 429 fprintf(fp, " max hash chain length: %u\n", maxChainLen); 430 fprintf(fp, " max hash chain: [%u]\n", maxChain); 431 432 for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++) 433 if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT) { 434 break; 435 } 436 } 437 #endif /* HASHMETER */ 438 439 PR_IMPLEMENT(int) 440 PL_HashTableDump(PLHashTable* ht, PLHashEnumerator dump, FILE* fp) { 441 int count; 442 443 count = PL_HashTableEnumerateEntries(ht, dump, fp); 444 #ifdef HASHMETER 445 PL_HashTableDumpMeter(ht, dump, fp); 446 #endif 447 return count; 448 } 449 450 PR_IMPLEMENT(PLHashNumber) 451 PL_HashString(const void* key) { 452 PLHashNumber h; 453 const PRUint8* s; 454 455 h = 0; 456 for (s = (const PRUint8*)key; *s; s++) { 457 h = PR_ROTATE_LEFT32(h, 4) ^ *s; 458 } 459 return h; 460 } 461 462 PR_IMPLEMENT(int) 463 PL_CompareStrings(const void* v1, const void* v2) { 464 return strcmp((const char*)v1, (const char*)v2) == 0; 465 } 466 467 PR_IMPLEMENT(int) 468 PL_CompareValues(const void* v1, const void* v2) { return v1 == v2; }