1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Netscape Portable Runtime (NSPR).
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /*
39 : * PL hash table package.
40 : */
41 : #include "plhash.h"
42 : #include "prbit.h"
43 : #include "prlog.h"
44 : #include "prmem.h"
45 : #include "prtypes.h"
46 : #include <stdlib.h>
47 : #include <string.h>
48 :
49 : /* Compute the number of buckets in ht */
50 : #define NBUCKETS(ht) (1 << (PL_HASH_BITS - (ht)->shift))
51 :
52 : /* The smallest table has 16 buckets */
53 : #define MINBUCKETSLOG2 4
54 : #define MINBUCKETS (1 << MINBUCKETSLOG2)
55 :
56 : /* Compute the maximum entries given n buckets that we will tolerate, ~90% */
57 : #define OVERLOADED(n) ((n) - ((n) >> 3))
58 :
59 : /* Compute the number of entries below which we shrink the table by half */
60 : #define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
61 :
62 : /*
63 : ** Stubs for default hash allocator ops.
64 : */
65 : static void * PR_CALLBACK
66 23023 : DefaultAllocTable(void *pool, PRSize size)
67 : {
68 23023 : return PR_MALLOC(size);
69 : }
70 :
71 : static void PR_CALLBACK
72 22801 : DefaultFreeTable(void *pool, void *item)
73 : {
74 22801 : PR_Free(item);
75 22801 : }
76 :
77 : static PLHashEntry * PR_CALLBACK
78 263909 : DefaultAllocEntry(void *pool, const void *key)
79 : {
80 263909 : return PR_NEW(PLHashEntry);
81 : }
82 :
83 : static void PR_CALLBACK
84 268528 : DefaultFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
85 : {
86 268528 : if (flag == HT_FREE_ENTRY)
87 263848 : PR_Free(he);
88 268528 : }
89 :
90 : static PLHashAllocOps defaultHashAllocOps = {
91 : DefaultAllocTable, DefaultFreeTable,
92 : DefaultAllocEntry, DefaultFreeEntry
93 : };
94 :
95 : PR_IMPLEMENT(PLHashTable *)
96 19085 : PL_NewHashTable(PRUint32 n, PLHashFunction keyHash,
97 : PLHashComparator keyCompare, PLHashComparator valueCompare,
98 : const PLHashAllocOps *allocOps, void *allocPriv)
99 : {
100 : PLHashTable *ht;
101 : PRSize nb;
102 :
103 19085 : if (n <= MINBUCKETS) {
104 8069 : n = MINBUCKETSLOG2;
105 : } else {
106 11016 : n = PR_CeilingLog2(n);
107 11016 : if ((PRInt32)n < 0)
108 0 : return 0;
109 : }
110 :
111 19085 : if (!allocOps) allocOps = &defaultHashAllocOps;
112 :
113 19085 : ht = (PLHashTable*)((*allocOps->allocTable)(allocPriv, sizeof *ht));
114 19085 : if (!ht)
115 0 : return 0;
116 19085 : memset(ht, 0, sizeof *ht);
117 19085 : ht->shift = PL_HASH_BITS - n;
118 19085 : n = 1 << n;
119 19085 : nb = n * sizeof(PLHashEntry *);
120 19085 : ht->buckets = (PLHashEntry**)((*allocOps->allocTable)(allocPriv, nb));
121 19085 : if (!ht->buckets) {
122 0 : (*allocOps->freeTable)(allocPriv, ht);
123 0 : return 0;
124 : }
125 19085 : memset(ht->buckets, 0, nb);
126 :
127 19085 : ht->keyHash = keyHash;
128 19085 : ht->keyCompare = keyCompare;
129 19085 : ht->valueCompare = valueCompare;
130 19085 : ht->allocOps = allocOps;
131 19085 : ht->allocPriv = allocPriv;
132 19085 : return ht;
133 : }
134 :
135 : PR_IMPLEMENT(void)
136 18967 : PL_HashTableDestroy(PLHashTable *ht)
137 : {
138 : PRUint32 i, n;
139 : PLHashEntry *he, *next;
140 18967 : const PLHashAllocOps *allocOps = ht->allocOps;
141 18967 : void *allocPriv = ht->allocPriv;
142 :
143 18967 : n = NBUCKETS(ht);
144 1679655 : for (i = 0; i < n; i++) {
145 2384794 : for (he = ht->buckets[i]; he; he = next) {
146 724106 : next = he->next;
147 724106 : (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
148 : }
149 : }
150 : #ifdef DEBUG
151 18967 : memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
152 : #endif
153 18967 : (*allocOps->freeTable)(allocPriv, ht->buckets);
154 : #ifdef DEBUG
155 18967 : memset(ht, 0xDB, sizeof *ht);
156 : #endif
157 18967 : (*allocOps->freeTable)(allocPriv, ht);
158 18967 : }
159 :
160 : /*
161 : ** Multiplicative hash, from Knuth 6.4.
162 : */
163 : #define GOLDEN_RATIO 0x9E3779B9U /* 2/(1+sqrt(5))*(2^32) */
164 :
165 : PR_IMPLEMENT(PLHashEntry **)
166 238823810 : PL_HashTableRawLookup(PLHashTable *ht, PLHashNumber keyHash, const void *key)
167 : {
168 : PLHashEntry *he, **hep, **hep0;
169 : PLHashNumber h;
170 :
171 : #ifdef HASHMETER
172 : ht->nlookups++;
173 : #endif
174 238823810 : h = keyHash * GOLDEN_RATIO;
175 238823810 : h >>= ht->shift;
176 238823810 : hep = hep0 = &ht->buckets[h];
177 479485423 : while ((he = *hep) != 0) {
178 238937017 : if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
179 : /* Move to front of chain if not already there */
180 237098324 : if (hep != hep0) {
181 1117386 : *hep = he->next;
182 1117386 : he->next = *hep0;
183 1117386 : *hep0 = he;
184 : }
185 237098324 : return hep0;
186 : }
187 1837803 : hep = &he->next;
188 : #ifdef HASHMETER
189 : ht->nsteps++;
190 : #endif
191 : }
192 1724596 : return hep;
193 : }
194 :
195 : /*
196 : ** Same as PL_HashTableRawLookup but doesn't reorder the hash entries.
197 : */
198 : PR_IMPLEMENT(PLHashEntry **)
199 406316 : PL_HashTableRawLookupConst(PLHashTable *ht, PLHashNumber keyHash,
200 : const void *key)
201 : {
202 : PLHashEntry *he, **hep;
203 : PLHashNumber h;
204 :
205 : #ifdef HASHMETER
206 : ht->nlookups++;
207 : #endif
208 406316 : h = keyHash * GOLDEN_RATIO;
209 406316 : h >>= ht->shift;
210 406316 : hep = &ht->buckets[h];
211 837918 : while ((he = *hep) != 0) {
212 425158 : if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
213 399872 : break;
214 : }
215 25286 : hep = &he->next;
216 : #ifdef HASHMETER
217 : ht->nsteps++;
218 : #endif
219 : }
220 406316 : return hep;
221 : }
222 :
223 : PR_IMPLEMENT(PLHashEntry *)
224 748607 : PL_HashTableRawAdd(PLHashTable *ht, PLHashEntry **hep,
225 : PLHashNumber keyHash, const void *key, void *value)
226 : {
227 : PRUint32 i, n;
228 : PLHashEntry *he, *next, **oldbuckets;
229 : PRSize nb;
230 :
231 : /* Grow the table if it is overloaded */
232 748607 : n = NBUCKETS(ht);
233 748607 : if (ht->nentries >= OVERLOADED(n)) {
234 7400 : oldbuckets = ht->buckets;
235 7400 : nb = 2 * n * sizeof(PLHashEntry *);
236 7400 : ht->buckets = (PLHashEntry**)
237 7400 : ((*ht->allocOps->allocTable)(ht->allocPriv, nb));
238 7400 : if (!ht->buckets) {
239 0 : ht->buckets = oldbuckets;
240 0 : return 0;
241 : }
242 7400 : memset(ht->buckets, 0, nb);
243 : #ifdef HASHMETER
244 : ht->ngrows++;
245 : #endif
246 7400 : ht->shift--;
247 :
248 726120 : for (i = 0; i < n; i++) {
249 1347600 : for (he = oldbuckets[i]; he; he = next) {
250 628880 : next = he->next;
251 628880 : hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
252 628880 : PR_ASSERT(*hep == 0);
253 628880 : he->next = 0;
254 628880 : *hep = he;
255 : }
256 : }
257 : #ifdef DEBUG
258 7400 : memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
259 : #endif
260 7400 : (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
261 7400 : hep = PL_HashTableRawLookup(ht, keyHash, key);
262 : }
263 :
264 : /* Make a new key value entry */
265 748607 : he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);
266 748607 : if (!he)
267 0 : return 0;
268 748607 : he->keyHash = keyHash;
269 748607 : he->key = key;
270 748607 : he->value = value;
271 748607 : he->next = *hep;
272 748607 : *hep = he;
273 748607 : ht->nentries++;
274 748607 : return he;
275 : }
276 :
277 : PR_IMPLEMENT(PLHashEntry *)
278 753287 : PL_HashTableAdd(PLHashTable *ht, const void *key, void *value)
279 : {
280 : PLHashNumber keyHash;
281 : PLHashEntry *he, **hep;
282 :
283 753287 : keyHash = (*ht->keyHash)(key);
284 753287 : hep = PL_HashTableRawLookup(ht, keyHash, key);
285 753287 : if ((he = *hep) != 0) {
286 : /* Hit; see if values match */
287 4680 : if ((*ht->valueCompare)(he->value, value)) {
288 : /* key,value pair is already present in table */
289 0 : return he;
290 : }
291 4680 : if (he->value)
292 4680 : (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);
293 4680 : he->value = value;
294 4680 : return he;
295 : }
296 748607 : return PL_HashTableRawAdd(ht, hep, keyHash, key, value);
297 : }
298 :
299 : PR_IMPLEMENT(void)
300 24416 : PL_HashTableRawRemove(PLHashTable *ht, PLHashEntry **hep, PLHashEntry *he)
301 : {
302 : PRUint32 i, n;
303 : PLHashEntry *next, **oldbuckets;
304 : PRSize nb;
305 :
306 24416 : *hep = he->next;
307 24416 : (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);
308 :
309 : /* Shrink table if it's underloaded */
310 24416 : n = NBUCKETS(ht);
311 24416 : if (--ht->nentries < UNDERLOADED(n)) {
312 2929 : oldbuckets = ht->buckets;
313 2929 : nb = n * sizeof(PLHashEntry*) / 2;
314 2929 : ht->buckets = (PLHashEntry**)(
315 2929 : (*ht->allocOps->allocTable)(ht->allocPriv, nb));
316 2929 : if (!ht->buckets) {
317 0 : ht->buckets = oldbuckets;
318 0 : return;
319 : }
320 2929 : memset(ht->buckets, 0, nb);
321 : #ifdef HASHMETER
322 : ht->nshrinks++;
323 : #endif
324 2929 : ht->shift++;
325 :
326 142449 : for (i = 0; i < n; i++) {
327 147090 : for (he = oldbuckets[i]; he; he = next) {
328 7570 : next = he->next;
329 7570 : hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
330 7570 : PR_ASSERT(*hep == 0);
331 7570 : he->next = 0;
332 7570 : *hep = he;
333 : }
334 : }
335 : #ifdef DEBUG
336 2929 : memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
337 : #endif
338 2929 : (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
339 : }
340 : }
341 :
342 : PR_IMPLEMENT(PRBool)
343 23410 : PL_HashTableRemove(PLHashTable *ht, const void *key)
344 : {
345 : PLHashNumber keyHash;
346 : PLHashEntry *he, **hep;
347 :
348 23410 : keyHash = (*ht->keyHash)(key);
349 23410 : hep = PL_HashTableRawLookup(ht, keyHash, key);
350 23410 : if ((he = *hep) == 0)
351 0 : return PR_FALSE;
352 :
353 : /* Hit; remove element */
354 23410 : PL_HashTableRawRemove(ht, hep, he);
355 23410 : return PR_TRUE;
356 : }
357 :
358 : PR_IMPLEMENT(void *)
359 207856821 : PL_HashTableLookup(PLHashTable *ht, const void *key)
360 : {
361 : PLHashNumber keyHash;
362 : PLHashEntry *he, **hep;
363 :
364 207856821 : keyHash = (*ht->keyHash)(key);
365 207856821 : hep = PL_HashTableRawLookup(ht, keyHash, key);
366 207856821 : if ((he = *hep) != 0) {
367 207524692 : return he->value;
368 : }
369 332129 : return 0;
370 : }
371 :
372 : /*
373 : ** Same as PL_HashTableLookup but doesn't reorder the hash entries.
374 : */
375 : PR_IMPLEMENT(void *)
376 406316 : PL_HashTableLookupConst(PLHashTable *ht, const void *key)
377 : {
378 : PLHashNumber keyHash;
379 : PLHashEntry *he, **hep;
380 :
381 406316 : keyHash = (*ht->keyHash)(key);
382 406316 : hep = PL_HashTableRawLookupConst(ht, keyHash, key);
383 406316 : if ((he = *hep) != 0) {
384 399872 : return he->value;
385 : }
386 6444 : return 0;
387 : }
388 :
389 : /*
390 : ** Iterate over the entries in the hash table calling func for each
391 : ** entry found. Stop if "f" says to (return value & PR_ENUMERATE_STOP).
392 : ** Return a count of the number of elements scanned.
393 : */
394 : PR_IMPLEMENT(int)
395 9339 : PL_HashTableEnumerateEntries(PLHashTable *ht, PLHashEnumerator f, void *arg)
396 : {
397 : PLHashEntry *he, **hep;
398 : PRUint32 i, nbuckets;
399 9339 : int rv, n = 0;
400 9339 : PLHashEntry *todo = 0;
401 :
402 9339 : nbuckets = NBUCKETS(ht);
403 1203371 : for (i = 0; i < nbuckets; i++) {
404 1194032 : hep = &ht->buckets[i];
405 2990124 : while ((he = *hep) != 0) {
406 602060 : rv = (*f)(he, n, arg);
407 602060 : n++;
408 602060 : if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) {
409 996 : *hep = he->next;
410 996 : if (rv & HT_ENUMERATE_REMOVE) {
411 996 : he->next = todo;
412 996 : todo = he;
413 : }
414 : } else {
415 601064 : hep = &he->next;
416 : }
417 602060 : if (rv & HT_ENUMERATE_STOP) {
418 0 : goto out;
419 : }
420 : }
421 : }
422 :
423 : out:
424 9339 : hep = &todo;
425 19674 : while ((he = *hep) != 0) {
426 996 : PL_HashTableRawRemove(ht, hep, he);
427 : }
428 9339 : return n;
429 : }
430 :
431 : #ifdef HASHMETER
432 : #include <math.h>
433 : #include <stdio.h>
434 :
435 : PR_IMPLEMENT(void)
436 : PL_HashTableDumpMeter(PLHashTable *ht, PLHashEnumerator dump, FILE *fp)
437 : {
438 : double mean, variance;
439 : PRUint32 nchains, nbuckets;
440 : PRUint32 i, n, maxChain, maxChainLen;
441 : PLHashEntry *he;
442 :
443 : variance = 0;
444 : nchains = 0;
445 : maxChainLen = 0;
446 : nbuckets = NBUCKETS(ht);
447 : for (i = 0; i < nbuckets; i++) {
448 : he = ht->buckets[i];
449 : if (!he)
450 : continue;
451 : nchains++;
452 : for (n = 0; he; he = he->next)
453 : n++;
454 : variance += n * n;
455 : if (n > maxChainLen) {
456 : maxChainLen = n;
457 : maxChain = i;
458 : }
459 : }
460 : mean = (double)ht->nentries / nchains;
461 : variance = fabs(variance / nchains - mean * mean);
462 :
463 : fprintf(fp, "\nHash table statistics:\n");
464 : fprintf(fp, " number of lookups: %u\n", ht->nlookups);
465 : fprintf(fp, " number of entries: %u\n", ht->nentries);
466 : fprintf(fp, " number of grows: %u\n", ht->ngrows);
467 : fprintf(fp, " number of shrinks: %u\n", ht->nshrinks);
468 : fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps
469 : / ht->nlookups);
470 : fprintf(fp, "mean hash chain length: %g\n", mean);
471 : fprintf(fp, " standard deviation: %g\n", sqrt(variance));
472 : fprintf(fp, " max hash chain length: %u\n", maxChainLen);
473 : fprintf(fp, " max hash chain: [%u]\n", maxChain);
474 :
475 : for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)
476 : if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT)
477 : break;
478 : }
479 : #endif /* HASHMETER */
480 :
481 : PR_IMPLEMENT(int)
482 0 : PL_HashTableDump(PLHashTable *ht, PLHashEnumerator dump, FILE *fp)
483 : {
484 : int count;
485 :
486 0 : count = PL_HashTableEnumerateEntries(ht, dump, fp);
487 : #ifdef HASHMETER
488 : PL_HashTableDumpMeter(ht, dump, fp);
489 : #endif
490 0 : return count;
491 : }
492 :
493 : PR_IMPLEMENT(PLHashNumber)
494 208055584 : PL_HashString(const void *key)
495 : {
496 : PLHashNumber h;
497 : const PRUint8 *s;
498 :
499 208055584 : h = 0;
500 -1113351470 : for (s = (const PRUint8*)key; *s; s++)
501 -1321407054 : h = PR_ROTATE_LEFT32(h, 4) ^ *s;
502 208055584 : return h;
503 : }
504 :
505 : PR_IMPLEMENT(int)
506 207454161 : PL_CompareStrings(const void *v1, const void *v2)
507 : {
508 207454161 : return strcmp((const char*)v1, (const char*)v2) == 0;
509 : }
510 :
511 : PR_IMPLEMENT(int)
512 29809668 : PL_CompareValues(const void *v1, const void *v2)
513 : {
514 29809668 : return v1 == v2;
515 : }
|