1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 * The contents of this file are subject to the Mozilla Public
4 * License Version 1.1 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of
6 * the License at http://www.mozilla.org/MPL/
8 * Software distributed under the License is distributed on an "AS
9 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10 * implied. See the License for the specific language governing
11 * rights and limitations under the License.
13 * The Original Code is the Netscape Portable Runtime (NSPR).
15 * The Initial Developer of the Original Code is Netscape
16 * Communications Corporation. Portions created by Netscape are
17 * Copyright (C) 1998-2000 Netscape Communications Corporation. All
22 * Alternatively, the contents of this file may be used under the
23 * terms of the GNU General Public License Version 2 or later (the
24 * "GPL"), in which case the provisions of the GPL are applicable
25 * instead of those above. If you wish to allow use of your
26 * version of this file only under the terms of the GPL and not to
27 * allow others to use your version of this file under the MPL,
28 * indicate your decision by deleting the provisions above and
29 * replace them with the notice and other provisions required by
30 * the GPL. If you do not delete the provisions above, a recipient
31 * may use your version of this file under either the MPL or the
36 * PL hash table package.
39 #include <security_asn1/prbit.h>
40 #include <security_asn1/prlog.h>
41 #include <security_asn1/prmem.h>
42 #include <security_asn1/prtypes.h>
46 /* Compute the number of buckets in ht */
47 #define NBUCKETS(ht) (1 << (PL_HASH_BITS - (ht)->shift))
49 /* The smallest table has 16 buckets */
50 #define MINBUCKETSLOG2 4
51 #define MINBUCKETS (1 << MINBUCKETSLOG2)
53 /* Compute the maximum entries given n buckets that we will tolerate, ~90% */
54 #define OVERLOADED(n) ((n) - ((n) >> 3))
56 /* Compute the number of entries below which we shrink the table by half */
57 #define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
60 ** Stubs for default hash allocator ops.
62 static void * PR_CALLBACK
63 DefaultAllocTable(void *pool
, PRSize size
)
69 return PR_MALLOC(size
);
72 static void PR_CALLBACK
73 DefaultFreeTable(void *pool
, void *item
)
82 static PLHashEntry
* PR_CALLBACK
83 DefaultAllocEntry(void *pool
, const void *key
)
86 #pragma unused (pool,key)
89 return PR_NEW(PLHashEntry
);
92 static void PR_CALLBACK
93 DefaultFreeEntry(void *pool
, PLHashEntry
*he
, PRUintn flag
)
99 if (flag
== HT_FREE_ENTRY
)
103 static PLHashAllocOps defaultHashAllocOps
= {
104 DefaultAllocTable
, DefaultFreeTable
,
105 DefaultAllocEntry
, DefaultFreeEntry
108 PR_IMPLEMENT(PLHashTable
*)
109 PL_NewHashTable(PRUint32 n
, PLHashFunction keyHash
,
110 PLHashComparator keyCompare
, PLHashComparator valueCompare
,
111 const PLHashAllocOps
*allocOps
, void *allocPriv
)
116 if (n
<= MINBUCKETS
) {
119 n
= PR_CeilingLog2(n
);
124 if (!allocOps
) allocOps
= &defaultHashAllocOps
;
126 ht
= (PLHashTable
*)((*allocOps
->allocTable
)(allocPriv
, sizeof *ht
));
129 memset(ht
, 0, sizeof *ht
);
130 ht
->shift
= PL_HASH_BITS
- n
;
134 (*allocOps
->freeTable
)(allocPriv
, ht
);
138 nb
= n
* sizeof(PLHashEntry
*);
139 ht
->buckets
= (PLHashEntry
**)((*allocOps
->allocTable
)(allocPriv
, nb
));
141 (*allocOps
->freeTable
)(allocPriv
, ht
);
144 memset(ht
->buckets
, 0, nb
);
146 ht
->keyHash
= keyHash
;
147 ht
->keyCompare
= keyCompare
;
148 ht
->valueCompare
= valueCompare
;
149 ht
->allocOps
= allocOps
;
150 ht
->allocPriv
= allocPriv
;
155 PL_HashTableDestroy(PLHashTable
*ht
)
158 PLHashEntry
*he
, *next
;
159 const PLHashAllocOps
*allocOps
= ht
->allocOps
;
160 void *allocPriv
= ht
->allocPriv
;
163 for (i
= 0; i
< n
; i
++) {
164 for (he
= ht
->buckets
[i
]; he
; he
= next
) {
166 (*allocOps
->freeEntry
)(allocPriv
, he
, HT_FREE_ENTRY
);
170 memset(ht
->buckets
, 0xDB, n
* sizeof ht
->buckets
[0]);
172 (*allocOps
->freeTable
)(allocPriv
, ht
->buckets
);
174 memset(ht
, 0xDB, sizeof *ht
);
176 (*allocOps
->freeTable
)(allocPriv
, ht
);
180 ** Multiplicative hash, from Knuth 6.4.
182 #define GOLDEN_RATIO 0x9E3779B9U
184 PR_IMPLEMENT(PLHashEntry
**)
185 PL_HashTableRawLookup(PLHashTable
*ht
, PLHashNumber keyHash
, const void *key
)
187 PLHashEntry
*he
, **hep
, **hep0
;
193 h
= keyHash
* GOLDEN_RATIO
;
195 hep
= hep0
= &ht
->buckets
[h
];
196 while ((he
= *hep
) != 0) {
197 if (he
->keyHash
== keyHash
&& (*ht
->keyCompare
)(key
, he
->key
)) {
198 /* Move to front of chain if not already there */
215 ** Same as PL_HashTableRawLookup but doesn't reorder the hash entries.
217 PR_IMPLEMENT(PLHashEntry
**)
218 PL_HashTableRawLookupConst(PLHashTable
*ht
, PLHashNumber keyHash
,
221 PLHashEntry
*he
, **hep
;
227 h
= keyHash
* GOLDEN_RATIO
;
229 hep
= &ht
->buckets
[h
];
230 while ((he
= *hep
) != 0) {
231 if (he
->keyHash
== keyHash
&& (*ht
->keyCompare
)(key
, he
->key
)) {
242 PR_IMPLEMENT(PLHashEntry
*)
243 PL_HashTableRawAdd(PLHashTable
*ht
, PLHashEntry
**hep
,
244 PLHashNumber keyHash
, const void *key
, void *value
)
247 PLHashEntry
*he
, *next
, **oldbuckets
;
250 /* Grow the table if it is overloaded */
252 if (ht
->nentries
>= OVERLOADED(n
)) {
253 oldbuckets
= ht
->buckets
;
258 nb
= 2 * n
* sizeof(PLHashEntry
*);
259 ht
->buckets
= (PLHashEntry
**)
260 ((*ht
->allocOps
->allocTable
)(ht
->allocPriv
, nb
));
262 ht
->buckets
= oldbuckets
;
265 memset(ht
->buckets
, 0, nb
);
271 for (i
= 0; i
< n
; i
++) {
272 for (he
= oldbuckets
[i
]; he
; he
= next
) {
274 hep
= PL_HashTableRawLookup(ht
, he
->keyHash
, he
->key
);
275 PR_ASSERT(*hep
== 0);
281 memset(oldbuckets
, 0xDB, n
* sizeof oldbuckets
[0]);
283 (*ht
->allocOps
->freeTable
)(ht
->allocPriv
, oldbuckets
);
284 hep
= PL_HashTableRawLookup(ht
, keyHash
, key
);
287 /* Make a new key value entry */
288 he
= (*ht
->allocOps
->allocEntry
)(ht
->allocPriv
, key
);
291 he
->keyHash
= keyHash
;
300 PR_IMPLEMENT(PLHashEntry
*)
301 PL_HashTableAdd(PLHashTable
*ht
, const void *key
, void *value
)
303 PLHashNumber keyHash
;
304 PLHashEntry
*he
, **hep
;
306 keyHash
= (*ht
->keyHash
)(key
);
307 hep
= PL_HashTableRawLookup(ht
, keyHash
, key
);
308 if ((he
= *hep
) != 0) {
309 /* Hit; see if values match */
310 if ((*ht
->valueCompare
)(he
->value
, value
)) {
311 /* key,value pair is already present in table */
315 (*ht
->allocOps
->freeEntry
)(ht
->allocPriv
, he
, HT_FREE_VALUE
);
319 return PL_HashTableRawAdd(ht
, hep
, keyHash
, key
, value
);
323 PL_HashTableRawRemove(PLHashTable
*ht
, PLHashEntry
**hep
, PLHashEntry
*he
)
326 PLHashEntry
*next
, **oldbuckets
;
330 (*ht
->allocOps
->freeEntry
)(ht
->allocPriv
, he
, HT_FREE_ENTRY
);
332 /* Shrink table if it's underloaded */
334 if (--ht
->nentries
< UNDERLOADED(n
)) {
335 oldbuckets
= ht
->buckets
;
336 nb
= n
* sizeof(PLHashEntry
*) / 2;
337 ht
->buckets
= (PLHashEntry
**)(
338 (*ht
->allocOps
->allocTable
)(ht
->allocPriv
, nb
));
340 ht
->buckets
= oldbuckets
;
343 memset(ht
->buckets
, 0, nb
);
349 for (i
= 0; i
< n
; i
++) {
350 for (he
= oldbuckets
[i
]; he
; he
= next
) {
352 hep
= PL_HashTableRawLookup(ht
, he
->keyHash
, he
->key
);
353 PR_ASSERT(*hep
== 0);
359 memset(oldbuckets
, 0xDB, n
* sizeof oldbuckets
[0]);
361 (*ht
->allocOps
->freeTable
)(ht
->allocPriv
, oldbuckets
);
365 PR_IMPLEMENT(unsigned char)
366 PL_HashTableRemove(PLHashTable
*ht
, const void *key
)
368 PLHashNumber keyHash
;
369 PLHashEntry
*he
, **hep
;
371 keyHash
= (*ht
->keyHash
)(key
);
372 hep
= PL_HashTableRawLookup(ht
, keyHash
, key
);
373 if ((he
= *hep
) == 0)
376 /* Hit; remove element */
377 PL_HashTableRawRemove(ht
, hep
, he
);
382 PL_HashTableLookup(PLHashTable
*ht
, const void *key
)
384 PLHashNumber keyHash
;
385 PLHashEntry
*he
, **hep
;
387 keyHash
= (*ht
->keyHash
)(key
);
388 hep
= PL_HashTableRawLookup(ht
, keyHash
, key
);
389 if ((he
= *hep
) != 0) {
396 ** Same as PL_HashTableLookup but doesn't reorder the hash entries.
399 PL_HashTableLookupConst(PLHashTable
*ht
, const void *key
)
401 PLHashNumber keyHash
;
402 PLHashEntry
*he
, **hep
;
404 keyHash
= (*ht
->keyHash
)(key
);
405 hep
= PL_HashTableRawLookupConst(ht
, keyHash
, key
);
406 if ((he
= *hep
) != 0) {
413 ** Iterate over the entries in the hash table calling func for each
414 ** entry found. Stop if "f" says to (return value & PR_ENUMERATE_STOP).
415 ** Return a count of the number of elements scanned.
418 PL_HashTableEnumerateEntries(PLHashTable
*ht
, PLHashEnumerator f
, void *arg
)
420 PLHashEntry
*he
, **hep
;
421 PRUint32 i
, nbuckets
;
423 PLHashEntry
*todo
= 0;
425 nbuckets
= NBUCKETS(ht
);
426 for (i
= 0; i
< nbuckets
; i
++) {
427 hep
= &ht
->buckets
[i
];
428 while ((he
= *hep
) != 0) {
429 rv
= (*f
)(he
, n
, arg
);
431 if (rv
& (HT_ENUMERATE_REMOVE
| HT_ENUMERATE_UNHASH
)) {
433 if (rv
& HT_ENUMERATE_REMOVE
) {
440 if (rv
& HT_ENUMERATE_STOP
) {
448 while ((he
= *hep
) != 0) {
449 PL_HashTableRawRemove(ht
, hep
, he
);
459 PL_HashTableDumpMeter(PLHashTable
*ht
, PLHashEnumerator dump
, FILE *fp
)
461 double mean
, variance
;
462 PRUint32 nchains
, nbuckets
;
463 PRUint32 i
, n
, maxChain
, maxChainLen
;
469 nbuckets
= NBUCKETS(ht
);
470 for (i
= 0; i
< nbuckets
; i
++) {
475 for (n
= 0; he
; he
= he
->next
)
478 if (n
> maxChainLen
) {
483 mean
= (double)ht
->nentries
/ nchains
;
484 variance
= fabs(variance
/ nchains
- mean
* mean
);
486 fprintf(fp
, "\nHash table statistics:\n");
487 fprintf(fp
, " number of lookups: %u\n", ht
->nlookups
);
488 fprintf(fp
, " number of entries: %u\n", ht
->nentries
);
489 fprintf(fp
, " number of grows: %u\n", ht
->ngrows
);
490 fprintf(fp
, " number of shrinks: %u\n", ht
->nshrinks
);
491 fprintf(fp
, " mean steps per hash: %g\n", (double)ht
->nsteps
493 fprintf(fp
, "mean hash chain length: %g\n", mean
);
494 fprintf(fp
, " standard deviation: %g\n", sqrt(variance
));
495 fprintf(fp
, " max hash chain length: %u\n", maxChainLen
);
496 fprintf(fp
, " max hash chain: [%u]\n", maxChain
);
498 for (he
= ht
->buckets
[maxChain
], i
= 0; he
; he
= he
->next
, i
++)
499 if ((*dump
)(he
, i
, fp
) != HT_ENUMERATE_NEXT
)
502 #endif /* HASHMETER */
505 PL_HashTableDump(PLHashTable
*ht
, PLHashEnumerator dump
, FILE *fp
)
509 count
= PL_HashTableEnumerateEntries(ht
, dump
, fp
);
511 PL_HashTableDumpMeter(ht
, dump
, fp
);
516 PR_IMPLEMENT(PLHashNumber
)
517 PL_HashString(const void *key
)
523 for (s
= (const PRUint8
*)key
; *s
; s
++)
524 h
= (h
>> 28) ^ (h
<< 4) ^ *s
;
529 PL_CompareStrings(const void *v1
, const void *v2
)
531 return strcmp((const char*)v1
, (const char*)v2
) == 0;
535 PL_CompareValues(const void *v1
, const void *v2
)