JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg) { JSHashEntry *he, **hep, **bucket; uint32 nlimit, n, nbuckets, newlog2; int rv; nlimit = ht->nentries; n = 0; for (bucket = ht->buckets; n != nlimit; ++bucket) { hep = bucket; while ((he = *hep) != NULL) { JS_ASSERT(n < nlimit); rv = f(he, n, arg); n++; if (rv & HT_ENUMERATE_REMOVE) { *hep = he->next; ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); --ht->nentries; } else { hep = &he->next; } if (rv & HT_ENUMERATE_STOP) { goto out; } } } out: /* Shrink table if removal of entries made it underloaded */ if (ht->nentries != nlimit) { JS_ASSERT(ht->nentries < nlimit); nbuckets = NBUCKETS(ht); if (MINBUCKETS < nbuckets && ht->nentries < UNDERLOADED(nbuckets)) { newlog2 = JS_CeilingLog2(ht->nentries); if (newlog2 < MINBUCKETSLOG2) newlog2 = MINBUCKETSLOG2; /* Check that we really shrink the table. */ JS_ASSERT(JS_HASH_BITS - ht->shift > newlog2); Resize(ht, JS_HASH_BITS - newlog2); } } return (int)n; }
Int32 HashTableEnumerateEntries(HashTable *ht, HashEnumerator f) { HashEntry *he = NULL, **hep = NULL; Uint32 i = 0, nbuckets = 0; int rv = 0, n = 0; HashEntry *todo = 0; nbuckets = NBUCKETS(ht); for (i = 0; i < nbuckets; i++) { hep = &ht->buckets[i]; while ((he = *hep) != 0) { rv = (*f)(he, n); n++; if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) { *hep = he->next; if (rv & HT_ENUMERATE_REMOVE) { he->next = todo; todo = he; } } else { hep = &he->next; } if (rv & HT_ENUMERATE_STOP) { goto out; } } } out: hep = &todo; while ((he = *hep) != 0) { HashTableRawRemove(ht, hep, he); } return n; }
PL_HashTableDumpMeter(PLHashTable *ht, PLHashEnumerator dump, FILE *fp) { double mean, variance; PRUint32 nchains, nbuckets; PRUint32 i, n, maxChain, maxChainLen; PLHashEntry *he; variance = 0; nchains = 0; maxChainLen = 0; nbuckets = NBUCKETS(ht); for (i = 0; i < nbuckets; i++) { he = ht->buckets[i]; if (!he) continue; nchains++; for (n = 0; he; he = he->next) n++; variance += n * n; if (n > maxChainLen) { maxChainLen = n; maxChain = i; } } mean = (double)ht->nentries / nchains; variance = fabs(variance / nchains - mean * mean); fprintf(fp, "\nHash table statistics:\n"); fprintf(fp, " number of lookups: %u\n", ht->nlookups); fprintf(fp, " number of entries: %u\n", ht->nentries); fprintf(fp, " number of grows: %u\n", ht->ngrows); fprintf(fp, " number of shrinks: %u\n", ht->nshrinks); fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps / ht->nlookups); fprintf(fp, "mean hash chain length: %g\n", mean); fprintf(fp, " standard deviation: %g\n", sqrt(variance)); fprintf(fp, " max hash chain length: %u\n", maxChainLen); fprintf(fp, " max hash chain: [%u]\n", maxChain); for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++) if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT) break; }
JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) { double sqsum, mean, sigma; uint32 nchains, nbuckets; uint32 i, n, maxChain, maxChainLen; JSHashEntry *he; sqsum = 0; nchains = 0; maxChain = maxChainLen = 0; nbuckets = NBUCKETS(ht); for (i = 0; i < nbuckets; i++) { he = ht->buckets[i]; if (!he) continue; nchains++; for (n = 0; he; he = he->next) n++; sqsum += n * n; if (n > maxChainLen) { maxChainLen = n; maxChain = i; } } mean = JS_MeanAndStdDev(nchains, ht->nentries, sqsum, &sigma); fprintf(fp, "\nHash table statistics:\n"); fprintf(fp, " number of lookups: %u\n", ht->nlookups); fprintf(fp, " number of entries: %u\n", ht->nentries); fprintf(fp, " number of grows: %u\n", ht->ngrows); fprintf(fp, " number of shrinks: %u\n", ht->nshrinks); fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps / ht->nlookups); fprintf(fp, "mean hash chain length: %g\n", mean); fprintf(fp, " standard deviation: %g\n", sigma); fprintf(fp, " max hash chain length: %u\n", maxChainLen); fprintf(fp, " max hash chain: [%u]\n", maxChain); for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++) if (dump(he, i, fp) != HT_ENUMERATE_NEXT) break; }
PL_HashTableRawRemove(PLHashTable *ht, PLHashEntry **hep, PLHashEntry *he) { PRUint32 i, n; PLHashEntry *next, **oldbuckets; PRSize nb; *hep = he->next; (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY); /* Shrink table if it's underloaded */ n = NBUCKETS(ht); if (--ht->nentries < UNDERLOADED(n)) { oldbuckets = ht->buckets; nb = n * sizeof(PLHashEntry*) / 2; ht->buckets = (PLHashEntry**)( (*ht->allocOps->allocTable)(ht->allocPriv, nb)); if (!ht->buckets) { ht->buckets = oldbuckets; return; } memset(ht->buckets, 0, nb); #ifdef HASHMETER ht->nshrinks++; #endif ht->shift++; for (i = 0; i < n; i++) { for (he = oldbuckets[i]; he; he = next) { next = he->next; hep = PL_HashTableRawLookup(ht, he->keyHash, he->key); PR_ASSERT(*hep == 0); he->next = 0; *hep = he; } } #ifdef DEBUG memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]); #endif (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets); } }
PL_HashTableDestroy(PLHashTable *ht) { PRUint32 i, n; PLHashEntry *he, *next; const PLHashAllocOps *allocOps = ht->allocOps; void *allocPriv = ht->allocPriv; n = NBUCKETS(ht); for (i = 0; i < n; i++) { for (he = ht->buckets[i]; he; he = next) { next = he->next; (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY); } } #ifdef DEBUG memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]); #endif (*allocOps->freeTable)(allocPriv, ht->buckets); #ifdef DEBUG memset(ht, 0xDB, sizeof *ht); #endif (*allocOps->freeTable)(allocPriv, ht); }
JS_HashTableDestroy(JSHashTable *ht) { uint32 i, n; JSHashEntry *he, **hep; JSHashAllocOps *allocOps = ht->allocOps; void *allocPriv = ht->allocPriv; n = NBUCKETS(ht); for (i = 0; i < n; i++) { hep = &ht->buckets[i]; while ((he = *hep) != NULL) { *hep = he->next; allocOps->freeEntry(allocPriv, he, HT_FREE_ENTRY); } } #ifdef DEBUG memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]); #endif allocOps->freeTable(allocPriv, ht->buckets, n * sizeof ht->buckets[0]); #ifdef DEBUG memset(ht, 0xDB, sizeof *ht); #endif allocOps->freeTable(allocPriv, ht, sizeof *ht); }
HashEntry *HashTableRawAdd(HashTable *ht, HashEntry **hep, HashNumber keyHash, const void *key, void *value) { Uint32 i = 0, n = 0, nbuckets = 0; HashEntry *he = NULL, *next = NULL, **oldbuckets = NULL; Size_t nb = 0; /* Grow the table if it is overloaded */ n = NBUCKETS(ht); if (ht->nentries >= OVERLOADED(n)) { oldbuckets = ht->buckets; nbuckets = hash_next_prime(n + 1); nb = 2 * nbuckets * sizeof(HashEntry *); ht->buckets = (HashEntry **)((*ht->allocOps->allocTable)(ht->allocPriv, nb)); if (!ht->buckets) { ht->buckets = oldbuckets; return 0; } memset(ht->buckets, 0, nb); ht->nbuckets = nbuckets; #ifdef HASHMETER ht->ngrows++; #endif for (i = 0; i < n; i++) { for (he = oldbuckets[i]; he; he = next) { next = he->next; hep = HashTableRawLookup(ht, he->keyHash, he->key); if (*hep != 0) { PR_LOG("##### Oops! %s: Severe problem.\n", __func__); return 0; } he->next = 0; *hep = he; } } #ifdef HASHDEBUG memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]); #endif (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets); hep = HashTableRawLookup(ht, keyHash, key); } /* Make a new key value entry */ he = (*ht->allocOps->allocEntry)(ht->allocPriv, key); if (!he) { return 0; } he->keyHash = keyHash; he->key = key; he->value = value; he->next = *hep; *hep = he; ht->nentries++; return he; }