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 22897 : DefaultAllocTable(void *pool, PRSize size)
67 : {
68 22897 : return PR_MALLOC(size);
69 : }
70 :
71 : static void PR_CALLBACK
72 22675 : DefaultFreeTable(void *pool, void *item)
73 : {
74 22675 : PR_Free(item);
75 22675 : }
76 :
77 : static PLHashEntry * PR_CALLBACK
78 262676 : DefaultAllocEntry(void *pool, const void *key)
79 : {
80 262676 : return PR_NEW(PLHashEntry);
81 : }
82 :
83 : static void PR_CALLBACK
84 267285 : DefaultFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
85 : {
86 267285 : if (flag == HT_FREE_ENTRY)
87 262615 : PR_Free(he);
88 267285 : }
89 :
90 : static PLHashAllocOps defaultHashAllocOps = {
91 : DefaultAllocTable, DefaultFreeTable,
92 : DefaultAllocEntry, DefaultFreeEntry
93 : };
94 :
95 : PR_IMPLEMENT(PLHashTable *)
96 18903 : 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 18903 : if (n <= MINBUCKETS) {
104 8049 : n = MINBUCKETSLOG2;
105 : } else {
106 10854 : n = PR_CeilingLog2(n);
107 10854 : if ((PRInt32)n < 0)
108 0 : return 0;
109 : }
110 :
111 18903 : if (!allocOps) allocOps = &defaultHashAllocOps;
112 :
113 18903 : ht = (PLHashTable*)((*allocOps->allocTable)(allocPriv, sizeof *ht));
114 18903 : if (!ht)
115 0 : return 0;
116 18903 : memset(ht, 0, sizeof *ht);
117 18903 : ht->shift = PL_HASH_BITS - n;
118 18903 : n = 1 << n;
119 18903 : nb = n * sizeof(PLHashEntry *);
120 18903 : ht->buckets = (PLHashEntry**)((*allocOps->allocTable)(allocPriv, nb));
121 18903 : if (!ht->buckets) {
122 0 : (*allocOps->freeTable)(allocPriv, ht);
123 0 : return 0;
124 : }
125 18903 : memset(ht->buckets, 0, nb);
126 :
127 18903 : ht->keyHash = keyHash;
128 18903 : ht->keyCompare = keyCompare;
129 18903 : ht->valueCompare = valueCompare;
130 18903 : ht->allocOps = allocOps;
131 18903 : ht->allocPriv = allocPriv;
132 18903 : return ht;
133 : }
134 :
135 : PR_IMPLEMENT(void)
136 18790 : PL_HashTableDestroy(PLHashTable *ht)
137 : {
138 : PRUint32 i, n;
139 : PLHashEntry *he, *next;
140 18790 : const PLHashAllocOps *allocOps = ht->allocOps;
141 18790 : void *allocPriv = ht->allocPriv;
142 :
143 18790 : n = NBUCKETS(ht);
144 1661830 : for (i = 0; i < n; i++) {
145 2358999 : for (he = ht->buckets[i]; he; he = next) {
146 715959 : next = he->next;
147 715959 : (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
148 : }
149 : }
150 : #ifdef DEBUG
151 18790 : memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
152 : #endif
153 18790 : (*allocOps->freeTable)(allocPriv, ht->buckets);
154 : #ifdef DEBUG
155 18790 : memset(ht, 0xDB, sizeof *ht);
156 : #endif
157 18790 : (*allocOps->freeTable)(allocPriv, ht);
158 18790 : }
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 242870925 : 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 242870925 : h = keyHash * GOLDEN_RATIO;
175 242870925 : h >>= ht->shift;
176 242870925 : hep = hep0 = &ht->buckets[h];
177 487673214 : while ((he = *hep) != 0) {
178 243094027 : if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
179 : /* Move to front of chain if not already there */
180 241162663 : if (hep != hep0) {
181 1232276 : *hep = he->next;
182 1232276 : he->next = *hep0;
183 1232276 : *hep0 = he;
184 : }
185 241162663 : return hep0;
186 : }
187 1931364 : hep = &he->next;
188 : #ifdef HASHMETER
189 : ht->nsteps++;
190 : #endif
191 : }
192 1708262 : return hep;
193 : }
194 :
195 : /*
196 : ** Same as PL_HashTableRawLookup but doesn't reorder the hash entries.
197 : */
198 : PR_IMPLEMENT(PLHashEntry **)
199 409291 : 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 409291 : h = keyHash * GOLDEN_RATIO;
209 409291 : h >>= ht->shift;
210 409291 : hep = &ht->buckets[h];
211 845716 : while ((he = *hep) != 0) {
212 429993 : if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
213 402859 : break;
214 : }
215 27134 : hep = &he->next;
216 : #ifdef HASHMETER
217 : ht->nsteps++;
218 : #endif
219 : }
220 409291 : return hep;
221 : }
222 :
223 : PR_IMPLEMENT(PLHashEntry *)
224 740308 : 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 740308 : n = NBUCKETS(ht);
233 740308 : if (ht->nentries >= OVERLOADED(n)) {
234 7321 : oldbuckets = ht->buckets;
235 7321 : nb = 2 * n * sizeof(PLHashEntry *);
236 7321 : ht->buckets = (PLHashEntry**)
237 7321 : ((*ht->allocOps->allocTable)(ht->allocPriv, nb));
238 7321 : if (!ht->buckets) {
239 0 : ht->buckets = oldbuckets;
240 0 : return 0;
241 : }
242 7321 : memset(ht->buckets, 0, nb);
243 : #ifdef HASHMETER
244 : ht->ngrows++;
245 : #endif
246 7321 : ht->shift--;
247 :
248 720697 : for (i = 0; i < n; i++) {
249 1337580 : for (he = oldbuckets[i]; he; he = next) {
250 624204 : next = he->next;
251 624204 : hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
252 624204 : PR_ASSERT(*hep == 0);
253 624204 : he->next = 0;
254 624204 : *hep = he;
255 : }
256 : }
257 : #ifdef DEBUG
258 7321 : memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
259 : #endif
260 7321 : (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
261 7321 : hep = PL_HashTableRawLookup(ht, keyHash, key);
262 : }
263 :
264 : /* Make a new key value entry */
265 740308 : he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);
266 740308 : if (!he)
267 0 : return 0;
268 740308 : he->keyHash = keyHash;
269 740308 : he->key = key;
270 740308 : he->value = value;
271 740308 : he->next = *hep;
272 740308 : *hep = he;
273 740308 : ht->nentries++;
274 740308 : return he;
275 : }
276 :
277 : PR_IMPLEMENT(PLHashEntry *)
278 744978 : PL_HashTableAdd(PLHashTable *ht, const void *key, void *value)
279 : {
280 : PLHashNumber keyHash;
281 : PLHashEntry *he, **hep;
282 :
283 744978 : keyHash = (*ht->keyHash)(key);
284 744978 : hep = PL_HashTableRawLookup(ht, keyHash, key);
285 744978 : if ((he = *hep) != 0) {
286 : /* Hit; see if values match */
287 4670 : if ((*ht->valueCompare)(he->value, value)) {
288 : /* key,value pair is already present in table */
289 0 : return he;
290 : }
291 4670 : if (he->value)
292 4670 : (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);
293 4670 : he->value = value;
294 4670 : return he;
295 : }
296 740308 : return PL_HashTableRawAdd(ht, hep, keyHash, key, value);
297 : }
298 :
299 : PR_IMPLEMENT(void)
300 24280 : PL_HashTableRawRemove(PLHashTable *ht, PLHashEntry **hep, PLHashEntry *he)
301 : {
302 : PRUint32 i, n;
303 : PLHashEntry *next, **oldbuckets;
304 : PRSize nb;
305 :
306 24280 : *hep = he->next;
307 24280 : (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);
308 :
309 : /* Shrink table if it's underloaded */
310 24280 : n = NBUCKETS(ht);
311 24280 : if (--ht->nentries < UNDERLOADED(n)) {
312 2905 : oldbuckets = ht->buckets;
313 2905 : nb = n * sizeof(PLHashEntry*) / 2;
314 2905 : ht->buckets = (PLHashEntry**)(
315 2905 : (*ht->allocOps->allocTable)(ht->allocPriv, nb));
316 2905 : if (!ht->buckets) {
317 0 : ht->buckets = oldbuckets;
318 0 : return;
319 : }
320 2905 : memset(ht->buckets, 0, nb);
321 : #ifdef HASHMETER
322 : ht->nshrinks++;
323 : #endif
324 2905 : ht->shift++;
325 :
326 141625 : for (i = 0; i < n; i++) {
327 146195 : for (he = oldbuckets[i]; he; he = next) {
328 7475 : next = he->next;
329 7475 : hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
330 7475 : PR_ASSERT(*hep == 0);
331 7475 : he->next = 0;
332 7475 : *hep = he;
333 : }
334 : }
335 : #ifdef DEBUG
336 2905 : memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
337 : #endif
338 2905 : (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
339 : }
340 : }
341 :
342 : PR_IMPLEMENT(PRBool)
343 23274 : PL_HashTableRemove(PLHashTable *ht, const void *key)
344 : {
345 : PLHashNumber keyHash;
346 : PLHashEntry *he, **hep;
347 :
348 23274 : keyHash = (*ht->keyHash)(key);
349 23274 : hep = PL_HashTableRawLookup(ht, keyHash, key);
350 23274 : if ((he = *hep) == 0)
351 0 : return PR_FALSE;
352 :
353 : /* Hit; remove element */
354 23274 : PL_HashTableRawRemove(ht, hep, he);
355 23274 : return PR_TRUE;
356 : }
357 :
358 : PR_IMPLEMENT(void *)
359 212629618 : PL_HashTableLookup(PLHashTable *ht, const void *key)
360 : {
361 : PLHashNumber keyHash;
362 : PLHashEntry *he, **hep;
363 :
364 212629618 : keyHash = (*ht->keyHash)(key);
365 212629618 : hep = PL_HashTableRawLookup(ht, keyHash, key);
366 212629618 : if ((he = *hep) != 0) {
367 212300674 : return he->value;
368 : }
369 328944 : return 0;
370 : }
371 :
372 : /*
373 : ** Same as PL_HashTableLookup but doesn't reorder the hash entries.
374 : */
375 : PR_IMPLEMENT(void *)
376 409291 : PL_HashTableLookupConst(PLHashTable *ht, const void *key)
377 : {
378 : PLHashNumber keyHash;
379 : PLHashEntry *he, **hep;
380 :
381 409291 : keyHash = (*ht->keyHash)(key);
382 409291 : hep = PL_HashTableRawLookupConst(ht, keyHash, key);
383 409291 : if ((he = *hep) != 0) {
384 402859 : return he->value;
385 : }
386 6432 : 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 9262 : PL_HashTableEnumerateEntries(PLHashTable *ht, PLHashEnumerator f, void *arg)
396 : {
397 : PLHashEntry *he, **hep;
398 : PRUint32 i, nbuckets;
399 9262 : int rv, n = 0;
400 9262 : PLHashEntry *todo = 0;
401 :
402 9262 : nbuckets = NBUCKETS(ht);
403 1192814 : for (i = 0; i < nbuckets; i++) {
404 1183552 : hep = &ht->buckets[i];
405 2962998 : while ((he = *hep) != 0) {
406 595894 : rv = (*f)(he, n, arg);
407 595894 : n++;
408 595894 : 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 594898 : hep = &he->next;
416 : }
417 595894 : if (rv & HT_ENUMERATE_STOP) {
418 0 : goto out;
419 : }
420 : }
421 : }
422 :
423 : out:
424 9262 : hep = &todo;
425 19520 : while ((he = *hep) != 0) {
426 996 : PL_HashTableRawRemove(ht, hep, he);
427 : }
428 9262 : 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 212825471 : PL_HashString(const void *key)
495 : {
496 : PLHashNumber h;
497 : const PRUint8 *s;
498 :
499 212825471 : h = 0;
500 -1040983207 : for (s = (const PRUint8*)key; *s; s++)
501 -1253808678 : h = PR_ROTATE_LEFT32(h, 4) ^ *s;
502 212825471 : return h;
503 : }
504 :
505 : PR_IMPLEMENT(int)
506 212230161 : PL_CompareStrings(const void *v1, const void *v2)
507 : {
508 212230161 : return strcmp((const char*)v1, (const char*)v2) == 0;
509 : }
510 :
511 : PR_IMPLEMENT(int)
512 29098803 : PL_CompareValues(const void *v1, const void *v2)
513 : {
514 29098803 : return v1 == v2;
515 : }
|