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 <stdlib.h>
44 : #include <string.h>
45 : #include "jstypes.h"
46 : #include "jsutil.h"
47 : #include "jshash.h"
48 :
49 : using namespace js;
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 : */
67 : static void *
68 1120 : DefaultAllocTable(void *pool, size_t size)
69 : {
70 1120 : return OffTheBooks::malloc_(size);
71 : }
72 :
73 : static void
74 1120 : DefaultFreeTable(void *pool, void *item, size_t size)
75 : {
76 1120 : UnwantedForeground::free_(item);
77 1120 : }
78 :
79 : static JSHashEntry *
80 0 : DefaultAllocEntry(void *pool, const void *key)
81 : {
82 0 : return (JSHashEntry*) OffTheBooks::malloc_(sizeof(JSHashEntry));
83 : }
84 :
85 : static void
86 0 : DefaultFreeEntry(void *pool, JSHashEntry *he, unsigned flag)
87 : {
88 0 : if (flag == HT_FREE_ENTRY)
89 0 : UnwantedForeground::free_(he);
90 0 : }
91 :
92 : static JSHashAllocOps defaultHashAllocOps = {
93 : DefaultAllocTable, DefaultFreeTable,
94 : DefaultAllocEntry, DefaultFreeEntry
95 : };
96 :
97 : JS_PUBLIC_API(JSHashTable *)
98 840 : JS_NewHashTable(uint32_t n, JSHashFunction keyHash,
99 : JSHashComparator keyCompare, JSHashComparator valueCompare,
100 : JSHashAllocOps *allocOps, void *allocPriv)
101 : {
102 : JSHashTable *ht;
103 : size_t nb;
104 :
105 840 : if (n <= MINBUCKETS) {
106 0 : n = MINBUCKETSLOG2;
107 : } else {
108 840 : n = JS_CEILING_LOG2W(n);
109 840 : if (int32_t(n) < 0)
110 0 : return NULL;
111 : }
112 :
113 840 : if (!allocOps) allocOps = &defaultHashAllocOps;
114 :
115 840 : ht = (JSHashTable*) allocOps->allocTable(allocPriv, sizeof *ht);
116 840 : if (!ht)
117 0 : return NULL;
118 840 : memset(ht, 0, sizeof *ht);
119 840 : ht->shift = JS_HASH_BITS - n;
120 840 : n = JS_BIT(n);
121 840 : nb = n * sizeof(JSHashEntry *);
122 840 : ht->buckets = (JSHashEntry**) allocOps->allocTable(allocPriv, nb);
123 840 : if (!ht->buckets) {
124 0 : allocOps->freeTable(allocPriv, ht, nb);
125 0 : return NULL;
126 : }
127 840 : memset(ht->buckets, 0, nb);
128 :
129 840 : ht->keyHash = keyHash;
130 840 : ht->keyCompare = keyCompare;
131 840 : ht->valueCompare = valueCompare;
132 840 : ht->allocOps = allocOps;
133 840 : ht->allocPriv = allocPriv;
134 840 : return ht;
135 : }
136 :
137 : JS_PUBLIC_API(void)
138 840 : JS_HashTableDestroy(JSHashTable *ht)
139 : {
140 : uint32_t i, n;
141 : JSHashEntry *he, **hep;
142 840 : JSHashAllocOps *allocOps = ht->allocOps;
143 840 : void *allocPriv = ht->allocPriv;
144 :
145 840 : n = NBUCKETS(ht);
146 493672 : for (i = 0; i < n; i++) {
147 492832 : hep = &ht->buckets[i];
148 1137011 : while ((he = *hep) != NULL) {
149 151347 : *hep = he->next;
150 151347 : allocOps->freeEntry(allocPriv, he, HT_FREE_ENTRY);
151 : }
152 : }
153 : #ifdef DEBUG
154 840 : memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
155 : #endif
156 840 : allocOps->freeTable(allocPriv, ht->buckets, n * sizeof ht->buckets[0]);
157 : #ifdef DEBUG
158 840 : memset(ht, 0xDB, sizeof *ht);
159 : #endif
160 840 : allocOps->freeTable(allocPriv, ht, sizeof *ht);
161 840 : }
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 :
169 : JS_PUBLIC_API(JSHashEntry **)
170 333061 : JS_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 333061 : hep = hep0 = BUCKET_HEAD(ht, keyHash);
178 762308 : while ((he = *hep) != NULL) {
179 206659 : if (he->keyHash == keyHash && ht->keyCompare(key, he->key)) {
180 : /* Move to front of chain if not already there */
181 110473 : if (hep != hep0) {
182 8245 : *hep = he->next;
183 8245 : he->next = *hep0;
184 8245 : *hep0 = he;
185 : }
186 110473 : return hep0;
187 : }
188 96186 : hep = &he->next;
189 : #ifdef JS_HASHMETER
190 : ht->nsteps++;
191 : #endif
192 : }
193 222588 : return hep;
194 : }
195 :
196 : static JSBool
197 684 : Resize(JSHashTable *ht, uint32_t newshift)
198 : {
199 : size_t nb, nentries, i;
200 : JSHashEntry **oldbuckets, *he, *next, **hep;
201 684 : size_t nold = NBUCKETS(ht);
202 :
203 684 : JS_ASSERT(newshift < JS_HASH_BITS);
204 :
205 684 : nb = (size_t)1 << (JS_HASH_BITS - newshift);
206 :
207 : /* Integer overflow protection. */
208 684 : if (nb > (size_t)-1 / sizeof(JSHashEntry*))
209 0 : return JS_FALSE;
210 684 : nb *= sizeof(JSHashEntry*);
211 :
212 684 : oldbuckets = ht->buckets;
213 684 : ht->buckets = (JSHashEntry**)ht->allocOps->allocTable(ht->allocPriv, nb);
214 684 : if (!ht->buckets) {
215 0 : ht->buckets = oldbuckets;
216 0 : return JS_FALSE;
217 : }
218 684 : memset(ht->buckets, 0, nb);
219 :
220 684 : ht->shift = newshift;
221 684 : nentries = ht->nentries;
222 :
223 406408 : for (i = 0; nentries != 0; i++) {
224 590280 : for (he = oldbuckets[i]; he; he = next) {
225 184556 : JS_ASSERT(nentries != 0);
226 184556 : --nentries;
227 184556 : next = he->next;
228 184556 : hep = BUCKET_HEAD(ht, he->keyHash);
229 :
230 : /*
231 : * We do not require unique entries, instead appending he to the
232 : * chain starting at hep.
233 : */
234 397903 : while (*hep)
235 28791 : hep = &(*hep)->next;
236 184556 : he->next = NULL;
237 184556 : *hep = he;
238 : }
239 : }
240 : #ifdef DEBUG
241 684 : memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]);
242 : #endif
243 : ht->allocOps->freeTable(ht->allocPriv, oldbuckets,
244 684 : nold * sizeof oldbuckets[0]);
245 684 : return JS_TRUE;
246 : }
247 :
248 : JS_PUBLIC_API(JSHashEntry *)
249 206573 : JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **&hep,
250 : JSHashNumber keyHash, const void *key, void *value)
251 : {
252 : uint32_t n;
253 : JSHashEntry *he;
254 :
255 : /* Grow the table if it is overloaded */
256 206573 : n = NBUCKETS(ht);
257 206573 : if (ht->nentries >= OVERLOADED(n)) {
258 150 : if (!Resize(ht, ht->shift - 1))
259 0 : return NULL;
260 : #ifdef JS_HASHMETER
261 : ht->ngrows++;
262 : #endif
263 150 : hep = JS_HashTableRawLookup(ht, keyHash, key);
264 : }
265 :
266 : /* Make a new key value entry */
267 206573 : he = ht->allocOps->allocEntry(ht->allocPriv, key);
268 206573 : if (!he)
269 0 : return NULL;
270 206573 : he->keyHash = keyHash;
271 206573 : he->key = key;
272 206573 : he->value = value;
273 206573 : he->next = *hep;
274 206573 : *hep = he;
275 206573 : ht->nentries++;
276 206573 : return he;
277 : }
278 :
279 : JS_PUBLIC_API(JSHashEntry *)
280 206573 : JS_HashTableAdd(JSHashTable *ht, const void *key, void *value)
281 : {
282 : JSHashNumber keyHash;
283 : JSHashEntry *he, **hep;
284 :
285 206573 : keyHash = ht->keyHash(key);
286 206573 : hep = JS_HashTableRawLookup(ht, keyHash, key);
287 206573 : if ((he = *hep) != NULL) {
288 : /* Hit; see if values match */
289 0 : if (ht->valueCompare(he->value, value)) {
290 : /* key,value pair is already present in table */
291 0 : return he;
292 : }
293 0 : if (he->value)
294 0 : ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_VALUE);
295 0 : he->value = value;
296 0 : return he;
297 : }
298 206573 : return JS_HashTableRawAdd(ht, hep, keyHash, key, value);
299 : }
300 :
301 : JS_PUBLIC_API(void)
302 55226 : JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he)
303 : {
304 : uint32_t n;
305 :
306 55226 : *hep = he->next;
307 55226 : ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY);
308 :
309 : /* Shrink table if it's underloaded */
310 55226 : n = NBUCKETS(ht);
311 55226 : if (--ht->nentries < UNDERLOADED(n)) {
312 534 : Resize(ht, ht->shift + 1);
313 : #ifdef JS_HASHMETER
314 : ht->nshrinks++;
315 : #endif
316 : }
317 55226 : }
318 :
319 : JS_PUBLIC_API(JSBool)
320 55226 : JS_HashTableRemove(JSHashTable *ht, const void *key)
321 : {
322 : JSHashNumber keyHash;
323 : JSHashEntry *he, **hep;
324 :
325 55226 : keyHash = ht->keyHash(key);
326 55226 : hep = JS_HashTableRawLookup(ht, keyHash, key);
327 55226 : if ((he = *hep) == NULL)
328 0 : return JS_FALSE;
329 :
330 : /* Hit; remove element */
331 55226 : JS_HashTableRawRemove(ht, hep, he);
332 55226 : return JS_TRUE;
333 : }
334 :
335 : JS_PUBLIC_API(void *)
336 71112 : JS_HashTableLookup(JSHashTable *ht, const void *key)
337 : {
338 : JSHashNumber keyHash;
339 : JSHashEntry *he, **hep;
340 :
341 71112 : keyHash = ht->keyHash(key);
342 71112 : hep = JS_HashTableRawLookup(ht, keyHash, key);
343 71112 : if ((he = *hep) != NULL) {
344 55247 : return he->value;
345 : }
346 15865 : return NULL;
347 : }
348 :
349 : /*
350 : ** Iterate over the entries in the hash table calling func for each
351 : ** entry found. Stop if "f" says to (return value & JS_ENUMERATE_STOP).
352 : ** Return a count of the number of elements scanned.
353 : */
354 : JS_PUBLIC_API(int)
355 280 : JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg)
356 : {
357 : JSHashEntry *he, **hep, **bucket;
358 : uint32_t nlimit, n, nbuckets, newlog2;
359 : int rv;
360 :
361 280 : nlimit = ht->nentries;
362 280 : n = 0;
363 280 : for (bucket = ht->buckets; n != nlimit; ++bucket) {
364 0 : hep = bucket;
365 0 : while ((he = *hep) != NULL) {
366 0 : JS_ASSERT(n < nlimit);
367 0 : rv = f(he, n, arg);
368 0 : n++;
369 0 : if (rv & HT_ENUMERATE_REMOVE) {
370 0 : *hep = he->next;
371 0 : ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY);
372 0 : --ht->nentries;
373 : } else {
374 0 : hep = &he->next;
375 : }
376 0 : if (rv & HT_ENUMERATE_STOP) {
377 0 : goto out;
378 : }
379 : }
380 : }
381 :
382 : out:
383 : /* Shrink table if removal of entries made it underloaded */
384 280 : if (ht->nentries != nlimit) {
385 0 : JS_ASSERT(ht->nentries < nlimit);
386 0 : nbuckets = NBUCKETS(ht);
387 0 : if (MINBUCKETS < nbuckets && ht->nentries < UNDERLOADED(nbuckets)) {
388 0 : newlog2 = JS_CEILING_LOG2W(ht->nentries);
389 0 : if (newlog2 < MINBUCKETSLOG2)
390 0 : newlog2 = MINBUCKETSLOG2;
391 :
392 : /* Check that we really shrink the table. */
393 0 : JS_ASSERT(JS_HASH_BITS - ht->shift > newlog2);
394 0 : Resize(ht, JS_HASH_BITS - newlog2);
395 : }
396 : }
397 280 : return (int)n;
398 : }
399 :
400 : #ifdef JS_HASHMETER
401 : #include <stdio.h>
402 :
403 : JS_PUBLIC_API(void)
404 : JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
405 : {
406 : double sqsum, mean, sigma;
407 : uint32_t nchains, nbuckets;
408 : uint32_t i, n, maxChain, maxChainLen;
409 : JSHashEntry *he;
410 :
411 : sqsum = 0;
412 : nchains = 0;
413 : maxChain = maxChainLen = 0;
414 : nbuckets = NBUCKETS(ht);
415 : for (i = 0; i < nbuckets; i++) {
416 : he = ht->buckets[i];
417 : if (!he)
418 : continue;
419 : nchains++;
420 : for (n = 0; he; he = he->next)
421 : n++;
422 : sqsum += n * n;
423 : if (n > maxChainLen) {
424 : maxChainLen = n;
425 : maxChain = i;
426 : }
427 : }
428 :
429 : mean = JS_MeanAndStdDev(nchains, ht->nentries, sqsum, &sigma);
430 :
431 : fprintf(fp, "\nHash table statistics:\n");
432 : fprintf(fp, " number of lookups: %u\n", ht->nlookups);
433 : fprintf(fp, " number of entries: %u\n", ht->nentries);
434 : fprintf(fp, " number of grows: %u\n", ht->ngrows);
435 : fprintf(fp, " number of shrinks: %u\n", ht->nshrinks);
436 : fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps
437 : / ht->nlookups);
438 : fprintf(fp, "mean hash chain length: %g\n", mean);
439 : fprintf(fp, " standard deviation: %g\n", sigma);
440 : fprintf(fp, " max hash chain length: %u\n", maxChainLen);
441 : fprintf(fp, " max hash chain: [%u]\n", maxChain);
442 :
443 : for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)
444 : if (dump(he, i, fp) != HT_ENUMERATE_NEXT)
445 : break;
446 : }
447 : #endif /* JS_HASHMETER */
448 :
449 : JS_PUBLIC_API(int)
450 0 : JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
451 : {
452 : int count;
453 :
454 0 : count = JS_HashTableEnumerateEntries(ht, dump, fp);
455 : #ifdef JS_HASHMETER
456 : JS_HashTableDumpMeter(ht, dump, fp);
457 : #endif
458 0 : return count;
459 : }
460 :
461 : JS_PUBLIC_API(JSHashNumber)
462 1286209 : JS_HashString(const void *key)
463 : {
464 : JSHashNumber h;
465 : const unsigned char *s;
466 :
467 1286209 : h = 0;
468 76119073 : for (s = (const unsigned char *)key; *s; s++)
469 74832864 : h = JS_ROTATE_LEFT32(h, 4) ^ *s;
470 1286209 : return h;
471 : }
472 :
473 : JS_PUBLIC_API(int)
474 110473 : JS_CompareValues(const void *v1, const void *v2)
475 : {
476 110473 : return v1 == v2;
477 : }
|