VirtualBox

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

Last change on this file since 78394 was 49491, checked in by vboxsync, 11 years ago

libs/xpcom: fixed a bunch of pointer size vs. integer size warnings

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