1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
16 * The Original Code is Mozilla Communicator client code, released
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.
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.
38 * ***** END LICENSE BLOCK ***** */
41 * This is a copy of the NSPR hash-table library, but it has been slightly
42 * modified to allow an additional argument to be passed into the hash
43 * key-comparison function. This is used to maintain thread-safety by
44 * passing in a JNIEnv pointer to the key-comparison function rather
45 * than storing it in a global. All types,function names, etc. have
46 * been renamed from their original NSPR names to protect the innocent.
58 /* Compute the number of buckets in ht */
59 #define NBUCKETS(ht) (1 << (JSJ_HASH_BITS - (ht)->shift))
61 /* The smallest table has 16 buckets */
62 #define MINBUCKETSLOG2 4
63 #define MINBUCKETS (1 << MINBUCKETSLOG2)
65 /* Compute the maximum entries given n buckets that we will tolerate, ~90% */
66 #define OVERLOADED(n) ((n) - ((n) >> 3))
68 /* Compute the number of entries below which we shrink the table by half */
69 #define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
72 ** Stubs for default hash allocator ops.
75 DefaultAllocTable(void *pool
, size_t size
)
81 DefaultFreeTable(void *pool
, void *item
)
87 DefaultAllocEntry(void *pool
, const void *key
)
89 return malloc(sizeof(JSJHashEntry
));
93 DefaultFreeEntry(void *pool
, JSJHashEntry
*he
, JSUintn flag
)
95 if (flag
== HT_FREE_ENTRY
)
99 static JSJHashAllocOps defaultHashAllocOps
= {
100 DefaultAllocTable
, DefaultFreeTable
,
101 DefaultAllocEntry
, DefaultFreeEntry
104 JS_EXPORT_API(JSJHashTable
*)
105 JSJ_NewHashTable(JSUint32 n
, JSJHashFunction keyHash
,
106 JSJHashComparator keyCompare
, JSJHashComparator valueCompare
,
107 JSJHashAllocOps
*allocOps
, void *allocPriv
)
112 if (n
<= MINBUCKETS
) {
115 n
= JS_CeilingLog2(n
);
120 if (!allocOps
) allocOps
= &defaultHashAllocOps
;
122 ht
= (*allocOps
->allocTable
)(allocPriv
, sizeof *ht
);
125 memset(ht
, 0, sizeof *ht
);
126 ht
->shift
= JSJ_HASH_BITS
- n
;
128 #if (defined(XP_WIN) || defined(XP_OS2)) && !defined(_WIN32)
130 (*allocOps
->freeTable
)(allocPriv
, ht
);
134 nb
= n
* sizeof(JSJHashEntry
*);
135 ht
->buckets
= (*allocOps
->allocTable
)(allocPriv
, nb
);
137 (*allocOps
->freeTable
)(allocPriv
, ht
);
140 memset(ht
->buckets
, 0, nb
);
142 ht
->keyHash
= keyHash
;
143 ht
->keyCompare
= keyCompare
;
144 ht
->valueCompare
= valueCompare
;
145 ht
->allocOps
= allocOps
;
146 ht
->allocPriv
= allocPriv
;
151 JSJ_HashTableDestroy(JSJHashTable
*ht
)
154 JSJHashEntry
*he
, *next
;
155 JSJHashAllocOps
*allocOps
= ht
->allocOps
;
156 void *allocPriv
= ht
->allocPriv
;
159 for (i
= 0; i
< n
; i
++) {
160 for (he
= ht
->buckets
[i
]; he
; he
= next
) {
162 (*allocOps
->freeEntry
)(allocPriv
, he
, HT_FREE_ENTRY
);
166 memset(ht
->buckets
, 0xDB, n
* sizeof ht
->buckets
[0]);
168 (*allocOps
->freeTable
)(allocPriv
, ht
->buckets
);
170 memset(ht
, 0xDB, sizeof *ht
);
172 (*allocOps
->freeTable
)(allocPriv
, ht
);
176 ** Multiplicative hash, from Knuth 6.4.
178 #define GOLDEN_RATIO 0x9E3779B9U
180 JS_EXPORT_API(JSJHashEntry
**)
181 JSJ_HashTableRawLookup(JSJHashTable
*ht
, JSJHashNumber keyHash
, const void *key
, void *arg
)
183 JSJHashEntry
*he
, **hep
, **hep0
;
189 h
= keyHash
* GOLDEN_RATIO
;
191 hep
= hep0
= &ht
->buckets
[h
];
192 while ((he
= *hep
) != 0) {
193 if (he
->keyHash
== keyHash
&& (*ht
->keyCompare
)(key
, he
->key
, arg
)) {
194 /* Move to front of chain if not already there */
210 JS_EXPORT_API(JSJHashEntry
*)
211 JSJ_HashTableRawAdd(JSJHashTable
*ht
, JSJHashEntry
**hep
,
212 JSJHashNumber keyHash
, const void *key
, void *value
,
216 JSJHashEntry
*he
, *next
, **oldbuckets
;
219 /* Grow the table if it is overloaded */
221 if (ht
->nentries
>= OVERLOADED(n
)) {
226 oldbuckets
= ht
->buckets
;
227 #if (defined(XP_WIN) || defined(XP_OS2)) && !defined(_WIN32)
231 nb
= 2 * n
* sizeof(JSJHashEntry
*);
232 ht
->buckets
= (*ht
->allocOps
->allocTable
)(ht
->allocPriv
, nb
);
234 ht
->buckets
= oldbuckets
;
237 memset(ht
->buckets
, 0, nb
);
239 for (i
= 0; i
< n
; i
++) {
240 for (he
= oldbuckets
[i
]; he
; he
= next
) {
242 hep
= JSJ_HashTableRawLookup(ht
, he
->keyHash
, he
->key
, arg
);
243 JS_ASSERT(*hep
== 0);
249 memset(oldbuckets
, 0xDB, n
* sizeof oldbuckets
[0]);
251 (*ht
->allocOps
->freeTable
)(ht
->allocPriv
, oldbuckets
);
252 hep
= JSJ_HashTableRawLookup(ht
, keyHash
, key
, arg
);
255 /* Make a new key value entry */
256 he
= (*ht
->allocOps
->allocEntry
)(ht
->allocPriv
, key
);
259 he
->keyHash
= keyHash
;
268 JS_EXPORT_API(JSJHashEntry
*)
269 JSJ_HashTableAdd(JSJHashTable
*ht
, const void *key
, void *value
, void *arg
)
271 JSJHashNumber keyHash
;
272 JSJHashEntry
*he
, **hep
;
274 keyHash
= (*ht
->keyHash
)(key
, arg
);
275 hep
= JSJ_HashTableRawLookup(ht
, keyHash
, key
, arg
);
276 if ((he
= *hep
) != 0) {
277 /* Hit; see if values match */
278 if ((*ht
->valueCompare
)(he
->value
, value
, arg
)) {
279 /* key,value pair is already present in table */
283 (*ht
->allocOps
->freeEntry
)(ht
->allocPriv
, he
, HT_FREE_VALUE
);
287 return JSJ_HashTableRawAdd(ht
, hep
, keyHash
, key
, value
, arg
);
291 JSJ_HashTableRawRemove(JSJHashTable
*ht
, JSJHashEntry
**hep
, JSJHashEntry
*he
, void *arg
)
294 JSJHashEntry
*next
, **oldbuckets
;
298 (*ht
->allocOps
->freeEntry
)(ht
->allocPriv
, he
, HT_FREE_ENTRY
);
300 /* Shrink table if it's underloaded */
302 if (--ht
->nentries
< UNDERLOADED(n
)) {
307 oldbuckets
= ht
->buckets
;
308 nb
= n
* sizeof(JSJHashEntry
*) / 2;
309 ht
->buckets
= (*ht
->allocOps
->allocTable
)(ht
->allocPriv
, nb
);
311 ht
->buckets
= oldbuckets
;
314 memset(ht
->buckets
, 0, nb
);
316 for (i
= 0; i
< n
; i
++) {
317 for (he
= oldbuckets
[i
]; he
; he
= next
) {
319 hep
= JSJ_HashTableRawLookup(ht
, he
->keyHash
, he
->key
, arg
);
320 JS_ASSERT(*hep
== 0);
326 memset(oldbuckets
, 0xDB, n
* sizeof oldbuckets
[0]);
328 (*ht
->allocOps
->freeTable
)(ht
->allocPriv
, oldbuckets
);
332 JS_EXPORT_API(JSBool
)
333 JSJ_HashTableRemove(JSJHashTable
*ht
, const void *key
, void *arg
)
335 JSJHashNumber keyHash
;
336 JSJHashEntry
*he
, **hep
;
338 keyHash
= (*ht
->keyHash
)(key
, arg
);
339 hep
= JSJ_HashTableRawLookup(ht
, keyHash
, key
, arg
);
340 if ((he
= *hep
) == 0)
343 /* Hit; remove element */
344 JSJ_HashTableRawRemove(ht
, hep
, he
, arg
);
348 JS_EXPORT_API(void *)
349 JSJ_HashTableLookup(JSJHashTable
*ht
, const void *key
, void *arg
)
351 JSJHashNumber keyHash
;
352 JSJHashEntry
*he
, **hep
;
354 keyHash
= (*ht
->keyHash
)(key
, arg
);
355 hep
= JSJ_HashTableRawLookup(ht
, keyHash
, key
, arg
);
356 if ((he
= *hep
) != 0) {
363 ** Iterate over the entries in the hash table calling func for each
364 ** entry found. Stop if "f" says to (return value & JS_ENUMERATE_STOP).
365 ** Return a count of the number of elements scanned.
368 JSJ_HashTableEnumerateEntries(JSJHashTable
*ht
, JSJHashEnumerator f
, void *arg
)
370 JSJHashEntry
*he
, **hep
;
371 JSUint32 i
, nbuckets
;
373 JSJHashEntry
*todo
= 0;
375 nbuckets
= NBUCKETS(ht
);
376 for (i
= 0; i
< nbuckets
; i
++) {
377 hep
= &ht
->buckets
[i
];
378 while ((he
= *hep
) != 0) {
379 rv
= (*f
)(he
, n
, arg
);
381 if (rv
& (HT_ENUMERATE_REMOVE
| HT_ENUMERATE_UNHASH
)) {
383 if (rv
& HT_ENUMERATE_REMOVE
) {
390 if (rv
& HT_ENUMERATE_STOP
) {
398 while ((he
= *hep
) != 0) {
399 JSJ_HashTableRawRemove(ht
, hep
, he
, arg
);
409 JSJ_HashTableDumpMeter(JSJHashTable
*ht
, JSJHashEnumerator dump
, FILE *fp
)
411 double mean
, variance
;
412 JSUint32 nchains
, nbuckets
;
413 JSUint32 i
, n
, maxChain
, maxChainLen
;
419 nbuckets
= NBUCKETS(ht
);
420 for (i
= 0; i
< nbuckets
; i
++) {
425 for (n
= 0; he
; he
= he
->next
)
428 if (n
> maxChainLen
) {
433 mean
= (double)ht
->nentries
/ nchains
;
434 variance
= fabs(variance
/ nchains
- mean
* mean
);
436 fprintf(fp
, "\nHash table statistics:\n");
437 fprintf(fp
, " number of lookups: %u\n", ht
->nlookups
);
438 fprintf(fp
, " number of entries: %u\n", ht
->nentries
);
439 fprintf(fp
, " number of grows: %u\n", ht
->ngrows
);
440 fprintf(fp
, " number of shrinks: %u\n", ht
->nshrinks
);
441 fprintf(fp
, " mean steps per hash: %g\n", (double)ht
->nsteps
443 fprintf(fp
, "mean hash chain length: %g\n", mean
);
444 fprintf(fp
, " standard deviation: %g\n", sqrt(variance
));
445 fprintf(fp
, " max hash chain length: %u\n", maxChainLen
);
446 fprintf(fp
, " max hash chain: [%u]\n", maxChain
);
448 for (he
= ht
->buckets
[maxChain
], i
= 0; he
; he
= he
->next
, i
++)
449 if ((*dump
)(he
, i
, fp
) != HT_ENUMERATE_NEXT
)
452 #endif /* HASHMETER */
455 JSJ_HashTableDump(JSJHashTable
*ht
, JSJHashEnumerator dump
, FILE *fp
)
459 count
= JSJ_HashTableEnumerateEntries(ht
, dump
, fp
);
461 JSJ_HashTableDumpMeter(ht
, dump
, fp
);
466 JS_EXPORT_API(JSJHashNumber
)
467 JSJ_HashString(const void *key
)
470 const unsigned char *s
;
473 for (s
= key
; *s
; s
++)
474 h
= JS_ROTATE_LEFT32(h
, 4) ^ *s
;
479 JSJ_CompareStrings(const void *v1
, const void *v2
)
481 return strcmp(v1
, v2
) == 0;
485 JSJ_CompareValues(const void *v1
, const void *v2
)