1 |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
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 Mozilla JavaScript code. |
16 |
* |
17 |
* The Initial Developer of the Original Code is |
18 |
* Netscape Communications Corporation. |
19 |
* Portions created by the Initial Developer are Copyright (C) 1999-2001 |
20 |
* the Initial Developer. All Rights Reserved. |
21 |
* |
22 |
* Contributor(s): |
23 |
* Brendan Eich <brendan@mozilla.org> (Original Author) |
24 |
* |
25 |
* Alternatively, the contents of this file may be used under the terms of |
26 |
* either of the GNU General Public License Version 2 or later (the "GPL"), |
27 |
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
28 |
* in which case the provisions of the GPL or the LGPL are applicable instead |
29 |
* of those above. If you wish to allow use of your version of this file only |
30 |
* under the terms of either the GPL or the LGPL, and not to allow others to |
31 |
* use your version of this file under the terms of the MPL, indicate your |
32 |
* decision by deleting the provisions above and replace them with the notice |
33 |
* and other provisions required by the GPL or the LGPL. If you do not delete |
34 |
* the provisions above, a recipient may use your version of this file under |
35 |
* the terms of any one of the MPL, the GPL or the LGPL. |
36 |
* |
37 |
* ***** END LICENSE BLOCK ***** */ |
38 |
|
39 |
#ifndef jsdhash_h___ |
40 |
#define jsdhash_h___ |
41 |
/* |
42 |
* Double hashing, a la Knuth 6. |
43 |
*/ |
44 |
#include "jstypes.h" |
45 |
|
46 |
JS_BEGIN_EXTERN_C |
47 |
|
48 |
#if defined(__GNUC__) && defined(__i386__) && (__GNUC__ >= 3) && !defined(XP_OS2) |
49 |
#define JS_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall)) |
50 |
#elif defined(XP_WIN) |
51 |
#define JS_DHASH_FASTCALL __fastcall |
52 |
#else |
53 |
#define JS_DHASH_FASTCALL |
54 |
#endif |
55 |
|
56 |
#ifdef DEBUG_XXXbrendan |
57 |
#define JS_DHASHMETER 1 |
58 |
#endif |
59 |
|
60 |
/* Table size limit, do not equal or exceed (see min&maxAlphaFrac, below). */ |
61 |
#undef JS_DHASH_SIZE_LIMIT |
62 |
#define JS_DHASH_SIZE_LIMIT JS_BIT(24) |
63 |
|
64 |
/* Minimum table size, or gross entry count (net is at most .75 loaded). */ |
65 |
#ifndef JS_DHASH_MIN_SIZE |
66 |
#define JS_DHASH_MIN_SIZE 16 |
67 |
#elif (JS_DHASH_MIN_SIZE & (JS_DHASH_MIN_SIZE - 1)) != 0 |
68 |
#error "JS_DHASH_MIN_SIZE must be a power of two!" |
69 |
#endif |
70 |
|
71 |
/* |
72 |
* Multiplicative hash uses an unsigned 32 bit integer and the golden ratio, |
73 |
* expressed as a fixed-point 32-bit fraction. |
74 |
*/ |
75 |
#define JS_DHASH_BITS 32 |
76 |
#define JS_DHASH_GOLDEN_RATIO 0x9E3779B9U |
77 |
|
78 |
/* Primitive and forward-struct typedefs. */ |
79 |
typedef uint32 JSDHashNumber; |
80 |
typedef struct JSDHashEntryHdr JSDHashEntryHdr; |
81 |
typedef struct JSDHashEntryStub JSDHashEntryStub; |
82 |
typedef struct JSDHashTable JSDHashTable; |
83 |
typedef struct JSDHashTableOps JSDHashTableOps; |
84 |
|
85 |
/* |
86 |
* Table entry header structure. |
87 |
* |
88 |
* In order to allow in-line allocation of key and value, we do not declare |
89 |
* either here. Instead, the API uses const void *key as a formal parameter. |
90 |
* The key need not be stored in the entry; it may be part of the value, but |
91 |
* need not be stored at all. |
92 |
* |
93 |
* Callback types are defined below and grouped into the JSDHashTableOps |
94 |
* structure, for single static initialization per hash table sub-type. |
95 |
* |
96 |
* Each hash table sub-type should nest the JSDHashEntryHdr structure at the |
97 |
* front of its particular entry type. The keyHash member contains the result |
98 |
* of multiplying the hash code returned from the hashKey callback (see below) |
99 |
* by JS_DHASH_GOLDEN_RATIO, then constraining the result to avoid the magic 0 |
100 |
* and 1 values. The stored keyHash value is table size invariant, and it is |
101 |
* maintained automatically by JS_DHashTableOperate -- users should never set |
102 |
* it, and its only uses should be via the entry macros below. |
103 |
* |
104 |
* The JS_DHASH_ENTRY_IS_LIVE macro tests whether entry is neither free nor |
105 |
* removed. An entry may be either busy or free; if busy, it may be live or |
106 |
* removed. Consumers of this API should not access members of entries that |
107 |
* are not live. |
108 |
* |
109 |
* However, use JS_DHASH_ENTRY_IS_BUSY for faster liveness testing of entries |
110 |
* returned by JS_DHashTableOperate, as JS_DHashTableOperate never returns a |
111 |
* non-live, busy (i.e., removed) entry pointer to its caller. See below for |
112 |
* more details on JS_DHashTableOperate's calling rules. |
113 |
*/ |
114 |
struct JSDHashEntryHdr { |
115 |
JSDHashNumber keyHash; /* every entry must begin like this */ |
116 |
}; |
117 |
|
118 |
#define JS_DHASH_ENTRY_IS_FREE(entry) ((entry)->keyHash == 0) |
119 |
#define JS_DHASH_ENTRY_IS_BUSY(entry) (!JS_DHASH_ENTRY_IS_FREE(entry)) |
120 |
#define JS_DHASH_ENTRY_IS_LIVE(entry) ((entry)->keyHash >= 2) |
121 |
|
122 |
/* |
123 |
* A JSDHashTable is currently 8 words (without the JS_DHASHMETER overhead) |
124 |
* on most architectures, and may be allocated on the stack or within another |
125 |
* structure or class (see below for the Init and Finish functions to use). |
126 |
* |
127 |
* To decide whether to use double hashing vs. chaining, we need to develop a |
128 |
* trade-off relation, as follows: |
129 |
* |
130 |
* Let alpha be the load factor, esize the entry size in words, count the |
131 |
* entry count, and pow2 the power-of-two table size in entries. |
132 |
* |
133 |
* (JSDHashTable overhead) > (JSHashTable overhead) |
134 |
* (unused table entry space) > (malloc and .next overhead per entry) + |
135 |
* (buckets overhead) |
136 |
* (1 - alpha) * esize * pow2 > 2 * count + pow2 |
137 |
* |
138 |
* Notice that alpha is by definition (count / pow2): |
139 |
* |
140 |
* (1 - alpha) * esize * pow2 > 2 * alpha * pow2 + pow2 |
141 |
* (1 - alpha) * esize > 2 * alpha + 1 |
142 |
* |
143 |
* esize > (1 + 2 * alpha) / (1 - alpha) |
144 |
* |
145 |
* This assumes both tables must keep keyHash, key, and value for each entry, |
146 |
* where key and value point to separately allocated strings or structures. |
147 |
* If key and value can be combined into one pointer, then the trade-off is: |
148 |
* |
149 |
* esize > (1 + 3 * alpha) / (1 - alpha) |
150 |
* |
151 |
* If the entry value can be a subtype of JSDHashEntryHdr, rather than a type |
152 |
* that must be allocated separately and referenced by an entry.value pointer |
153 |
* member, and provided key's allocation can be fused with its entry's, then |
154 |
* k (the words wasted per entry with chaining) is 4. |
155 |
* |
156 |
* To see these curves, feed gnuplot input like so: |
157 |
* |
158 |
* gnuplot> f(x,k) = (1 + k * x) / (1 - x) |
159 |
* gnuplot> plot [0:.75] f(x,2), f(x,3), f(x,4) |
160 |
* |
161 |
* For k of 2 and a well-loaded table (alpha > .5), esize must be more than 4 |
162 |
* words for chaining to be more space-efficient than double hashing. |
163 |
* |
164 |
* Solving for alpha helps us decide when to shrink an underloaded table: |
165 |
* |
166 |
* esize > (1 + k * alpha) / (1 - alpha) |
167 |
* esize - alpha * esize > 1 + k * alpha |
168 |
* esize - 1 > (k + esize) * alpha |
169 |
* (esize - 1) / (k + esize) > alpha |
170 |
* |
171 |
* alpha < (esize - 1) / (esize + k) |
172 |
* |
173 |
* Therefore double hashing should keep alpha >= (esize - 1) / (esize + k), |
174 |
* assuming esize is not too large (in which case, chaining should probably be |
175 |
* used for any alpha). For esize=2 and k=3, we want alpha >= .2; for esize=3 |
176 |
* and k=2, we want alpha >= .4. For k=4, esize could be 6, and alpha >= .5 |
177 |
* would still obtain. See the JS_DHASH_MIN_ALPHA macro further below. |
178 |
* |
179 |
* The current implementation uses a configurable lower bound on alpha, which |
180 |
* defaults to .25, when deciding to shrink the table (while still respecting |
181 |
* JS_DHASH_MIN_SIZE). |
182 |
* |
183 |
* Note a qualitative difference between chaining and double hashing: under |
184 |
* chaining, entry addresses are stable across table shrinks and grows. With |
185 |
* double hashing, you can't safely hold an entry pointer and use it after an |
186 |
* ADD or REMOVE operation, unless you sample table->generation before adding |
187 |
* or removing, and compare the sample after, dereferencing the entry pointer |
188 |
* only if table->generation has not changed. |
189 |
* |
190 |
* The moral of this story: there is no one-size-fits-all hash table scheme, |
191 |
* but for small table entry size, and assuming entry address stability is not |
192 |
* required, double hashing wins. |
193 |
*/ |
194 |
struct JSDHashTable { |
195 |
const JSDHashTableOps *ops; /* virtual operations, see below */ |
196 |
void *data; /* ops- and instance-specific data */ |
197 |
int16 hashShift; /* multiplicative hash shift */ |
198 |
uint8 maxAlphaFrac; /* 8-bit fixed point max alpha */ |
199 |
uint8 minAlphaFrac; /* 8-bit fixed point min alpha */ |
200 |
uint32 entrySize; /* number of bytes in an entry */ |
201 |
uint32 entryCount; /* number of entries in table */ |
202 |
uint32 removedCount; /* removed entry sentinels in table */ |
203 |
uint32 generation; /* entry storage generation number */ |
204 |
char *entryStore; /* entry storage */ |
205 |
#ifdef JS_DHASHMETER |
206 |
struct JSDHashStats { |
207 |
uint32 searches; /* total number of table searches */ |
208 |
uint32 steps; /* hash chain links traversed */ |
209 |
uint32 hits; /* searches that found key */ |
210 |
uint32 misses; /* searches that didn't find key */ |
211 |
uint32 lookups; /* number of JS_DHASH_LOOKUPs */ |
212 |
uint32 addMisses; /* adds that miss, and do work */ |
213 |
uint32 addOverRemoved; /* adds that recycled a removed entry */ |
214 |
uint32 addHits; /* adds that hit an existing entry */ |
215 |
uint32 addFailures; /* out-of-memory during add growth */ |
216 |
uint32 removeHits; /* removes that hit, and do work */ |
217 |
uint32 removeMisses; /* useless removes that miss */ |
218 |
uint32 removeFrees; /* removes that freed entry directly */ |
219 |
uint32 removeEnums; /* removes done by Enumerate */ |
220 |
uint32 grows; /* table expansions */ |
221 |
uint32 shrinks; /* table contractions */ |
222 |
uint32 compresses; /* table compressions */ |
223 |
uint32 enumShrinks; /* contractions after Enumerate */ |
224 |
} stats; |
225 |
#endif |
226 |
}; |
227 |
|
228 |
/* |
229 |
* Size in entries (gross, not net of free and removed sentinels) for table. |
230 |
* We store hashShift rather than sizeLog2 to optimize the collision-free case |
231 |
* in SearchTable. |
232 |
*/ |
233 |
#define JS_DHASH_TABLE_SIZE(table) JS_BIT(JS_DHASH_BITS - (table)->hashShift) |
234 |
|
235 |
/* |
236 |
* Table space at entryStore is allocated and freed using these callbacks. |
237 |
* The allocator should return null on error only (not if called with nbytes |
238 |
* equal to 0; but note that jsdhash.c code will never call with 0 nbytes). |
239 |
*/ |
240 |
typedef void * |
241 |
(* JSDHashAllocTable)(JSDHashTable *table, uint32 nbytes); |
242 |
|
243 |
typedef void |
244 |
(* JSDHashFreeTable) (JSDHashTable *table, void *ptr); |
245 |
|
246 |
/* |
247 |
* Compute the hash code for a given key to be looked up, added, or removed |
248 |
* from table. A hash code may have any JSDHashNumber value. |
249 |
*/ |
250 |
typedef JSDHashNumber |
251 |
(* JSDHashHashKey) (JSDHashTable *table, const void *key); |
252 |
|
253 |
/* |
254 |
* Compare the key identifying entry in table with the provided key parameter. |
255 |
* Return JS_TRUE if keys match, JS_FALSE otherwise. |
256 |
*/ |
257 |
typedef JSBool |
258 |
(* JSDHashMatchEntry)(JSDHashTable *table, const JSDHashEntryHdr *entry, |
259 |
const void *key); |
260 |
|
261 |
/* |
262 |
* Copy the data starting at from to the new entry storage at to. Do not add |
263 |
* reference counts for any strong references in the entry, however, as this |
264 |
* is a "move" operation: the old entry storage at from will be freed without |
265 |
* any reference-decrementing callback shortly. |
266 |
*/ |
267 |
typedef void |
268 |
(* JSDHashMoveEntry)(JSDHashTable *table, const JSDHashEntryHdr *from, |
269 |
JSDHashEntryHdr *to); |
270 |
|
271 |
/* |
272 |
* Clear the entry and drop any strong references it holds. This callback is |
273 |
* invoked during a JS_DHASH_REMOVE operation (see below for operation codes), |
274 |
* but only if the given key is found in the table. |
275 |
*/ |
276 |
typedef void |
277 |
(* JSDHashClearEntry)(JSDHashTable *table, JSDHashEntryHdr *entry); |
278 |
|
279 |
/* |
280 |
* Called when a table (whether allocated dynamically by itself, or nested in |
281 |
* a larger structure, or allocated on the stack) is finished. This callback |
282 |
* allows table->ops-specific code to finalize table->data. |
283 |
*/ |
284 |
typedef void |
285 |
(* JSDHashFinalize) (JSDHashTable *table); |
286 |
|
287 |
/* |
288 |
* Initialize a new entry, apart from keyHash. This function is called when |
289 |
* JS_DHashTableOperate's JS_DHASH_ADD case finds no existing entry for the |
290 |
* given key, and must add a new one. At that point, entry->keyHash is not |
291 |
* set yet, to avoid claiming the last free entry in a severely overloaded |
292 |
* table. |
293 |
*/ |
294 |
typedef JSBool |
295 |
(* JSDHashInitEntry)(JSDHashTable *table, JSDHashEntryHdr *entry, |
296 |
const void *key); |
297 |
|
298 |
/* |
299 |
* Finally, the "vtable" structure for JSDHashTable. The first eight hooks |
300 |
* must be provided by implementations; they're called unconditionally by the |
301 |
* generic jsdhash.c code. Hooks after these may be null. |
302 |
* |
303 |
* Summary of allocation-related hook usage with C++ placement new emphasis: |
304 |
* allocTable Allocate raw bytes with malloc, no ctors run. |
305 |
* freeTable Free raw bytes with free, no dtors run. |
306 |
* initEntry Call placement new using default key-based ctor. |
307 |
* Return JS_TRUE on success, JS_FALSE on error. |
308 |
* moveEntry Call placement new using copy ctor, run dtor on old |
309 |
* entry storage. |
310 |
* clearEntry Run dtor on entry. |
311 |
* finalize Stub unless table->data was initialized and needs to |
312 |
* be finalized. |
313 |
* |
314 |
* Note the reason why initEntry is optional: the default hooks (stubs) clear |
315 |
* entry storage: On successful JS_DHashTableOperate(tbl, key, JS_DHASH_ADD), |
316 |
* the returned entry pointer addresses an entry struct whose keyHash member |
317 |
* has been set non-zero, but all other entry members are still clear (null). |
318 |
* JS_DHASH_ADD callers can test such members to see whether the entry was |
319 |
* newly created by the JS_DHASH_ADD call that just succeeded. If placement |
320 |
* new or similar initialization is required, define an initEntry hook. Of |
321 |
* course, the clearEntry hook must zero or null appropriately. |
322 |
* |
323 |
* XXX assumes 0 is null for pointer types. |
324 |
*/ |
325 |
struct JSDHashTableOps { |
326 |
/* Mandatory hooks. All implementations must provide these. */ |
327 |
JSDHashAllocTable allocTable; |
328 |
JSDHashFreeTable freeTable; |
329 |
JSDHashHashKey hashKey; |
330 |
JSDHashMatchEntry matchEntry; |
331 |
JSDHashMoveEntry moveEntry; |
332 |
JSDHashClearEntry clearEntry; |
333 |
JSDHashFinalize finalize; |
334 |
|
335 |
/* Optional hooks start here. If null, these are not called. */ |
336 |
JSDHashInitEntry initEntry; |
337 |
}; |
338 |
|
339 |
/* |
340 |
* Default implementations for the above ops. |
341 |
*/ |
342 |
extern JS_PUBLIC_API(void *) |
343 |
JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes); |
344 |
|
345 |
extern JS_PUBLIC_API(void) |
346 |
JS_DHashFreeTable(JSDHashTable *table, void *ptr); |
347 |
|
348 |
extern JS_PUBLIC_API(JSDHashNumber) |
349 |
JS_DHashStringKey(JSDHashTable *table, const void *key); |
350 |
|
351 |
/* A minimal entry contains a keyHash header and a void key pointer. */ |
352 |
struct JSDHashEntryStub { |
353 |
JSDHashEntryHdr hdr; |
354 |
const void *key; |
355 |
}; |
356 |
|
357 |
extern JS_PUBLIC_API(JSDHashNumber) |
358 |
JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key); |
359 |
|
360 |
extern JS_PUBLIC_API(JSBool) |
361 |
JS_DHashMatchEntryStub(JSDHashTable *table, |
362 |
const JSDHashEntryHdr *entry, |
363 |
const void *key); |
364 |
|
365 |
extern JS_PUBLIC_API(JSBool) |
366 |
JS_DHashMatchStringKey(JSDHashTable *table, |
367 |
const JSDHashEntryHdr *entry, |
368 |
const void *key); |
369 |
|
370 |
extern JS_PUBLIC_API(void) |
371 |
JS_DHashMoveEntryStub(JSDHashTable *table, |
372 |
const JSDHashEntryHdr *from, |
373 |
JSDHashEntryHdr *to); |
374 |
|
375 |
extern JS_PUBLIC_API(void) |
376 |
JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry); |
377 |
|
378 |
extern JS_PUBLIC_API(void) |
379 |
JS_DHashFreeStringKey(JSDHashTable *table, JSDHashEntryHdr *entry); |
380 |
|
381 |
extern JS_PUBLIC_API(void) |
382 |
JS_DHashFinalizeStub(JSDHashTable *table); |
383 |
|
384 |
/* |
385 |
* If you use JSDHashEntryStub or a subclass of it as your entry struct, and |
386 |
* if your entries move via memcpy and clear via memset(0), you can use these |
387 |
* stub operations. |
388 |
*/ |
389 |
extern JS_PUBLIC_API(const JSDHashTableOps *) |
390 |
JS_DHashGetStubOps(void); |
391 |
|
392 |
/* |
393 |
* Dynamically allocate a new JSDHashTable using malloc, initialize it using |
394 |
* JS_DHashTableInit, and return its address. Return null on malloc failure. |
395 |
* Note that the entry storage at table->entryStore will be allocated using |
396 |
* the ops->allocTable callback. |
397 |
*/ |
398 |
extern JS_PUBLIC_API(JSDHashTable *) |
399 |
JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize, |
400 |
uint32 capacity); |
401 |
|
402 |
/* |
403 |
* Finalize table's data, free its entry storage (via table->ops->freeTable), |
404 |
* and return the memory starting at table to the malloc heap. |
405 |
*/ |
406 |
extern JS_PUBLIC_API(void) |
407 |
JS_DHashTableDestroy(JSDHashTable *table); |
408 |
|
409 |
/* |
410 |
* Initialize table with ops, data, entrySize, and capacity. Capacity is a |
411 |
* guess for the smallest table size at which the table will usually be less |
412 |
* than 75% loaded (the table will grow or shrink as needed; capacity serves |
413 |
* only to avoid inevitable early growth from JS_DHASH_MIN_SIZE). |
414 |
*/ |
415 |
extern JS_PUBLIC_API(JSBool) |
416 |
JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, |
417 |
uint32 entrySize, uint32 capacity); |
418 |
|
419 |
/* |
420 |
* Set maximum and minimum alpha for table. The defaults are 0.75 and .25. |
421 |
* maxAlpha must be in [0.5, 0.9375] for the default JS_DHASH_MIN_SIZE; or if |
422 |
* MinSize=JS_DHASH_MIN_SIZE <= 256, in [0.5, (float)(MinSize-1)/MinSize]; or |
423 |
* else in [0.5, 255.0/256]. minAlpha must be in [0, maxAlpha / 2), so that |
424 |
* we don't shrink on the very next remove after growing a table upon adding |
425 |
* an entry that brings entryCount past maxAlpha * tableSize. |
426 |
*/ |
427 |
extern JS_PUBLIC_API(void) |
428 |
JS_DHashTableSetAlphaBounds(JSDHashTable *table, |
429 |
float maxAlpha, |
430 |
float minAlpha); |
431 |
|
432 |
/* |
433 |
* Call this macro with k, the number of pointer-sized words wasted per entry |
434 |
* under chaining, to compute the minimum alpha at which double hashing still |
435 |
* beats chaining. |
436 |
*/ |
437 |
#define JS_DHASH_MIN_ALPHA(table, k) \ |
438 |
((float)((table)->entrySize / sizeof(void *) - 1) \ |
439 |
/ ((table)->entrySize / sizeof(void *) + (k))) |
440 |
|
441 |
/* |
442 |
* Default max/min alpha, and macros to compute the value for the |capacity| |
443 |
* parameter to JS_NewDHashTable and JS_DHashTableInit, given default or any |
444 |
* max alpha, such that adding entryCount entries right after initializing the |
445 |
* table will not require a reallocation (so JS_DHASH_ADD can't fail for those |
446 |
* JS_DHashTableOperate calls). |
447 |
* |
448 |
* NB: JS_DHASH_CAP is a helper macro meant for use only in JS_DHASH_CAPACITY. |
449 |
* Don't use it directly! |
450 |
*/ |
451 |
#define JS_DHASH_DEFAULT_MAX_ALPHA 0.75 |
452 |
#define JS_DHASH_DEFAULT_MIN_ALPHA 0.25 |
453 |
|
454 |
#define JS_DHASH_CAP(entryCount, maxAlpha) \ |
455 |
((uint32)((double)(entryCount) / (maxAlpha))) |
456 |
|
457 |
#define JS_DHASH_CAPACITY(entryCount, maxAlpha) \ |
458 |
(JS_DHASH_CAP(entryCount, maxAlpha) + \ |
459 |
(((JS_DHASH_CAP(entryCount, maxAlpha) * (uint8)(0x100 * (maxAlpha))) \ |
460 |
>> 8) < (entryCount))) |
461 |
|
462 |
#define JS_DHASH_DEFAULT_CAPACITY(entryCount) \ |
463 |
JS_DHASH_CAPACITY(entryCount, JS_DHASH_DEFAULT_MAX_ALPHA) |
464 |
|
465 |
/* |
466 |
* Finalize table's data, free its entry storage using table->ops->freeTable, |
467 |
* and leave its members unchanged from their last live values (which leaves |
468 |
* pointers dangling). If you want to burn cycles clearing table, it's up to |
469 |
* your code to call memset. |
470 |
*/ |
471 |
extern JS_PUBLIC_API(void) |
472 |
JS_DHashTableFinish(JSDHashTable *table); |
473 |
|
474 |
/* |
475 |
* To consolidate keyHash computation and table grow/shrink code, we use a |
476 |
* single entry point for lookup, add, and remove operations. The operation |
477 |
* codes are declared here, along with codes returned by JSDHashEnumerator |
478 |
* functions, which control JS_DHashTableEnumerate's behavior. |
479 |
*/ |
480 |
typedef enum JSDHashOperator { |
481 |
JS_DHASH_LOOKUP = 0, /* lookup entry */ |
482 |
JS_DHASH_ADD = 1, /* add entry */ |
483 |
JS_DHASH_REMOVE = 2, /* remove entry, or enumerator says remove */ |
484 |
JS_DHASH_NEXT = 0, /* enumerator says continue */ |
485 |
JS_DHASH_STOP = 1 /* enumerator says stop */ |
486 |
} JSDHashOperator; |
487 |
|
488 |
/* |
489 |
* To lookup a key in table, call: |
490 |
* |
491 |
* entry = JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP); |
492 |
* |
493 |
* If JS_DHASH_ENTRY_IS_BUSY(entry) is true, key was found and it identifies |
494 |
* entry. If JS_DHASH_ENTRY_IS_FREE(entry) is true, key was not found. |
495 |
* |
496 |
* To add an entry identified by key to table, call: |
497 |
* |
498 |
* entry = JS_DHashTableOperate(table, key, JS_DHASH_ADD); |
499 |
* |
500 |
* If entry is null upon return, then either the table is severely overloaded, |
501 |
* and memory can't be allocated for entry storage via table->ops->allocTable; |
502 |
* Or if table->ops->initEntry is non-null, the table->ops->initEntry op may |
503 |
* have returned false. |
504 |
* |
505 |
* Otherwise, entry->keyHash has been set so that JS_DHASH_ENTRY_IS_BUSY(entry) |
506 |
* is true, and it is up to the caller to initialize the key and value parts |
507 |
* of the entry sub-type, if they have not been set already (i.e. if entry was |
508 |
* not already in the table, and if the optional initEntry hook was not used). |
509 |
* |
510 |
* To remove an entry identified by key from table, call: |
511 |
* |
512 |
* (void) JS_DHashTableOperate(table, key, JS_DHASH_REMOVE); |
513 |
* |
514 |
* If key's entry is found, it is cleared (via table->ops->clearEntry) and |
515 |
* the entry is marked so that JS_DHASH_ENTRY_IS_FREE(entry). This operation |
516 |
* returns null unconditionally; you should ignore its return value. |
517 |
*/ |
518 |
extern JS_PUBLIC_API(JSDHashEntryHdr *) JS_DHASH_FASTCALL |
519 |
JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op); |
520 |
|
521 |
/* |
522 |
* Remove an entry already accessed via LOOKUP or ADD. |
523 |
* |
524 |
* NB: this is a "raw" or low-level routine, intended to be used only where |
525 |
* the inefficiency of a full JS_DHashTableOperate (which rehashes in order |
526 |
* to find the entry given its key) is not tolerable. This function does not |
527 |
* shrink the table if it is underloaded. It does not update stats #ifdef |
528 |
* JS_DHASHMETER, either. |
529 |
*/ |
530 |
extern JS_PUBLIC_API(void) |
531 |
JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry); |
532 |
|
533 |
/* |
534 |
* Enumerate entries in table using etor: |
535 |
* |
536 |
* count = JS_DHashTableEnumerate(table, etor, arg); |
537 |
* |
538 |
* JS_DHashTableEnumerate calls etor like so: |
539 |
* |
540 |
* op = etor(table, entry, number, arg); |
541 |
* |
542 |
* where number is a zero-based ordinal assigned to live entries according to |
543 |
* their order in table->entryStore. |
544 |
* |
545 |
* The return value, op, is treated as a set of flags. If op is JS_DHASH_NEXT, |
546 |
* then continue enumerating. If op contains JS_DHASH_REMOVE, then clear (via |
547 |
* table->ops->clearEntry) and free entry. Then we check whether op contains |
548 |
* JS_DHASH_STOP; if so, stop enumerating and return the number of live entries |
549 |
* that were enumerated so far. Return the total number of live entries when |
550 |
* enumeration completes normally. |
551 |
* |
552 |
* If etor calls JS_DHashTableOperate on table with op != JS_DHASH_LOOKUP, it |
553 |
* must return JS_DHASH_STOP; otherwise undefined behavior results. |
554 |
* |
555 |
* If any enumerator returns JS_DHASH_REMOVE, table->entryStore may be shrunk |
556 |
* or compressed after enumeration, but before JS_DHashTableEnumerate returns. |
557 |
* Such an enumerator therefore can't safely set aside entry pointers, but an |
558 |
* enumerator that never returns JS_DHASH_REMOVE can set pointers to entries |
559 |
* aside, e.g., to avoid copying live entries into an array of the entry type. |
560 |
* Copying entry pointers is cheaper, and safe so long as the caller of such a |
561 |
* "stable" Enumerate doesn't use the set-aside pointers after any call either |
562 |
* to PL_DHashTableOperate, or to an "unstable" form of Enumerate, which might |
563 |
* grow or shrink entryStore. |
564 |
* |
565 |
* If your enumerator wants to remove certain entries, but set aside pointers |
566 |
* to other entries that it retains, it can use JS_DHashTableRawRemove on the |
567 |
* entries to be removed, returning JS_DHASH_NEXT to skip them. Likewise, if |
568 |
* you want to remove entries, but for some reason you do not want entryStore |
569 |
* to be shrunk or compressed, you can call JS_DHashTableRawRemove safely on |
570 |
* the entry being enumerated, rather than returning JS_DHASH_REMOVE. |
571 |
*/ |
572 |
typedef JSDHashOperator |
573 |
(* JSDHashEnumerator)(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, |
574 |
void *arg); |
575 |
|
576 |
extern JS_PUBLIC_API(uint32) |
577 |
JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg); |
578 |
|
579 |
#ifdef JS_DHASHMETER |
580 |
#include <stdio.h> |
581 |
|
582 |
extern JS_PUBLIC_API(void) |
583 |
JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp); |
584 |
#endif |
585 |
|
586 |
JS_END_EXTERN_C |
587 |
|
588 |
#endif /* jsdhash_h___ */ |