Bug 328294: After enumeration that removes hashtable entries,
the hashtable is shrinked only once. r=brendan git-svn-id: svn://10.0.0.236/trunk@191075 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -161,20 +161,20 @@ JS_HashTableDestroy(JSHashTable *ht)
|
||||
}
|
||||
|
||||
/*
|
||||
** Multiplicative hash, from Knuth 6.4.
|
||||
*/
|
||||
* Multiplicative hash, from Knuth 6.4.
|
||||
*/
|
||||
#define BUCKET_HEAD(ht, keyHash) \
|
||||
(&(ht)->buckets[((keyHash) * JS_GOLDEN_RATIO) >> (ht)->shift])
|
||||
|
||||
JS_PUBLIC_API(JSHashEntry **)
|
||||
JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key)
|
||||
{
|
||||
JSHashEntry *he, **hep, **hep0;
|
||||
JSHashNumber h;
|
||||
|
||||
#ifdef HASHMETER
|
||||
ht->nlookups++;
|
||||
#endif
|
||||
h = keyHash * JS_GOLDEN_RATIO;
|
||||
h >>= ht->shift;
|
||||
hep = hep0 = &ht->buckets[h];
|
||||
hep = hep0 = BUCKET_HEAD(ht, keyHash);
|
||||
while ((he = *hep) != NULL) {
|
||||
if (he->keyHash == keyHash && ht->keyCompare(key, he->key)) {
|
||||
/* Move to front of chain if not already there */
|
||||
@@ -193,44 +193,72 @@ JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key)
|
||||
return hep;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
Resize(JSHashTable *ht, uint32 newshift)
|
||||
{
|
||||
size_t nb, nentries, i;
|
||||
JSHashEntry **oldbuckets, *he, *next, **hep;
|
||||
#ifdef DEBUG
|
||||
size_t nold = NBUCKETS(ht);
|
||||
#endif
|
||||
|
||||
JS_ASSERT(newshift < JS_HASH_BITS);
|
||||
|
||||
nb = (size_t)1 << (JS_HASH_BITS - newshift);
|
||||
|
||||
/* Integer overflow protection. */
|
||||
if (nb > (size_t)-1 / sizeof(JSHashEntry*))
|
||||
return JS_FALSE;
|
||||
nb *= sizeof(JSHashEntry*);
|
||||
|
||||
oldbuckets = ht->buckets;
|
||||
ht->buckets = (JSHashEntry**)ht->allocOps->allocTable(ht->allocPriv, nb);
|
||||
if (!ht->buckets) {
|
||||
ht->buckets = oldbuckets;
|
||||
return JS_FALSE;
|
||||
}
|
||||
memset(ht->buckets, 0, nb);
|
||||
|
||||
ht->shift = newshift;
|
||||
nentries = ht->nentries;
|
||||
|
||||
for (i = 0; nentries != 0; i++) {
|
||||
for (he = oldbuckets[i]; he; he = next) {
|
||||
JS_ASSERT(nentries != 0);
|
||||
--nentries;
|
||||
next = he->next;
|
||||
hep = BUCKET_HEAD(ht, he->keyHash);
|
||||
|
||||
/*
|
||||
* Since he comes from the old table, it must be unique and we
|
||||
* simply add it to the head of bucket chain without chain lookup.
|
||||
*/
|
||||
he->next = *hep;
|
||||
*hep = he;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]);
|
||||
#endif
|
||||
ht->allocOps->freeTable(ht->allocPriv, oldbuckets);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSHashEntry *)
|
||||
JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep,
|
||||
JSHashNumber keyHash, const void *key, void *value)
|
||||
{
|
||||
uint32 i, n;
|
||||
JSHashEntry *he, *next, **oldbuckets;
|
||||
size_t nb;
|
||||
uint32 n;
|
||||
JSHashEntry *he;
|
||||
|
||||
/* Grow the table if it is overloaded */
|
||||
n = NBUCKETS(ht);
|
||||
if (ht->nentries >= OVERLOADED(n)) {
|
||||
oldbuckets = ht->buckets;
|
||||
nb = 2 * n * sizeof(JSHashEntry *);
|
||||
ht->buckets = (JSHashEntry**)
|
||||
ht->allocOps->allocTable(ht->allocPriv, nb);
|
||||
if (!ht->buckets) {
|
||||
ht->buckets = oldbuckets;
|
||||
if (!Resize(ht, ht->shift - 1))
|
||||
return NULL;
|
||||
}
|
||||
memset(ht->buckets, 0, nb);
|
||||
#ifdef HASHMETER
|
||||
ht->ngrows++;
|
||||
#endif
|
||||
ht->shift--;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
for (he = oldbuckets[i]; he; he = next) {
|
||||
next = he->next;
|
||||
hep = JS_HashTableRawLookup(ht, he->keyHash, he->key);
|
||||
JS_ASSERT(*hep == NULL);
|
||||
he->next = NULL;
|
||||
*hep = he;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
|
||||
#endif
|
||||
ht->allocOps->freeTable(ht->allocPriv, oldbuckets);
|
||||
hep = JS_HashTableRawLookup(ht, keyHash, key);
|
||||
}
|
||||
|
||||
@@ -272,9 +300,7 @@ JS_HashTableAdd(JSHashTable *ht, const void *key, void *value)
|
||||
JS_PUBLIC_API(void)
|
||||
JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he)
|
||||
{
|
||||
uint32 i, n;
|
||||
JSHashEntry *next, **oldbuckets;
|
||||
size_t nb;
|
||||
uint32 n;
|
||||
|
||||
*hep = he->next;
|
||||
ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY);
|
||||
@@ -282,33 +308,10 @@ JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he)
|
||||
/* Shrink table if it's underloaded */
|
||||
n = NBUCKETS(ht);
|
||||
if (--ht->nentries < UNDERLOADED(n)) {
|
||||
oldbuckets = ht->buckets;
|
||||
nb = n * sizeof(JSHashEntry*) / 2;
|
||||
ht->buckets = (JSHashEntry**)
|
||||
ht->allocOps->allocTable(ht->allocPriv, nb);
|
||||
if (!ht->buckets) {
|
||||
ht->buckets = oldbuckets;
|
||||
return;
|
||||
}
|
||||
memset(ht->buckets, 0, nb);
|
||||
Resize(ht, ht->shift + 1);
|
||||
#ifdef HASHMETER
|
||||
ht->nshrinks++;
|
||||
#endif
|
||||
ht->shift++;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
for (he = oldbuckets[i]; he; he = next) {
|
||||
next = he->next;
|
||||
hep = JS_HashTableRawLookup(ht, he->keyHash, he->key);
|
||||
JS_ASSERT(*hep == NULL);
|
||||
he->next = NULL;
|
||||
*hep = he;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
|
||||
#endif
|
||||
ht->allocOps->freeTable(ht->allocPriv, oldbuckets);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,21 +353,22 @@ JS_HashTableLookup(JSHashTable *ht, const void *key)
|
||||
JS_PUBLIC_API(int)
|
||||
JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg)
|
||||
{
|
||||
JSHashEntry *he, **hep;
|
||||
uint32 i, nbuckets;
|
||||
int rv, n = 0;
|
||||
JSHashEntry *todo = NULL;
|
||||
JSHashEntry *he, **hep, **bucket;
|
||||
uint32 nlimit, n, nbuckets, newlog2;
|
||||
int rv;
|
||||
|
||||
nbuckets = NBUCKETS(ht);
|
||||
for (i = 0; i < nbuckets; i++) {
|
||||
hep = &ht->buckets[i];
|
||||
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;
|
||||
he->next = todo;
|
||||
todo = he;
|
||||
ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY);
|
||||
--ht->nentries;
|
||||
} else {
|
||||
hep = &he->next;
|
||||
}
|
||||
@@ -375,11 +379,21 @@ JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg)
|
||||
}
|
||||
|
||||
out:
|
||||
hep = &todo;
|
||||
while ((he = *hep) != NULL) {
|
||||
JS_HashTableRawRemove(ht, hep, he);
|
||||
/* 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 n;
|
||||
return (int)n;
|
||||
}
|
||||
|
||||
#ifdef HASHMETER
|
||||
|
||||
Reference in New Issue
Block a user