VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/ds/pldhash.c@ 102345

Last change on this file since 102345 was 102334, checked in by vboxsync, 15 months ago

libs/xpcom: Drop VBOX_USE_IPRT_IN_XPCOM andmake the code the default as running without IPRT is impossible anyways, bugref:10545

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.5 KB
Line 
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 * Chris Waterson <waterson@netscape.com>
25 *
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.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40/*
41 * Double hashing implementation.
42 * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!!
43 */
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include "prbit.h"
48#include "pldhash.h"
49
50#ifdef PL_DHASHMETER
51# if defined MOZILLA_CLIENT && defined DEBUG_XXXbrendan
52# include "nsTraceMalloc.h"
53# endif
54# define METER(x) x
55#else
56# define METER(x) /* nothing */
57#endif
58#include <iprt/assert.h>
59#include <iprt/mem.h>
60
61PR_IMPLEMENT(void *)
62PL_DHashAllocTable(PLDHashTable *table, PRUint32 nbytes)
63{
64 return RTMemAlloc(nbytes);
65}
66
67PR_IMPLEMENT(void)
68PL_DHashFreeTable(PLDHashTable *table, void *ptr)
69{
70 RTMemFree(ptr);
71}
72
73PR_IMPLEMENT(PLDHashNumber)
74PL_DHashStringKey(PLDHashTable *table, const void *key)
75{
76 PLDHashNumber h;
77 const unsigned char *s;
78
79 h = 0;
80 for (s = key; *s != '\0'; s++)
81 h = (h >> (PL_DHASH_BITS - 4)) ^ (h << 4) ^ *s;
82 return h;
83}
84
85PR_IMPLEMENT(const void *)
86PL_DHashGetKeyStub(PLDHashTable *table, PLDHashEntryHdr *entry)
87{
88 PLDHashEntryStub *stub = (PLDHashEntryStub *)entry;
89
90 return stub->key;
91}
92
93PR_IMPLEMENT(PLDHashNumber)
94PL_DHashVoidPtrKeyStub(PLDHashTable *table, const void *key)
95{
96 return (PLDHashNumber)(uintptr_t)key >> 2;
97}
98
99PR_IMPLEMENT(PRBool)
100PL_DHashMatchEntryStub(PLDHashTable *table,
101 const PLDHashEntryHdr *entry,
102 const void *key)
103{
104 const PLDHashEntryStub *stub = (const PLDHashEntryStub *)entry;
105
106 return stub->key == key;
107}
108
109PR_IMPLEMENT(PRBool)
110PL_DHashMatchStringKey(PLDHashTable *table,
111 const PLDHashEntryHdr *entry,
112 const void *key)
113{
114 const PLDHashEntryStub *stub = (const PLDHashEntryStub *)entry;
115
116 /* XXX tolerate null keys on account of sloppy Mozilla callers. */
117 return stub->key == key ||
118 (stub->key && key && strcmp(stub->key, key) == 0);
119}
120
121PR_IMPLEMENT(void)
122PL_DHashMoveEntryStub(PLDHashTable *table,
123 const PLDHashEntryHdr *from,
124 PLDHashEntryHdr *to)
125{
126 memcpy(to, from, table->entrySize);
127}
128
129PR_IMPLEMENT(void)
130PL_DHashClearEntryStub(PLDHashTable *table, PLDHashEntryHdr *entry)
131{
132 memset(entry, 0, table->entrySize);
133}
134
135PR_IMPLEMENT(void)
136PL_DHashFreeStringKey(PLDHashTable *table, PLDHashEntryHdr *entry)
137{
138 const PLDHashEntryStub *stub = (const PLDHashEntryStub *)entry;
139
140 RTMemFree((void *) stub->key);
141 memset(entry, 0, table->entrySize);
142}
143
144PR_IMPLEMENT(void)
145PL_DHashFinalizeStub(PLDHashTable *table)
146{
147}
148
149static const PLDHashTableOps stub_ops = {
150 PL_DHashAllocTable,
151 PL_DHashFreeTable,
152 PL_DHashGetKeyStub,
153 PL_DHashVoidPtrKeyStub,
154 PL_DHashMatchEntryStub,
155 PL_DHashMoveEntryStub,
156 PL_DHashClearEntryStub,
157 PL_DHashFinalizeStub,
158 NULL
159};
160
161PR_IMPLEMENT(const PLDHashTableOps *)
162PL_DHashGetStubOps(void)
163{
164 return &stub_ops;
165}
166
167PR_IMPLEMENT(PLDHashTable *)
168PL_NewDHashTable(const PLDHashTableOps *ops, void *data, PRUint32 entrySize,
169 PRUint32 capacity)
170{
171 PLDHashTable *table;
172
173 table = (PLDHashTable *) RTMemAlloc(sizeof *table);
174 if (!table)
175 return NULL;
176 if (!PL_DHashTableInit(table, ops, data, entrySize, capacity)) {
177 RTMemFree(table);
178 return NULL;
179 }
180 return table;
181}
182
183PR_IMPLEMENT(void)
184PL_DHashTableDestroy(PLDHashTable *table)
185{
186 PL_DHashTableFinish(table);
187 RTMemFree(table);
188}
189
190PR_IMPLEMENT(PRBool)
191PL_DHashTableInit(PLDHashTable *table, const PLDHashTableOps *ops, void *data,
192 PRUint32 entrySize, PRUint32 capacity)
193{
194 int log2;
195 PRUint32 nbytes;
196
197#ifdef DEBUG
198 if (entrySize > 10 * sizeof(void *)) {
199 fprintf(stderr,
200 "pldhash: for the table at address %p, the given entrySize"
201 " of %lu %s favors chaining over double hashing.\n",
202 (void *)table,
203 (unsigned long) entrySize,
204 (entrySize > 16 * sizeof(void*)) ? "definitely" : "probably");
205 }
206#endif
207
208 table->ops = ops;
209 table->data = data;
210 if (capacity < PL_DHASH_MIN_SIZE)
211 capacity = PL_DHASH_MIN_SIZE;
212 log2 = PR_CeilingLog2(capacity);
213 capacity = PR_BIT(log2);
214 if (capacity >= PL_DHASH_SIZE_LIMIT)
215 return PR_FALSE;
216 table->hashShift = PL_DHASH_BITS - log2;
217 table->maxAlphaFrac = 0xC0; /* .75 */
218 table->minAlphaFrac = 0x40; /* .25 */
219 table->entrySize = entrySize;
220 table->entryCount = table->removedCount = 0;
221 table->generation = 0;
222 nbytes = capacity * entrySize;
223
224 table->entryStore = ops->allocTable(table, nbytes);
225 if (!table->entryStore)
226 return PR_FALSE;
227 memset(table->entryStore, 0, nbytes);
228 METER(memset(&table->stats, 0, sizeof table->stats));
229 return PR_TRUE;
230}
231
232/*
233 * Compute max and min load numbers (entry counts) from table params.
234 */
235#define MAX_LOAD(table, size) (((table)->maxAlphaFrac * (size)) >> 8)
236#define MIN_LOAD(table, size) (((table)->minAlphaFrac * (size)) >> 8)
237
238PR_IMPLEMENT(void)
239PL_DHashTableSetAlphaBounds(PLDHashTable *table,
240 float maxAlpha,
241 float minAlpha)
242{
243 PRUint32 size;
244
245 /*
246 * Reject obviously insane bounds, rather than trying to guess what the
247 * buggy caller intended.
248 */
249 Assert(0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha);
250 if (maxAlpha < 0.5 || 1 <= maxAlpha || minAlpha < 0)
251 return;
252
253 /*
254 * Ensure that at least one entry will always be free. If maxAlpha at
255 * minimum size leaves no entries free, reduce maxAlpha based on minimum
256 * size and the precision limit of maxAlphaFrac's fixed point format.
257 */
258 Assert(PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) >= 1);
259 if (PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) < 1) {
260 maxAlpha = (float)
261 (PL_DHASH_MIN_SIZE - PR_MAX(PL_DHASH_MIN_SIZE / 256, 1))
262 / PL_DHASH_MIN_SIZE;
263 }
264
265 /*
266 * Ensure that minAlpha is strictly less than half maxAlpha. Take care
267 * not to truncate an entry's worth of alpha when storing in minAlphaFrac
268 * (8-bit fixed point format).
269 */
270 Assert(minAlpha < maxAlpha / 2);
271 if (minAlpha >= maxAlpha / 2) {
272 size = PL_DHASH_TABLE_SIZE(table);
273 minAlpha = (size * maxAlpha - PR_MAX(size / 256, 1)) / (2 * size);
274 }
275
276 table->maxAlphaFrac = (uint8)(maxAlpha * 256);
277 table->minAlphaFrac = (uint8)(minAlpha * 256);
278}
279
280/*
281 * Double hashing needs the second hash code to be relatively prime to table
282 * size, so we simply make hash2 odd.
283 */
284#define HASH1(hash0, shift) ((hash0) >> (shift))
285#define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1)
286
287/*
288 * Reserve keyHash 0 for free entries and 1 for removed-entry sentinels. Note
289 * that a removed-entry sentinel need be stored only if the removed entry had
290 * a colliding entry added after it. Therefore we can use 1 as the collision
291 * flag in addition to the removed-entry sentinel value. Multiplicative hash
292 * uses the high order bits of keyHash, so this least-significant reservation
293 * should not hurt the hash function's effectiveness much.
294 *
295 * If you change any of these magic numbers, also update PL_DHASH_ENTRY_IS_LIVE
296 * in pldhash.h. It used to be private to pldhash.c, but then became public to
297 * assist iterator writers who inspect table->entryStore directly.
298 */
299#define COLLISION_FLAG ((PLDHashNumber) 1)
300#define MARK_ENTRY_FREE(entry) ((entry)->keyHash = 0)
301#define MARK_ENTRY_REMOVED(entry) ((entry)->keyHash = 1)
302#define ENTRY_IS_REMOVED(entry) ((entry)->keyHash == 1)
303#define ENTRY_IS_LIVE(entry) PL_DHASH_ENTRY_IS_LIVE(entry)
304#define ENSURE_LIVE_KEYHASH(hash0) if (hash0 < 2) hash0 -= 2; else (void)0
305
306/* Match an entry's keyHash against an unstored one computed from a key. */
307#define MATCH_ENTRY_KEYHASH(entry,hash0) \
308 (((entry)->keyHash & ~COLLISION_FLAG) == (hash0))
309
310/* Compute the address of the indexed entry in table. */
311#define ADDRESS_ENTRY(table, index) \
312 ((PLDHashEntryHdr *)((table)->entryStore + (index) * (table)->entrySize))
313
314PR_IMPLEMENT(void)
315PL_DHashTableFinish(PLDHashTable *table)
316{
317 char *entryAddr, *entryLimit;
318 PRUint32 entrySize;
319 PLDHashEntryHdr *entry;
320
321#ifdef DEBUG_XXXbrendan
322 static FILE *dumpfp = NULL;
323 if (!dumpfp) dumpfp = fopen("/tmp/pldhash.bigdump", "w");
324 if (dumpfp) {
325#ifdef MOZILLA_CLIENT
326 NS_TraceStack(1, dumpfp);
327#endif
328 PL_DHashTableDumpMeter(table, NULL, dumpfp);
329 fputc('\n', dumpfp);
330 }
331#endif
332
333 /* Call finalize before clearing entries, so it can enumerate them. */
334 table->ops->finalize(table);
335
336 /* Clear any remaining live entries. */
337 entryAddr = table->entryStore;
338 entrySize = table->entrySize;
339 entryLimit = entryAddr + PL_DHASH_TABLE_SIZE(table) * entrySize;
340 while (entryAddr < entryLimit) {
341 entry = (PLDHashEntryHdr *)entryAddr;
342 if (ENTRY_IS_LIVE(entry)) {
343 METER(table->stats.removeEnums++);
344 table->ops->clearEntry(table, entry);
345 }
346 entryAddr += entrySize;
347 }
348
349 /* Free entry storage last. */
350 table->ops->freeTable(table, table->entryStore);
351}
352
353static PLDHashEntryHdr * PL_DHASH_FASTCALL
354SearchTable(PLDHashTable *table, const void *key, PLDHashNumber keyHash,
355 PLDHashOperator op)
356{
357 PLDHashNumber hash1, hash2;
358 int hashShift, sizeLog2;
359 PLDHashEntryHdr *entry, *firstRemoved;
360 PLDHashMatchEntry matchEntry;
361 PRUint32 sizeMask;
362
363 METER(table->stats.searches++);
364 Assert(!(keyHash & COLLISION_FLAG));
365
366 /* Compute the primary hash address. */
367 hashShift = table->hashShift;
368 hash1 = HASH1(keyHash, hashShift);
369 entry = ADDRESS_ENTRY(table, hash1);
370
371 /* Miss: return space for a new entry. */
372 if (PL_DHASH_ENTRY_IS_FREE(entry)) {
373 METER(table->stats.misses++);
374 return entry;
375 }
376
377 /* Hit: return entry. */
378 matchEntry = table->ops->matchEntry;
379 if (MATCH_ENTRY_KEYHASH(entry, keyHash) && matchEntry(table, entry, key)) {
380 METER(table->stats.hits++);
381 return entry;
382 }
383
384 /* Collision: double hash. */
385 sizeLog2 = PL_DHASH_BITS - table->hashShift;
386 hash2 = HASH2(keyHash, sizeLog2, hashShift);
387 sizeMask = PR_BITMASK(sizeLog2);
388
389 /* Save the first removed entry pointer so PL_DHASH_ADD can recycle it. */
390 if (ENTRY_IS_REMOVED(entry)) {
391 firstRemoved = entry;
392 } else {
393 firstRemoved = NULL;
394 if (op == PL_DHASH_ADD)
395 entry->keyHash |= COLLISION_FLAG;
396 }
397
398 for (;;) {
399 METER(table->stats.steps++);
400 hash1 -= hash2;
401 hash1 &= sizeMask;
402
403 entry = ADDRESS_ENTRY(table, hash1);
404 if (PL_DHASH_ENTRY_IS_FREE(entry)) {
405 METER(table->stats.misses++);
406 return (firstRemoved && op == PL_DHASH_ADD) ? firstRemoved : entry;
407 }
408
409 if (MATCH_ENTRY_KEYHASH(entry, keyHash) &&
410 matchEntry(table, entry, key)) {
411 METER(table->stats.hits++);
412 return entry;
413 }
414
415 if (ENTRY_IS_REMOVED(entry)) {
416 if (!firstRemoved)
417 firstRemoved = entry;
418 } else {
419 if (op == PL_DHASH_ADD)
420 entry->keyHash |= COLLISION_FLAG;
421 }
422 }
423
424 /* NOTREACHED */
425 return NULL;
426}
427
428static PRBool
429ChangeTable(PLDHashTable *table, int deltaLog2)
430{
431 int oldLog2, newLog2;
432 PRUint32 oldCapacity, newCapacity;
433 char *newEntryStore, *oldEntryStore, *oldEntryAddr;
434 PRUint32 entrySize, i, nbytes;
435 PLDHashEntryHdr *oldEntry, *newEntry;
436 PLDHashGetKey getKey;
437 PLDHashMoveEntry moveEntry;
438
439#ifdef VBOX /* HACK ALERT! generation == PR_UINT32_MAX during enumeration. */
440 Assert(table->generation != PR_UINT32_MAX);
441 if (table->generation == PR_UINT32_MAX)
442 return PR_FALSE;
443#endif
444
445 /* Look, but don't touch, until we succeed in getting new entry store. */
446 oldLog2 = PL_DHASH_BITS - table->hashShift;
447 newLog2 = oldLog2 + deltaLog2;
448 oldCapacity = PR_BIT(oldLog2);
449 newCapacity = PR_BIT(newLog2);
450 if (newCapacity >= PL_DHASH_SIZE_LIMIT)
451 return PR_FALSE;
452 entrySize = table->entrySize;
453 nbytes = newCapacity * entrySize;
454
455 newEntryStore = table->ops->allocTable(table, nbytes);
456 if (!newEntryStore)
457 return PR_FALSE;
458
459 /* We can't fail from here on, so update table parameters. */
460 table->hashShift = PL_DHASH_BITS - newLog2;
461 table->removedCount = 0;
462 table->generation++;
463#ifdef VBOX /* HACK ALERT! generation == PR_UINT32_MAX during enumeration. */
464 if (table->generation == PR_UINT32_MAX)
465 table->generation++;
466#endif
467
468 /* Assign the new entry store to table. */
469 memset(newEntryStore, 0, nbytes);
470 oldEntryAddr = oldEntryStore = table->entryStore;
471 table->entryStore = newEntryStore;
472 getKey = table->ops->getKey;
473 moveEntry = table->ops->moveEntry;
474
475 /* Copy only live entries, leaving removed ones behind. */
476 for (i = 0; i < oldCapacity; i++) {
477 oldEntry = (PLDHashEntryHdr *)oldEntryAddr;
478 if (ENTRY_IS_LIVE(oldEntry)) {
479 oldEntry->keyHash &= ~COLLISION_FLAG;
480 newEntry = SearchTable(table, getKey(table, oldEntry),
481 oldEntry->keyHash, PL_DHASH_ADD);
482 Assert(PL_DHASH_ENTRY_IS_FREE(newEntry));
483 moveEntry(table, oldEntry, newEntry);
484 newEntry->keyHash = oldEntry->keyHash;
485 }
486 oldEntryAddr += entrySize;
487 }
488
489 table->ops->freeTable(table, oldEntryStore);
490 return PR_TRUE;
491}
492
493PR_IMPLEMENT(PLDHashEntryHdr *) PL_DHASH_FASTCALL
494PL_DHashTableOperate(PLDHashTable *table, const void *key, PLDHashOperator op)
495{
496 PLDHashNumber keyHash;
497 PLDHashEntryHdr *entry;
498 PRUint32 size;
499 int deltaLog2;
500
501 keyHash = table->ops->hashKey(table, key);
502 keyHash *= PL_DHASH_GOLDEN_RATIO;
503
504 /* Avoid 0 and 1 hash codes, they indicate free and removed entries. */
505 ENSURE_LIVE_KEYHASH(keyHash);
506 keyHash &= ~COLLISION_FLAG;
507
508 switch (op) {
509 case PL_DHASH_LOOKUP:
510 METER(table->stats.lookups++);
511 entry = SearchTable(table, key, keyHash, op);
512 break;
513
514 case PL_DHASH_ADD:
515 /*
516 * If alpha is >= .75, grow or compress the table. If key is already
517 * in the table, we may grow once more than necessary, but only if we
518 * are on the edge of being overloaded.
519 */
520 size = PL_DHASH_TABLE_SIZE(table);
521 if (table->entryCount + table->removedCount >= MAX_LOAD(table, size)) {
522 /* Compress if a quarter or more of all entries are removed. */
523 if (table->removedCount >= size >> 2) {
524 METER(table->stats.compresses++);
525 deltaLog2 = 0;
526 } else {
527 METER(table->stats.grows++);
528 deltaLog2 = 1;
529 }
530
531 /*
532 * Grow or compress table, returning null if ChangeTable fails and
533 * falling through might claim the last free entry.
534 */
535 if (!ChangeTable(table, deltaLog2) &&
536 table->entryCount + table->removedCount == size - 1) {
537 METER(table->stats.addFailures++);
538 return NULL;
539 }
540 }
541
542 /*
543 * Look for entry after possibly growing, so we don't have to add it,
544 * then skip it while growing the table and re-add it after.
545 */
546 entry = SearchTable(table, key, keyHash, op);
547 if (!ENTRY_IS_LIVE(entry)) {
548 /* Initialize the entry, indicating that it's no longer free. */
549 METER(table->stats.addMisses++);
550 if (ENTRY_IS_REMOVED(entry)) {
551 METER(table->stats.addOverRemoved++);
552 table->removedCount--;
553 keyHash |= COLLISION_FLAG;
554 }
555 if (table->ops->initEntry &&
556 !table->ops->initEntry(table, entry, key)) {
557 /* We haven't claimed entry yet; fail with null return. */
558 memset(entry + 1, 0, table->entrySize - sizeof *entry);
559 return NULL;
560 }
561 entry->keyHash = keyHash;
562 table->entryCount++;
563 }
564 METER(else table->stats.addHits++);
565 break;
566
567 case PL_DHASH_REMOVE:
568 entry = SearchTable(table, key, keyHash, op);
569 if (ENTRY_IS_LIVE(entry)) {
570 /* Clear this entry and mark it as "removed". */
571 METER(table->stats.removeHits++);
572 PL_DHashTableRawRemove(table, entry);
573
574 /* Shrink if alpha is <= .25 and table isn't too small already. */
575 size = PL_DHASH_TABLE_SIZE(table);
576 if (size > PL_DHASH_MIN_SIZE &&
577#ifdef VBOX /* HACK ALERT! generation == PR_UINT32_MAX during enumeration. */
578 /** @todo This is where IPC screws up, avoid the assertion in ChangeTable until it's fixed. */
579 table->generation != PR_UINT32_MAX &&
580#endif
581 table->entryCount <= MIN_LOAD(table, size)) {
582 METER(table->stats.shrinks++);
583 (void) ChangeTable(table, -1);
584 }
585 }
586 METER(else table->stats.removeMisses++);
587 entry = NULL;
588 break;
589
590 default:
591 AssertFailed();
592 entry = NULL;
593 }
594
595 return entry;
596}
597
598PR_IMPLEMENT(void)
599PL_DHashTableRawRemove(PLDHashTable *table, PLDHashEntryHdr *entry)
600{
601 PLDHashNumber keyHash; /* load first in case clearEntry goofs it */
602
603 Assert(PL_DHASH_ENTRY_IS_LIVE(entry));
604 keyHash = entry->keyHash;
605 table->ops->clearEntry(table, entry);
606 if (keyHash & COLLISION_FLAG) {
607 MARK_ENTRY_REMOVED(entry);
608 table->removedCount++;
609 } else {
610 METER(table->stats.removeFrees++);
611 MARK_ENTRY_FREE(entry);
612 }
613 table->entryCount--;
614}
615
616PR_IMPLEMENT(PRUint32)
617PL_DHashTableEnumerate(PLDHashTable *table, PLDHashEnumerator etor, void *arg)
618{
619 char *entryAddr, *entryLimit;
620 PRUint32 i, capacity, entrySize;
621 PRBool didRemove;
622 PLDHashEntryHdr *entry;
623 PLDHashOperator op;
624#ifdef VBOX /* HACK ALERT! generation == PR_UINT32_MAX during enumeration. */
625 PRUint32 generation;
626
627 /*
628 * The hack! Set generation to PR_UINT32_MAX during the enumeration so
629 * we can prevent ChangeTable from being called.
630 *
631 * This happens during ipcDConnectService::OnClientStateChange()
632 * / ipcDConnectService::DeleteInstance() now when running
633 * java clienttest list hostinfo and vboxwebsrv crashes. It's quite
634 * likely that the IPC code isn't following the rules here, but it
635 * looks more difficult to fix that just hacking this hash code.
636 */
637 generation = table->generation;
638 table->generation = PR_UINT32_MAX;
639#endif /* VBOX */
640 entryAddr = table->entryStore;
641 entrySize = table->entrySize;
642 capacity = PL_DHASH_TABLE_SIZE(table);
643 entryLimit = entryAddr + capacity * entrySize;
644 i = 0;
645 didRemove = PR_FALSE;
646 while (entryAddr < entryLimit) {
647 entry = (PLDHashEntryHdr *)entryAddr;
648 if (ENTRY_IS_LIVE(entry)) {
649 op = etor(table, entry, i++, arg);
650#ifdef VBOX /* HACK ALERT! generation == PR_UINT32_MAX during enumeration. */
651 Assert(table->generation == PR_UINT32_MAX);
652#endif
653 if (op & PL_DHASH_REMOVE) {
654 METER(table->stats.removeEnums++);
655 PL_DHashTableRawRemove(table, entry);
656 didRemove = PR_TRUE;
657 }
658 if (op & PL_DHASH_STOP)
659 break;
660 }
661 entryAddr += entrySize;
662 }
663#ifdef VBOX /* HACK ALERT! generation == PR_UINT32_MAX during enumeration. */
664 table->generation = generation;
665#endif
666
667 /*
668 * Shrink or compress if a quarter or more of all entries are removed, or
669 * if the table is underloaded according to the configured minimum alpha,
670 * and is not minimal-size already. Do this only if we removed above, so
671 * non-removing enumerations can count on stable table->entryStore until
672 * the next non-lookup-Operate or removing-Enumerate.
673 */
674 if (didRemove &&
675 (table->removedCount >= capacity >> 2 ||
676 (capacity > PL_DHASH_MIN_SIZE &&
677 table->entryCount <= MIN_LOAD(table, capacity)))) {
678 METER(table->stats.enumShrinks++);
679 capacity = table->entryCount;
680 capacity += capacity >> 1;
681 if (capacity < PL_DHASH_MIN_SIZE)
682 capacity = PL_DHASH_MIN_SIZE;
683 (void) ChangeTable(table,
684 PR_CeilingLog2(capacity)
685 - (PL_DHASH_BITS - table->hashShift));
686 }
687 return i;
688}
689
690#ifdef PL_DHASHMETER
691#include <math.h>
692
693PR_IMPLEMENT(void)
694PL_DHashTableDumpMeter(PLDHashTable *table, PLDHashEnumerator dump, FILE *fp)
695{
696 char *entryAddr;
697 PRUint32 entrySize, entryCount;
698 int hashShift, sizeLog2;
699 PRUint32 i, tableSize, sizeMask, chainLen, maxChainLen, chainCount;
700 PLDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2;
701 double sqsum, mean, variance, sigma;
702 PLDHashEntryHdr *entry, *probe;
703
704 entryAddr = table->entryStore;
705 entrySize = table->entrySize;
706 hashShift = table->hashShift;
707 sizeLog2 = PL_DHASH_BITS - hashShift;
708 tableSize = PL_DHASH_TABLE_SIZE(table);
709 sizeMask = PR_BITMASK(sizeLog2);
710 chainCount = maxChainLen = 0;
711 hash2 = 0;
712 sqsum = 0;
713
714 for (i = 0; i < tableSize; i++) {
715 entry = (PLDHashEntryHdr *)entryAddr;
716 entryAddr += entrySize;
717 if (!ENTRY_IS_LIVE(entry))
718 continue;
719 hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, hashShift);
720 saveHash1 = hash1;
721 probe = ADDRESS_ENTRY(table, hash1);
722 chainLen = 1;
723 if (probe == entry) {
724 /* Start of a (possibly unit-length) chain. */
725 chainCount++;
726 } else {
727 hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, sizeLog2,
728 hashShift);
729 do {
730 chainLen++;
731 hash1 -= hash2;
732 hash1 &= sizeMask;
733 probe = ADDRESS_ENTRY(table, hash1);
734 } while (probe != entry);
735 }
736 sqsum += chainLen * chainLen;
737 if (chainLen > maxChainLen) {
738 maxChainLen = chainLen;
739 maxChainHash1 = saveHash1;
740 maxChainHash2 = hash2;
741 }
742 }
743
744 entryCount = table->entryCount;
745 if (entryCount && chainCount) {
746 mean = (double)entryCount / chainCount;
747 variance = chainCount * sqsum - entryCount * entryCount;
748 if (variance < 0 || chainCount == 1)
749 variance = 0;
750 else
751 variance /= chainCount * (chainCount - 1);
752 sigma = sqrt(variance);
753 } else {
754 mean = sigma = 0;
755 }
756
757 fprintf(fp, "Double hashing statistics:\n");
758 fprintf(fp, " table size (in entries): %u\n", tableSize);
759 fprintf(fp, " number of entries: %u\n", table->entryCount);
760 fprintf(fp, " number of removed entries: %u\n", table->removedCount);
761 fprintf(fp, " number of searches: %u\n", table->stats.searches);
762 fprintf(fp, " number of hits: %u\n", table->stats.hits);
763 fprintf(fp, " number of misses: %u\n", table->stats.misses);
764 fprintf(fp, " mean steps per search: %g\n", table->stats.searches ?
765 (double)table->stats.steps
766 / table->stats.searches :
767 0.);
768 fprintf(fp, " mean hash chain length: %g\n", mean);
769 fprintf(fp, " standard deviation: %g\n", sigma);
770 fprintf(fp, " maximum hash chain length: %u\n", maxChainLen);
771 fprintf(fp, " number of lookups: %u\n", table->stats.lookups);
772 fprintf(fp, " adds that made a new entry: %u\n", table->stats.addMisses);
773 fprintf(fp, "adds that recycled removeds: %u\n", table->stats.addOverRemoved);
774 fprintf(fp, " adds that found an entry: %u\n", table->stats.addHits);
775 fprintf(fp, " add failures: %u\n", table->stats.addFailures);
776 fprintf(fp, " useful removes: %u\n", table->stats.removeHits);
777 fprintf(fp, " useless removes: %u\n", table->stats.removeMisses);
778 fprintf(fp, "removes that freed an entry: %u\n", table->stats.removeFrees);
779 fprintf(fp, " removes while enumerating: %u\n", table->stats.removeEnums);
780 fprintf(fp, " number of grows: %u\n", table->stats.grows);
781 fprintf(fp, " number of shrinks: %u\n", table->stats.shrinks);
782 fprintf(fp, " number of compresses: %u\n", table->stats.compresses);
783 fprintf(fp, "number of enumerate shrinks: %u\n", table->stats.enumShrinks);
784
785 if (dump && maxChainLen && hash2) {
786 fputs("Maximum hash chain:\n", fp);
787 hash1 = maxChainHash1;
788 hash2 = maxChainHash2;
789 entry = ADDRESS_ENTRY(table, hash1);
790 i = 0;
791 do {
792 if (dump(table, entry, i++, fp) != PL_DHASH_NEXT)
793 break;
794 hash1 -= hash2;
795 hash1 &= sizeMask;
796 entry = ADDRESS_ENTRY(table, hash1);
797 } while (PL_DHASH_ENTRY_IS_BUSY(entry));
798 }
799}
800#endif /* PL_DHASHMETER */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette