/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jshash.cpp
C++ | 476 lines | 350 code | 56 blank | 70 comment | 57 complexity | 3fa1df16ff7543d4c526f28746ce1903 MD5 | raw file
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * 3 * ***** BEGIN LICENSE BLOCK ***** 4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 5 * 6 * The contents of this file are subject to the Mozilla Public License Version 7 * 1.1 (the "License"); you may not use this file except in compliance with 8 * the License. You may obtain a copy of the License at 9 * http://www.mozilla.org/MPL/ 10 * 11 * Software distributed under the License is distributed on an "AS IS" basis, 12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 13 * for the specific language governing rights and limitations under the 14 * License. 15 * 16 * The Original Code is Mozilla Communicator client code, released 17 * March 31, 1998. 18 * 19 * The Initial Developer of the Original Code is 20 * Netscape Communications Corporation. 21 * Portions created by the Initial Developer are Copyright (C) 1998 22 * the Initial Developer. All Rights Reserved. 23 * 24 * Contributor(s): 25 * 26 * Alternatively, the contents of this file may be used under the terms of 27 * either of the GNU General Public License Version 2 or later (the "GPL"), 28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 29 * in which case the provisions of the GPL or the LGPL are applicable instead 30 * of those above. If you wish to allow use of your version of this file only 31 * under the terms of either the GPL or the LGPL, and not to allow others to 32 * use your version of this file under the terms of the MPL, indicate your 33 * decision by deleting the provisions above and replace them with the notice 34 * and other provisions required by the GPL or the LGPL. If you do not delete 35 * the provisions above, a recipient may use your version of this file under 36 * the terms of any one of the MPL, the GPL or the LGPL. 37 * 38 * ***** END LICENSE BLOCK ***** */ 39 40/* 41 * PR hash table package. 42 */ 43#include "jsstddef.h" 44#include <stdlib.h> 45#include <string.h> 46#include "jstypes.h" 47#include "jsbit.h" 48#include "jsutil.h" /* Added by JSIFY */ 49#include "jshash.h" /* Added by JSIFY */ 50 51/* Compute the number of buckets in ht */ 52#define NBUCKETS(ht) JS_BIT(JS_HASH_BITS - (ht)->shift) 53 54/* The smallest table has 16 buckets */ 55#define MINBUCKETSLOG2 4 56#define MINBUCKETS JS_BIT(MINBUCKETSLOG2) 57 58/* Compute the maximum entries given n buckets that we will tolerate, ~90% */ 59#define OVERLOADED(n) ((n) - ((n) >> 3)) 60 61/* Compute the number of entries below which we shrink the table by half */ 62#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0) 63 64/* 65** Stubs for default hash allocator ops. 66*/ 67static void * 68DefaultAllocTable(void *pool, size_t size) 69{ 70 return malloc(size); 71} 72 73static void 74DefaultFreeTable(void *pool, void *item) 75{ 76 free(item); 77} 78 79static JSHashEntry * 80DefaultAllocEntry(void *pool, const void *key) 81{ 82 return (JSHashEntry*) malloc(sizeof(JSHashEntry)); 83} 84 85static void 86DefaultFreeEntry(void *pool, JSHashEntry *he, uintN flag) 87{ 88 if (flag == HT_FREE_ENTRY) 89 free(he); 90} 91 92static JSHashAllocOps defaultHashAllocOps = { 93 DefaultAllocTable, DefaultFreeTable, 94 DefaultAllocEntry, DefaultFreeEntry 95}; 96 97JS_PUBLIC_API(JSHashTable *) 98JS_NewHashTable(uint32 n, JSHashFunction keyHash, 99 JSHashComparator keyCompare, JSHashComparator valueCompare, 100 JSHashAllocOps *allocOps, void *allocPriv) 101{ 102 JSHashTable *ht; 103 size_t nb; 104 105 if (n <= MINBUCKETS) { 106 n = MINBUCKETSLOG2; 107 } else { 108 n = JS_CeilingLog2(n); 109 if ((int32)n < 0) 110 return NULL; 111 } 112 113 if (!allocOps) allocOps = &defaultHashAllocOps; 114 115 ht = (JSHashTable*) allocOps->allocTable(allocPriv, sizeof *ht); 116 if (!ht) 117 return NULL; 118 memset(ht, 0, sizeof *ht); 119 ht->shift = JS_HASH_BITS - n; 120 n = JS_BIT(n); 121 nb = n * sizeof(JSHashEntry *); 122 ht->buckets = (JSHashEntry**) allocOps->allocTable(allocPriv, nb); 123 if (!ht->buckets) { 124 allocOps->freeTable(allocPriv, ht); 125 return NULL; 126 } 127 memset(ht->buckets, 0, nb); 128 129 ht->keyHash = keyHash; 130 ht->keyCompare = keyCompare; 131 ht->valueCompare = valueCompare; 132 ht->allocOps = allocOps; 133 ht->allocPriv = allocPriv; 134 return ht; 135} 136 137JS_PUBLIC_API(void) 138JS_HashTableDestroy(JSHashTable *ht) 139{ 140 uint32 i, n; 141 JSHashEntry *he, **hep; 142 JSHashAllocOps *allocOps = ht->allocOps; 143 void *allocPriv = ht->allocPriv; 144 145 n = NBUCKETS(ht); 146 for (i = 0; i < n; i++) { 147 hep = &ht->buckets[i]; 148 while ((he = *hep) != NULL) { 149 *hep = he->next; 150 allocOps->freeEntry(allocPriv, he, HT_FREE_ENTRY); 151 } 152 } 153#ifdef DEBUG 154 memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]); 155#endif 156 allocOps->freeTable(allocPriv, ht->buckets); 157#ifdef DEBUG 158 memset(ht, 0xDB, sizeof *ht); 159#endif 160 allocOps->freeTable(allocPriv, ht); 161} 162 163/* 164 * Multiplicative hash, from Knuth 6.4. 165 */ 166#define BUCKET_HEAD(ht, keyHash) \ 167 (&(ht)->buckets[((keyHash) * JS_GOLDEN_RATIO) >> (ht)->shift]) 168 169JS_PUBLIC_API(JSHashEntry **) 170JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key) 171{ 172 JSHashEntry *he, **hep, **hep0; 173 174#ifdef JS_HASHMETER 175 ht->nlookups++; 176#endif 177 hep = hep0 = BUCKET_HEAD(ht, keyHash); 178 while ((he = *hep) != NULL) { 179 if (he->keyHash == keyHash && ht->keyCompare(key, he->key)) { 180 /* Move to front of chain if not already there */ 181 if (hep != hep0) { 182 *hep = he->next; 183 he->next = *hep0; 184 *hep0 = he; 185 } 186 return hep0; 187 } 188 hep = &he->next; 189#ifdef JS_HASHMETER 190 ht->nsteps++; 191#endif 192 } 193 return hep; 194} 195 196static JSBool 197Resize(JSHashTable *ht, uint32 newshift) 198{ 199 size_t nb, nentries, i; 200 JSHashEntry **oldbuckets, *he, *next, **hep; 201#ifdef DEBUG 202 size_t nold = NBUCKETS(ht); 203#endif 204 205 JS_ASSERT(newshift < JS_HASH_BITS); 206 207 nb = (size_t)1 << (JS_HASH_BITS - newshift); 208 209 /* Integer overflow protection. */ 210 if (nb > (size_t)-1 / sizeof(JSHashEntry*)) 211 return JS_FALSE; 212 nb *= sizeof(JSHashEntry*); 213 214 oldbuckets = ht->buckets; 215 ht->buckets = (JSHashEntry**)ht->allocOps->allocTable(ht->allocPriv, nb); 216 if (!ht->buckets) { 217 ht->buckets = oldbuckets; 218 return JS_FALSE; 219 } 220 memset(ht->buckets, 0, nb); 221 222 ht->shift = newshift; 223 nentries = ht->nentries; 224 225 for (i = 0; nentries != 0; i++) { 226 for (he = oldbuckets[i]; he; he = next) { 227 JS_ASSERT(nentries != 0); 228 --nentries; 229 next = he->next; 230 hep = BUCKET_HEAD(ht, he->keyHash); 231 232 /* 233 * Since he comes from the old table, it must be unique and we 234 * simply add it to the head of bucket chain without chain lookup. 235 */ 236 he->next = *hep; 237 *hep = he; 238 } 239 } 240#ifdef DEBUG 241 memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]); 242#endif 243 ht->allocOps->freeTable(ht->allocPriv, oldbuckets); 244 return JS_TRUE; 245} 246 247JS_PUBLIC_API(JSHashEntry *) 248JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, 249 JSHashNumber keyHash, const void *key, void *value) 250{ 251 uint32 n; 252 JSHashEntry *he; 253 254 /* Grow the table if it is overloaded */ 255 n = NBUCKETS(ht); 256 if (ht->nentries >= OVERLOADED(n)) { 257 if (!Resize(ht, ht->shift - 1)) 258 return NULL; 259#ifdef JS_HASHMETER 260 ht->ngrows++; 261#endif 262 hep = JS_HashTableRawLookup(ht, keyHash, key); 263 } 264 265 /* Make a new key value entry */ 266 he = ht->allocOps->allocEntry(ht->allocPriv, key); 267 if (!he) 268 return NULL; 269 he->keyHash = keyHash; 270 he->key = key; 271 he->value = value; 272 he->next = *hep; 273 *hep = he; 274 ht->nentries++; 275 return he; 276} 277 278JS_PUBLIC_API(JSHashEntry *) 279JS_HashTableAdd(JSHashTable *ht, const void *key, void *value) 280{ 281 JSHashNumber keyHash; 282 JSHashEntry *he, **hep; 283 284 keyHash = ht->keyHash(key); 285 hep = JS_HashTableRawLookup(ht, keyHash, key); 286 if ((he = *hep) != NULL) { 287 /* Hit; see if values match */ 288 if (ht->valueCompare(he->value, value)) { 289 /* key,value pair is already present in table */ 290 return he; 291 } 292 if (he->value) 293 ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_VALUE); 294 he->value = value; 295 return he; 296 } 297 return JS_HashTableRawAdd(ht, hep, keyHash, key, value); 298} 299 300JS_PUBLIC_API(void) 301JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he) 302{ 303 uint32 n; 304 305 *hep = he->next; 306 ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); 307 308 /* Shrink table if it's underloaded */ 309 n = NBUCKETS(ht); 310 if (--ht->nentries < UNDERLOADED(n)) { 311 Resize(ht, ht->shift + 1); 312#ifdef JS_HASHMETER 313 ht->nshrinks++; 314#endif 315 } 316} 317 318JS_PUBLIC_API(JSBool) 319JS_HashTableRemove(JSHashTable *ht, const void *key) 320{ 321 JSHashNumber keyHash; 322 JSHashEntry *he, **hep; 323 324 keyHash = ht->keyHash(key); 325 hep = JS_HashTableRawLookup(ht, keyHash, key); 326 if ((he = *hep) == NULL) 327 return JS_FALSE; 328 329 /* Hit; remove element */ 330 JS_HashTableRawRemove(ht, hep, he); 331 return JS_TRUE; 332} 333 334JS_PUBLIC_API(void *) 335JS_HashTableLookup(JSHashTable *ht, const void *key) 336{ 337 JSHashNumber keyHash; 338 JSHashEntry *he, **hep; 339 340 keyHash = ht->keyHash(key); 341 hep = JS_HashTableRawLookup(ht, keyHash, key); 342 if ((he = *hep) != NULL) { 343 return he->value; 344 } 345 return NULL; 346} 347 348/* 349** Iterate over the entries in the hash table calling func for each 350** entry found. Stop if "f" says to (return value & JS_ENUMERATE_STOP). 351** Return a count of the number of elements scanned. 352*/ 353JS_PUBLIC_API(int) 354JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg) 355{ 356 JSHashEntry *he, **hep, **bucket; 357 uint32 nlimit, n, nbuckets, newlog2; 358 int rv; 359 360 nlimit = ht->nentries; 361 n = 0; 362 for (bucket = ht->buckets; n != nlimit; ++bucket) { 363 hep = bucket; 364 while ((he = *hep) != NULL) { 365 JS_ASSERT(n < nlimit); 366 rv = f(he, n, arg); 367 n++; 368 if (rv & HT_ENUMERATE_REMOVE) { 369 *hep = he->next; 370 ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); 371 --ht->nentries; 372 } else { 373 hep = &he->next; 374 } 375 if (rv & HT_ENUMERATE_STOP) { 376 goto out; 377 } 378 } 379 } 380 381out: 382 /* Shrink table if removal of entries made it underloaded */ 383 if (ht->nentries != nlimit) { 384 JS_ASSERT(ht->nentries < nlimit); 385 nbuckets = NBUCKETS(ht); 386 if (MINBUCKETS < nbuckets && ht->nentries < UNDERLOADED(nbuckets)) { 387 newlog2 = JS_CeilingLog2(ht->nentries); 388 if (newlog2 < MINBUCKETSLOG2) 389 newlog2 = MINBUCKETSLOG2; 390 391 /* Check that we really shrink the table. */ 392 JS_ASSERT(JS_HASH_BITS - ht->shift > newlog2); 393 Resize(ht, JS_HASH_BITS - newlog2); 394 } 395 } 396 return (int)n; 397} 398 399#ifdef JS_HASHMETER 400#include <stdio.h> 401 402JS_PUBLIC_API(void) 403JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) 404{ 405 double sqsum, mean, sigma; 406 uint32 nchains, nbuckets; 407 uint32 i, n, maxChain, maxChainLen; 408 JSHashEntry *he; 409 410 sqsum = 0; 411 nchains = 0; 412 maxChain = maxChainLen = 0; 413 nbuckets = NBUCKETS(ht); 414 for (i = 0; i < nbuckets; i++) { 415 he = ht->buckets[i]; 416 if (!he) 417 continue; 418 nchains++; 419 for (n = 0; he; he = he->next) 420 n++; 421 sqsum += n * n; 422 if (n > maxChainLen) { 423 maxChainLen = n; 424 maxChain = i; 425 } 426 } 427 428 mean = JS_MeanAndStdDev(nchains, ht->nentries, sqsum, &sigma); 429 430 fprintf(fp, "\nHash table statistics:\n"); 431 fprintf(fp, " number of lookups: %u\n", ht->nlookups); 432 fprintf(fp, " number of entries: %u\n", ht->nentries); 433 fprintf(fp, " number of grows: %u\n", ht->ngrows); 434 fprintf(fp, " number of shrinks: %u\n", ht->nshrinks); 435 fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps 436 / ht->nlookups); 437 fprintf(fp, "mean hash chain length: %g\n", mean); 438 fprintf(fp, " standard deviation: %g\n", sigma); 439 fprintf(fp, " max hash chain length: %u\n", maxChainLen); 440 fprintf(fp, " max hash chain: [%u]\n", maxChain); 441 442 for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++) 443 if (dump(he, i, fp) != HT_ENUMERATE_NEXT) 444 break; 445} 446#endif /* JS_HASHMETER */ 447 448JS_PUBLIC_API(int) 449JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) 450{ 451 int count; 452 453 count = JS_HashTableEnumerateEntries(ht, dump, fp); 454#ifdef JS_HASHMETER 455 JS_HashTableDumpMeter(ht, dump, fp); 456#endif 457 return count; 458} 459 460JS_PUBLIC_API(JSHashNumber) 461JS_HashString(const void *key) 462{ 463 JSHashNumber h; 464 const unsigned char *s; 465 466 h = 0; 467 for (s = (const unsigned char *)key; *s; s++) 468 h = JS_ROTATE_LEFT32(h, 4) ^ *s; 469 return h; 470} 471 472JS_PUBLIC_API(int) 473JS_CompareValues(const void *v1, const void *v2) 474{ 475 return v1 == v2; 476}