2
# User Mike Hommey <mh+mozilla@glandium.org>
3
# Date 1312873597 -7200
4
# Node ID 3d20269baeeef329887625951d46872c15b4c861
5
# Parent 4c3bcc010d85f1ac063e76da0329d4df3ae6dadc
6
Bug 675618 - Compile pldhash as C++. r=bsmedberg
8
--- a/xpcom/build/Makefile.in
9
+++ b/xpcom/build/Makefile.in
10
@@ -55,10 +55,6 @@ EXPORT_LIBRARY = 1
12
MOZILLA_INTERNAL_API = 1
15
- $(XPCOM_GLUE_SRC_LCSRCS) \
19
$(XPCOM_GLUE_SRC_LCPPSRCS) \
20
$(XPCOM_GLUENS_SRC_LCPPSRCS) \
21
@@ -128,7 +124,7 @@ EXPORTS_mozilla = \
25
-GARBAGE += $(XPCOM_GLUE_SRC_LCSRCS) $(XPCOM_GLUE_SRC_LCPPSRCS) $(XPCOM_GLUENS_SRC_LCPPSRCS) $(wildcard *.$(OBJ_SUFFIX))
26
+GARBAGE += $(XPCOM_GLUE_SRC_LCPPSRCS) $(XPCOM_GLUENS_SRC_LCPPSRCS) $(wildcard *.$(OBJ_SUFFIX))
28
include $(topsrcdir)/config/config.mk
29
include $(topsrcdir)/ipc/chromium/chromium-config.mk
30
@@ -148,5 +144,5 @@ ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
31
CXXFLAGS += $(TK_CFLAGS)
34
-export:: $(XPCOM_GLUE_SRC_CSRCS) $(XPCOM_GLUE_SRC_CPPSRCS) $(XPCOM_GLUENS_SRC_CPPSRCS)
35
+export:: $(XPCOM_GLUE_SRC_CPPSRCS) $(XPCOM_GLUENS_SRC_CPPSRCS)
37
--- a/xpcom/glue/Makefile.in
38
+++ b/xpcom/glue/Makefile.in
39
@@ -55,10 +55,6 @@ LOCAL_INCLUDES = \
40
-I$(srcdir)/../build \
44
- $(XPCOM_GLUE_SRC_LCSRCS) \
48
$(XPCOM_GLUE_SRC_LCPPSRCS) \
49
$(XPCOM_GLUENS_SRC_LCPPSRCS) \
50
--- a/xpcom/glue/nomozalloc/Makefile.in
51
+++ b/xpcom/glue/nomozalloc/Makefile.in
52
@@ -54,10 +54,6 @@ LOCAL_INCLUDES = \
53
-I$(srcdir)/../../build \
57
- $(XPCOM_GLUE_SRC_LCSRCS) \
61
$(XPCOM_GLUE_SRC_LCPPSRCS) \
62
$(XPCOM_GLUENS_SRC_LCPPSRCS) \
63
@@ -69,7 +65,7 @@ SDK_LIBRARY =
64
$(LIB_PREFIX)xpcomglue_s_nomozalloc.$(LIB_SUFFIX) \
67
-GARBAGE += $(CSRCS) $(CPPSRCS) DeadlockDetector.h SSE.h arm.h
68
+GARBAGE += $(CPPSRCS) DeadlockDetector.h SSE.h arm.h
70
# we don't want the shared lib, but we want to force the creation of a static lib.
72
@@ -94,7 +90,7 @@ OS_COMPILE_CFLAGS += -Zl
73
DEFINES += -D_USE_ANSI_CPP
76
-export:: $(XPCOM_GLUE_SRC_CSRCS) $(XPCOM_GLUE_SRC_CPPSRCS) $(XPCOM_GLUENS_SRC_CPPSRCS) $(topsrcdir)/xpcom/glue/nsStringAPI.cpp $(topsrcdir)/xpcom/glue/GenericModule.cpp $(topsrcdir)/xpcom/glue/DeadlockDetector.h $(topsrcdir)/xpcom/glue/SSE.h $(topsrcdir)/xpcom/glue/arm.h
77
+export:: $(XPCOM_GLUE_SRC_CPPSRCS) $(XPCOM_GLUENS_SRC_CPPSRCS) $(topsrcdir)/xpcom/glue/nsStringAPI.cpp $(topsrcdir)/xpcom/glue/GenericModule.cpp $(topsrcdir)/xpcom/glue/DeadlockDetector.h $(topsrcdir)/xpcom/glue/SSE.h $(topsrcdir)/xpcom/glue/arm.h
80
ifdef TARGET_XPCOM_ABI
81
--- a/xpcom/glue/objs.mk
82
+++ b/xpcom/glue/objs.mk
85
# ***** END LICENSE BLOCK *****
87
-XPCOM_GLUE_SRC_LCSRCS = \
91
-XPCOM_GLUE_SRC_CSRCS = $(addprefix $(topsrcdir)/xpcom/glue/, $(XPCOM_GLUE_SRC_LCSRCS))
93
XPCOM_GLUE_SRC_LCPPSRCS = \
94
nsArrayEnumerator.cpp \
96
@@ -66,6 +60,7 @@ XPCOM_GLUE_SRC_LCPPSRCS = \
97
nsCycleCollectionParticipant.cpp \
98
nsCycleCollectorUtils.cpp \
103
XPCOM_GLUE_SRC_CPPSRCS = $(addprefix $(topsrcdir)/xpcom/glue/, $(XPCOM_GLUE_SRC_LCPPSRCS))
104
--- a/xpcom/glue/standalone/Makefile.in
105
+++ b/xpcom/glue/standalone/Makefile.in
106
@@ -71,10 +71,6 @@ LINKSRC = nsGlueLinkingNull.cpp
107
$(warning TinderboxPrint:<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=298044">Error: XPCOM Glue</a>)
111
- $(XPCOM_GLUE_SRC_LCSRCS) \
115
$(XPCOM_GLUE_SRC_LCPPSRCS) \
117
@@ -104,7 +100,7 @@ USE_STATIC_LIBS = 1
118
# Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc
121
-GARBAGE += $(XPCOM_GLUE_SRC_LCSRCS) $(XPCOM_GLUE_SRC_LCPPSRCS) $(wildcard *.$(OBJ_SUFFIX))
122
+GARBAGE += $(XPCOM_GLUE_SRC_LCPPSRCS) $(wildcard *.$(OBJ_SUFFIX))
126
@@ -117,7 +113,7 @@ OS_COMPILE_CFLAGS += -Zl
127
DEFINES += -D_USE_ANSI_CPP
130
-export:: $(XPCOM_GLUE_SRC_CSRCS) $(XPCOM_GLUE_SRC_CPPSRCS) $(topsrcdir)/xpcom/glue/nsStringAPI.cpp
131
+export:: $(XPCOM_GLUE_SRC_CPPSRCS) $(topsrcdir)/xpcom/glue/nsStringAPI.cpp
134
GARBAGE += nsStringAPI.cpp
135
--- a/xpcom/glue/pldhash.c
138
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
139
-/* ***** BEGIN LICENSE BLOCK *****
140
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
142
- * The contents of this file are subject to the Mozilla Public License Version
143
- * 1.1 (the "License"); you may not use this file except in compliance with
144
- * the License. You may obtain a copy of the License at
145
- * http://www.mozilla.org/MPL/
147
- * Software distributed under the License is distributed on an "AS IS" basis,
148
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
149
- * for the specific language governing rights and limitations under the
152
- * The Original Code is Mozilla JavaScript code.
154
- * The Initial Developer of the Original Code is
155
- * Netscape Communications Corporation.
156
- * Portions created by the Initial Developer are Copyright (C) 1999-2001
157
- * the Initial Developer. All Rights Reserved.
160
- * Brendan Eich <brendan@mozilla.org> (Original Author)
161
- * Chris Waterson <waterson@netscape.com>
162
- * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
164
- * Alternatively, the contents of this file may be used under the terms of
165
- * either of the GNU General Public License Version 2 or later (the "GPL"),
166
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
167
- * in which case the provisions of the GPL or the LGPL are applicable instead
168
- * of those above. If you wish to allow use of your version of this file only
169
- * under the terms of either the GPL or the LGPL, and not to allow others to
170
- * use your version of this file under the terms of the MPL, indicate your
171
- * decision by deleting the provisions above and replace them with the notice
172
- * and other provisions required by the GPL or the LGPL. If you do not delete
173
- * the provisions above, a recipient may use your version of this file under
174
- * the terms of any one of the MPL, the GPL or the LGPL.
176
- * ***** END LICENSE BLOCK ***** */
179
- * Double hashing implementation.
180
- * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!!
186
-#include "pldhash.h"
187
-#include "nsDebug.h" /* for PR_ASSERT */
189
-#ifdef PL_DHASHMETER
190
-# if defined MOZILLA_CLIENT && defined DEBUG_XXXbrendan
191
-# include "nsTraceMalloc.h"
195
-# define METER(x) /* nothing */
199
- * The following DEBUG-only code is used to assert that calls to one of
200
- * table->ops or to an enumerator do not cause re-entry into a call that
201
- * can mutate the table. The recursion level is stored in additional
202
- * space allocated at the end of the entry store to avoid changing
203
- * PLDHashTable, which could cause issues when mixing DEBUG and
204
- * non-DEBUG components.
208
-#define JSDHASH_ONELINE_ASSERT PR_ASSERT
209
-#define RECURSION_LEVEL(table_) (*(PRUint32*)(table_->entryStore + \
210
- PL_DHASH_TABLE_SIZE(table_) * \
211
- table_->entrySize))
213
- * Most callers that assert about the recursion level don't care about
214
- * this magical value because they are asserting that mutation is
215
- * allowed (and therefore the level is 0 or 1, depending on whether they
218
- * Only PL_DHashTableFinish needs to allow this special value.
220
-#define IMMUTABLE_RECURSION_LEVEL ((PRUint32)-1)
222
-#define RECURSION_LEVEL_SAFE_TO_FINISH(table_) \
223
- (RECURSION_LEVEL(table_) == 0 || \
224
- RECURSION_LEVEL(table_) == IMMUTABLE_RECURSION_LEVEL)
226
-#define ENTRY_STORE_EXTRA sizeof(PRUint32)
227
-#define INCREMENT_RECURSION_LEVEL(table_) \
229
- if (RECURSION_LEVEL(table_) != IMMUTABLE_RECURSION_LEVEL) \
230
- ++RECURSION_LEVEL(table_); \
232
-#define DECREMENT_RECURSION_LEVEL(table_) \
234
- if (RECURSION_LEVEL(table_) != IMMUTABLE_RECURSION_LEVEL) { \
235
- NS_ASSERTION(RECURSION_LEVEL(table_) > 0, "RECURSION_LEVEL(table_) > 0"); \
236
- --RECURSION_LEVEL(table_); \
242
-#define ENTRY_STORE_EXTRA 0
243
-#define INCREMENT_RECURSION_LEVEL(table_) PR_BEGIN_MACRO PR_END_MACRO
244
-#define DECREMENT_RECURSION_LEVEL(table_) PR_BEGIN_MACRO PR_END_MACRO
246
-#endif /* defined(DEBUG) */
249
-PL_DHashAllocTable(PLDHashTable *table, PRUint32 nbytes)
251
- return malloc(nbytes);
255
-PL_DHashFreeTable(PLDHashTable *table, void *ptr)
261
-PL_DHashStringKey(PLDHashTable *table, const void *key)
264
- const unsigned char *s;
267
- for (s = (const unsigned char *) key; *s != '\0'; s++)
268
- h = PR_ROTATE_LEFT32(h, 4) ^ *s;
273
-PL_DHashVoidPtrKeyStub(PLDHashTable *table, const void *key)
275
- return (PLDHashNumber)(unsigned long)key >> 2;
279
-PL_DHashMatchEntryStub(PLDHashTable *table,
280
- const PLDHashEntryHdr *entry,
283
- const PLDHashEntryStub *stub = (const PLDHashEntryStub *)entry;
285
- return stub->key == key;
289
-PL_DHashMatchStringKey(PLDHashTable *table,
290
- const PLDHashEntryHdr *entry,
293
- const PLDHashEntryStub *stub = (const PLDHashEntryStub *)entry;
295
- /* XXX tolerate null keys on account of sloppy Mozilla callers. */
296
- return stub->key == key ||
297
- (stub->key && key &&
298
- strcmp((const char *) stub->key, (const char *) key) == 0);
302
-PL_DHashMoveEntryStub(PLDHashTable *table,
303
- const PLDHashEntryHdr *from,
304
- PLDHashEntryHdr *to)
306
- memcpy(to, from, table->entrySize);
310
-PL_DHashClearEntryStub(PLDHashTable *table, PLDHashEntryHdr *entry)
312
- memset(entry, 0, table->entrySize);
316
-PL_DHashFreeStringKey(PLDHashTable *table, PLDHashEntryHdr *entry)
318
- const PLDHashEntryStub *stub = (const PLDHashEntryStub *)entry;
320
- free((void *) stub->key);
321
- memset(entry, 0, table->entrySize);
325
-PL_DHashFinalizeStub(PLDHashTable *table)
329
-static const PLDHashTableOps stub_ops = {
330
- PL_DHashAllocTable,
332
- PL_DHashVoidPtrKeyStub,
333
- PL_DHashMatchEntryStub,
334
- PL_DHashMoveEntryStub,
335
- PL_DHashClearEntryStub,
336
- PL_DHashFinalizeStub,
340
-const PLDHashTableOps *
341
-PL_DHashGetStubOps(void)
347
-PL_NewDHashTable(const PLDHashTableOps *ops, void *data, PRUint32 entrySize,
350
- PLDHashTable *table;
352
- table = (PLDHashTable *) malloc(sizeof *table);
355
- if (!PL_DHashTableInit(table, ops, data, entrySize, capacity)) {
363
-PL_DHashTableDestroy(PLDHashTable *table)
365
- PL_DHashTableFinish(table);
370
-PL_DHashTableInit(PLDHashTable *table, const PLDHashTableOps *ops, void *data,
371
- PRUint32 entrySize, PRUint32 capacity)
377
- if (entrySize > 10 * sizeof(void *)) {
379
- "pldhash: for the table at address %p, the given entrySize"
380
- " of %lu %s favors chaining over double hashing.\n",
382
- (unsigned long) entrySize,
383
- (entrySize > 16 * sizeof(void*)) ? "definitely" : "probably");
388
- table->data = data;
389
- if (capacity < PL_DHASH_MIN_SIZE)
390
- capacity = PL_DHASH_MIN_SIZE;
392
- PR_CEILING_LOG2(log2, capacity);
394
- capacity = PR_BIT(log2);
395
- if (capacity >= PL_DHASH_SIZE_LIMIT)
397
- table->hashShift = PL_DHASH_BITS - log2;
398
- table->maxAlphaFrac = (PRUint8)(0x100 * PL_DHASH_DEFAULT_MAX_ALPHA);
399
- table->minAlphaFrac = (PRUint8)(0x100 * PL_DHASH_DEFAULT_MIN_ALPHA);
400
- table->entrySize = entrySize;
401
- table->entryCount = table->removedCount = 0;
402
- table->generation = 0;
403
- nbytes = capacity * entrySize;
405
- table->entryStore = (char *) ops->allocTable(table,
406
- nbytes + ENTRY_STORE_EXTRA);
407
- if (!table->entryStore)
409
- memset(table->entryStore, 0, nbytes);
410
- METER(memset(&table->stats, 0, sizeof table->stats));
413
- RECURSION_LEVEL(table) = 0;
420
- * Compute max and min load numbers (entry counts) from table params.
422
-#define MAX_LOAD(table, size) (((table)->maxAlphaFrac * (size)) >> 8)
423
-#define MIN_LOAD(table, size) (((table)->minAlphaFrac * (size)) >> 8)
426
-PL_DHashTableSetAlphaBounds(PLDHashTable *table,
433
- * Reject obviously insane bounds, rather than trying to guess what the
434
- * buggy caller intended.
436
- NS_ASSERTION(0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha,
437
- "0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha");
438
- if (maxAlpha < 0.5 || 1 <= maxAlpha || minAlpha < 0)
442
- * Ensure that at least one entry will always be free. If maxAlpha at
443
- * minimum size leaves no entries free, reduce maxAlpha based on minimum
444
- * size and the precision limit of maxAlphaFrac's fixed point format.
446
- NS_ASSERTION(PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) >= 1,
447
- "PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) >= 1");
448
- if (PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) < 1) {
450
- (PL_DHASH_MIN_SIZE - PR_MAX(PL_DHASH_MIN_SIZE / 256, 1))
451
- / PL_DHASH_MIN_SIZE;
455
- * Ensure that minAlpha is strictly less than half maxAlpha. Take care
456
- * not to truncate an entry's worth of alpha when storing in minAlphaFrac
457
- * (8-bit fixed point format).
459
- NS_ASSERTION(minAlpha < maxAlpha / 2,
460
- "minAlpha < maxAlpha / 2");
461
- if (minAlpha >= maxAlpha / 2) {
462
- size = PL_DHASH_TABLE_SIZE(table);
463
- minAlpha = (size * maxAlpha - PR_MAX(size / 256, 1)) / (2 * size);
466
- table->maxAlphaFrac = (PRUint8)(maxAlpha * 256);
467
- table->minAlphaFrac = (PRUint8)(minAlpha * 256);
471
- * Double hashing needs the second hash code to be relatively prime to table
472
- * size, so we simply make hash2 odd.
474
-#define HASH1(hash0, shift) ((hash0) >> (shift))
475
-#define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1)
478
- * Reserve keyHash 0 for free entries and 1 for removed-entry sentinels. Note
479
- * that a removed-entry sentinel need be stored only if the removed entry had
480
- * a colliding entry added after it. Therefore we can use 1 as the collision
481
- * flag in addition to the removed-entry sentinel value. Multiplicative hash
482
- * uses the high order bits of keyHash, so this least-significant reservation
483
- * should not hurt the hash function's effectiveness much.
485
- * If you change any of these magic numbers, also update PL_DHASH_ENTRY_IS_LIVE
486
- * in pldhash.h. It used to be private to pldhash.c, but then became public to
487
- * assist iterator writers who inspect table->entryStore directly.
489
-#define COLLISION_FLAG ((PLDHashNumber) 1)
490
-#define MARK_ENTRY_FREE(entry) ((entry)->keyHash = 0)
491
-#define MARK_ENTRY_REMOVED(entry) ((entry)->keyHash = 1)
492
-#define ENTRY_IS_REMOVED(entry) ((entry)->keyHash == 1)
493
-#define ENTRY_IS_LIVE(entry) PL_DHASH_ENTRY_IS_LIVE(entry)
494
-#define ENSURE_LIVE_KEYHASH(hash0) if (hash0 < 2) hash0 -= 2; else (void)0
496
-/* Match an entry's keyHash against an unstored one computed from a key. */
497
-#define MATCH_ENTRY_KEYHASH(entry,hash0) \
498
- (((entry)->keyHash & ~COLLISION_FLAG) == (hash0))
500
-/* Compute the address of the indexed entry in table. */
501
-#define ADDRESS_ENTRY(table, index) \
502
- ((PLDHashEntryHdr *)((table)->entryStore + (index) * (table)->entrySize))
505
-PL_DHashTableFinish(PLDHashTable *table)
507
- char *entryAddr, *entryLimit;
508
- PRUint32 entrySize;
509
- PLDHashEntryHdr *entry;
511
-#ifdef DEBUG_XXXbrendan
512
- static FILE *dumpfp = NULL;
513
- if (!dumpfp) dumpfp = fopen("/tmp/pldhash.bigdump", "w");
515
-#ifdef MOZILLA_CLIENT
516
- NS_TraceStack(1, dumpfp);
518
- PL_DHashTableDumpMeter(table, NULL, dumpfp);
519
- fputc('\n', dumpfp);
523
- INCREMENT_RECURSION_LEVEL(table);
525
- /* Call finalize before clearing entries, so it can enumerate them. */
526
- table->ops->finalize(table);
528
- /* Clear any remaining live entries. */
529
- entryAddr = table->entryStore;
530
- entrySize = table->entrySize;
531
- entryLimit = entryAddr + PL_DHASH_TABLE_SIZE(table) * entrySize;
532
- while (entryAddr < entryLimit) {
533
- entry = (PLDHashEntryHdr *)entryAddr;
534
- if (ENTRY_IS_LIVE(entry)) {
535
- METER(table->stats.removeEnums++);
536
- table->ops->clearEntry(table, entry);
538
- entryAddr += entrySize;
541
- DECREMENT_RECURSION_LEVEL(table);
542
- NS_ASSERTION(RECURSION_LEVEL_SAFE_TO_FINISH(table),
543
- "RECURSION_LEVEL_SAFE_TO_FINISH(table)");
545
- /* Free entry storage last. */
546
- table->ops->freeTable(table, table->entryStore);
549
-static PLDHashEntryHdr * PL_DHASH_FASTCALL
550
-SearchTable(PLDHashTable *table, const void *key, PLDHashNumber keyHash,
551
- PLDHashOperator op)
553
- PLDHashNumber hash1, hash2;
554
- int hashShift, sizeLog2;
555
- PLDHashEntryHdr *entry, *firstRemoved;
556
- PLDHashMatchEntry matchEntry;
559
- METER(table->stats.searches++);
560
- NS_ASSERTION(!(keyHash & COLLISION_FLAG),
561
- "!(keyHash & COLLISION_FLAG)");
563
- /* Compute the primary hash address. */
564
- hashShift = table->hashShift;
565
- hash1 = HASH1(keyHash, hashShift);
566
- entry = ADDRESS_ENTRY(table, hash1);
568
- /* Miss: return space for a new entry. */
569
- if (PL_DHASH_ENTRY_IS_FREE(entry)) {
570
- METER(table->stats.misses++);
574
- /* Hit: return entry. */
575
- matchEntry = table->ops->matchEntry;
576
- if (MATCH_ENTRY_KEYHASH(entry, keyHash) && matchEntry(table, entry, key)) {
577
- METER(table->stats.hits++);
581
- /* Collision: double hash. */
582
- sizeLog2 = PL_DHASH_BITS - table->hashShift;
583
- hash2 = HASH2(keyHash, sizeLog2, hashShift);
584
- sizeMask = PR_BITMASK(sizeLog2);
586
- /* Save the first removed entry pointer so PL_DHASH_ADD can recycle it. */
587
- firstRemoved = NULL;
590
- if (NS_UNLIKELY(ENTRY_IS_REMOVED(entry))) {
592
- firstRemoved = entry;
594
- if (op == PL_DHASH_ADD)
595
- entry->keyHash |= COLLISION_FLAG;
598
- METER(table->stats.steps++);
602
- entry = ADDRESS_ENTRY(table, hash1);
603
- if (PL_DHASH_ENTRY_IS_FREE(entry)) {
604
- METER(table->stats.misses++);
605
- return (firstRemoved && op == PL_DHASH_ADD) ? firstRemoved : entry;
608
- if (MATCH_ENTRY_KEYHASH(entry, keyHash) &&
609
- matchEntry(table, entry, key)) {
610
- METER(table->stats.hits++);
620
- * This is a copy of SearchTable, used by ChangeTable, hardcoded to
621
- * 1. assume |op == PL_DHASH_ADD|,
622
- * 2. assume that |key| will never match an existing entry, and
623
- * 3. assume that no entries have been removed from the current table
625
- * Avoiding the need for |key| means we can avoid needing a way to map
626
- * entries to keys, which means callers can use complex key types more
629
-static PLDHashEntryHdr * PL_DHASH_FASTCALL
630
-FindFreeEntry(PLDHashTable *table, PLDHashNumber keyHash)
632
- PLDHashNumber hash1, hash2;
633
- int hashShift, sizeLog2;
634
- PLDHashEntryHdr *entry;
637
- METER(table->stats.searches++);
638
- NS_ASSERTION(!(keyHash & COLLISION_FLAG),
639
- "!(keyHash & COLLISION_FLAG)");
641
- /* Compute the primary hash address. */
642
- hashShift = table->hashShift;
643
- hash1 = HASH1(keyHash, hashShift);
644
- entry = ADDRESS_ENTRY(table, hash1);
646
- /* Miss: return space for a new entry. */
647
- if (PL_DHASH_ENTRY_IS_FREE(entry)) {
648
- METER(table->stats.misses++);
652
- /* Collision: double hash. */
653
- sizeLog2 = PL_DHASH_BITS - table->hashShift;
654
- hash2 = HASH2(keyHash, sizeLog2, hashShift);
655
- sizeMask = PR_BITMASK(sizeLog2);
658
- NS_ASSERTION(!ENTRY_IS_REMOVED(entry),
659
- "!ENTRY_IS_REMOVED(entry)");
660
- entry->keyHash |= COLLISION_FLAG;
662
- METER(table->stats.steps++);
666
- entry = ADDRESS_ENTRY(table, hash1);
667
- if (PL_DHASH_ENTRY_IS_FREE(entry)) {
668
- METER(table->stats.misses++);
678
-ChangeTable(PLDHashTable *table, int deltaLog2)
680
- int oldLog2, newLog2;
681
- PRUint32 oldCapacity, newCapacity;
682
- char *newEntryStore, *oldEntryStore, *oldEntryAddr;
683
- PRUint32 entrySize, i, nbytes;
684
- PLDHashEntryHdr *oldEntry, *newEntry;
685
- PLDHashMoveEntry moveEntry;
687
- PRUint32 recursionLevel;
690
- /* Look, but don't touch, until we succeed in getting new entry store. */
691
- oldLog2 = PL_DHASH_BITS - table->hashShift;
692
- newLog2 = oldLog2 + deltaLog2;
693
- oldCapacity = PR_BIT(oldLog2);
694
- newCapacity = PR_BIT(newLog2);
695
- if (newCapacity >= PL_DHASH_SIZE_LIMIT)
697
- entrySize = table->entrySize;
698
- nbytes = newCapacity * entrySize;
700
- newEntryStore = (char *) table->ops->allocTable(table,
701
- nbytes + ENTRY_STORE_EXTRA);
702
- if (!newEntryStore)
705
- /* We can't fail from here on, so update table parameters. */
707
- recursionLevel = RECURSION_LEVEL(table);
709
- table->hashShift = PL_DHASH_BITS - newLog2;
710
- table->removedCount = 0;
711
- table->generation++;
713
- /* Assign the new entry store to table. */
714
- memset(newEntryStore, 0, nbytes);
715
- oldEntryAddr = oldEntryStore = table->entryStore;
716
- table->entryStore = newEntryStore;
717
- moveEntry = table->ops->moveEntry;
719
- RECURSION_LEVEL(table) = recursionLevel;
722
- /* Copy only live entries, leaving removed ones behind. */
723
- for (i = 0; i < oldCapacity; i++) {
724
- oldEntry = (PLDHashEntryHdr *)oldEntryAddr;
725
- if (ENTRY_IS_LIVE(oldEntry)) {
726
- oldEntry->keyHash &= ~COLLISION_FLAG;
727
- newEntry = FindFreeEntry(table, oldEntry->keyHash);
728
- NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(newEntry),
729
- "PL_DHASH_ENTRY_IS_FREE(newEntry)");
730
- moveEntry(table, oldEntry, newEntry);
731
- newEntry->keyHash = oldEntry->keyHash;
733
- oldEntryAddr += entrySize;
736
- table->ops->freeTable(table, oldEntryStore);
740
-PLDHashEntryHdr * PL_DHASH_FASTCALL
741
-PL_DHashTableOperate(PLDHashTable *table, const void *key, PLDHashOperator op)
743
- PLDHashNumber keyHash;
744
- PLDHashEntryHdr *entry;
748
- NS_ASSERTION(op == PL_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0,
749
- "op == PL_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0");
750
- INCREMENT_RECURSION_LEVEL(table);
752
- keyHash = table->ops->hashKey(table, key);
753
- keyHash *= PL_DHASH_GOLDEN_RATIO;
755
- /* Avoid 0 and 1 hash codes, they indicate free and removed entries. */
756
- ENSURE_LIVE_KEYHASH(keyHash);
757
- keyHash &= ~COLLISION_FLAG;
760
- case PL_DHASH_LOOKUP:
761
- METER(table->stats.lookups++);
762
- entry = SearchTable(table, key, keyHash, op);
767
- * If alpha is >= .75, grow or compress the table. If key is already
768
- * in the table, we may grow once more than necessary, but only if we
769
- * are on the edge of being overloaded.
771
- size = PL_DHASH_TABLE_SIZE(table);
772
- if (table->entryCount + table->removedCount >= MAX_LOAD(table, size)) {
773
- /* Compress if a quarter or more of all entries are removed. */
774
- if (table->removedCount >= size >> 2) {
775
- METER(table->stats.compresses++);
778
- METER(table->stats.grows++);
783
- * Grow or compress table, returning null if ChangeTable fails and
784
- * falling through might claim the last free entry.
786
- if (!ChangeTable(table, deltaLog2) &&
787
- table->entryCount + table->removedCount == size - 1) {
788
- METER(table->stats.addFailures++);
795
- * Look for entry after possibly growing, so we don't have to add it,
796
- * then skip it while growing the table and re-add it after.
798
- entry = SearchTable(table, key, keyHash, op);
799
- if (!ENTRY_IS_LIVE(entry)) {
800
- /* Initialize the entry, indicating that it's no longer free. */
801
- METER(table->stats.addMisses++);
802
- if (ENTRY_IS_REMOVED(entry)) {
803
- METER(table->stats.addOverRemoved++);
804
- table->removedCount--;
805
- keyHash |= COLLISION_FLAG;
807
- if (table->ops->initEntry &&
808
- !table->ops->initEntry(table, entry, key)) {
809
- /* We haven't claimed entry yet; fail with null return. */
810
- memset(entry + 1, 0, table->entrySize - sizeof *entry);
814
- entry->keyHash = keyHash;
815
- table->entryCount++;
817
- METER(else table->stats.addHits++);
820
- case PL_DHASH_REMOVE:
821
- entry = SearchTable(table, key, keyHash, op);
822
- if (ENTRY_IS_LIVE(entry)) {
823
- /* Clear this entry and mark it as "removed". */
824
- METER(table->stats.removeHits++);
825
- PL_DHashTableRawRemove(table, entry);
827
- /* Shrink if alpha is <= .25 and table isn't too small already. */
828
- size = PL_DHASH_TABLE_SIZE(table);
829
- if (size > PL_DHASH_MIN_SIZE &&
830
- table->entryCount <= MIN_LOAD(table, size)) {
831
- METER(table->stats.shrinks++);
832
- (void) ChangeTable(table, -1);
835
- METER(else table->stats.removeMisses++);
840
- NS_NOTREACHED("0");
844
- DECREMENT_RECURSION_LEVEL(table);
850
-PL_DHashTableRawRemove(PLDHashTable *table, PLDHashEntryHdr *entry)
852
- PLDHashNumber keyHash; /* load first in case clearEntry goofs it */
854
- NS_ASSERTION(RECURSION_LEVEL(table) != IMMUTABLE_RECURSION_LEVEL,
855
- "RECURSION_LEVEL(table) != IMMUTABLE_RECURSION_LEVEL");
857
- NS_ASSERTION(PL_DHASH_ENTRY_IS_LIVE(entry),
858
- "PL_DHASH_ENTRY_IS_LIVE(entry)");
859
- keyHash = entry->keyHash;
860
- table->ops->clearEntry(table, entry);
861
- if (keyHash & COLLISION_FLAG) {
862
- MARK_ENTRY_REMOVED(entry);
863
- table->removedCount++;
865
- METER(table->stats.removeFrees++);
866
- MARK_ENTRY_FREE(entry);
868
- table->entryCount--;
872
-PL_DHashTableEnumerate(PLDHashTable *table, PLDHashEnumerator etor, void *arg)
874
- char *entryAddr, *entryLimit;
875
- PRUint32 i, capacity, entrySize, ceiling;
877
- PLDHashEntryHdr *entry;
878
- PLDHashOperator op;
880
- INCREMENT_RECURSION_LEVEL(table);
882
- entryAddr = table->entryStore;
883
- entrySize = table->entrySize;
884
- capacity = PL_DHASH_TABLE_SIZE(table);
885
- entryLimit = entryAddr + capacity * entrySize;
887
- didRemove = PR_FALSE;
888
- while (entryAddr < entryLimit) {
889
- entry = (PLDHashEntryHdr *)entryAddr;
890
- if (ENTRY_IS_LIVE(entry)) {
891
- op = etor(table, entry, i++, arg);
892
- if (op & PL_DHASH_REMOVE) {
893
- METER(table->stats.removeEnums++);
894
- PL_DHashTableRawRemove(table, entry);
895
- didRemove = PR_TRUE;
897
- if (op & PL_DHASH_STOP)
900
- entryAddr += entrySize;
903
- NS_ASSERTION(!didRemove || RECURSION_LEVEL(table) == 1,
904
- "!didRemove || RECURSION_LEVEL(table) == 1");
907
- * Shrink or compress if a quarter or more of all entries are removed, or
908
- * if the table is underloaded according to the configured minimum alpha,
909
- * and is not minimal-size already. Do this only if we removed above, so
910
- * non-removing enumerations can count on stable table->entryStore until
911
- * the next non-lookup-Operate or removing-Enumerate.
914
- (table->removedCount >= capacity >> 2 ||
915
- (capacity > PL_DHASH_MIN_SIZE &&
916
- table->entryCount <= MIN_LOAD(table, capacity)))) {
917
- METER(table->stats.enumShrinks++);
918
- capacity = table->entryCount;
919
- capacity += capacity >> 1;
920
- if (capacity < PL_DHASH_MIN_SIZE)
921
- capacity = PL_DHASH_MIN_SIZE;
923
- PR_CEILING_LOG2(ceiling, capacity);
924
- ceiling -= PL_DHASH_BITS - table->hashShift;
926
- (void) ChangeTable(table, ceiling);
929
- DECREMENT_RECURSION_LEVEL(table);
936
-PL_DHashMarkTableImmutable(PLDHashTable *table)
938
- RECURSION_LEVEL(table) = IMMUTABLE_RECURSION_LEVEL;
942
-#ifdef PL_DHASHMETER
946
-PL_DHashTableDumpMeter(PLDHashTable *table, PLDHashEnumerator dump, FILE *fp)
949
- PRUint32 entrySize, entryCount;
950
- int hashShift, sizeLog2;
951
- PRUint32 i, tableSize, sizeMask, chainLen, maxChainLen, chainCount;
952
- PLDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2;
953
- double sqsum, mean, variance, sigma;
954
- PLDHashEntryHdr *entry, *probe;
956
- entryAddr = table->entryStore;
957
- entrySize = table->entrySize;
958
- hashShift = table->hashShift;
959
- sizeLog2 = PL_DHASH_BITS - hashShift;
960
- tableSize = PL_DHASH_TABLE_SIZE(table);
961
- sizeMask = PR_BITMASK(sizeLog2);
962
- chainCount = maxChainLen = 0;
966
- for (i = 0; i < tableSize; i++) {
967
- entry = (PLDHashEntryHdr *)entryAddr;
968
- entryAddr += entrySize;
969
- if (!ENTRY_IS_LIVE(entry))
971
- hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, hashShift);
973
- probe = ADDRESS_ENTRY(table, hash1);
975
- if (probe == entry) {
976
- /* Start of a (possibly unit-length) chain. */
979
- hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, sizeLog2,
985
- probe = ADDRESS_ENTRY(table, hash1);
986
- } while (probe != entry);
988
- sqsum += chainLen * chainLen;
989
- if (chainLen > maxChainLen) {
990
- maxChainLen = chainLen;
991
- maxChainHash1 = saveHash1;
992
- maxChainHash2 = hash2;
996
- entryCount = table->entryCount;
997
- if (entryCount && chainCount) {
998
- mean = (double)entryCount / chainCount;
999
- variance = chainCount * sqsum - entryCount * entryCount;
1000
- if (variance < 0 || chainCount == 1)
1003
- variance /= chainCount * (chainCount - 1);
1004
- sigma = sqrt(variance);
1009
- fprintf(fp, "Double hashing statistics:\n");
1010
- fprintf(fp, " table size (in entries): %u\n", tableSize);
1011
- fprintf(fp, " number of entries: %u\n", table->entryCount);
1012
- fprintf(fp, " number of removed entries: %u\n", table->removedCount);
1013
- fprintf(fp, " number of searches: %u\n", table->stats.searches);
1014
- fprintf(fp, " number of hits: %u\n", table->stats.hits);
1015
- fprintf(fp, " number of misses: %u\n", table->stats.misses);
1016
- fprintf(fp, " mean steps per search: %g\n", table->stats.searches ?
1017
- (double)table->stats.steps
1018
- / table->stats.searches :
1020
- fprintf(fp, " mean hash chain length: %g\n", mean);
1021
- fprintf(fp, " standard deviation: %g\n", sigma);
1022
- fprintf(fp, " maximum hash chain length: %u\n", maxChainLen);
1023
- fprintf(fp, " number of lookups: %u\n", table->stats.lookups);
1024
- fprintf(fp, " adds that made a new entry: %u\n", table->stats.addMisses);
1025
- fprintf(fp, "adds that recycled removeds: %u\n", table->stats.addOverRemoved);
1026
- fprintf(fp, " adds that found an entry: %u\n", table->stats.addHits);
1027
- fprintf(fp, " add failures: %u\n", table->stats.addFailures);
1028
- fprintf(fp, " useful removes: %u\n", table->stats.removeHits);
1029
- fprintf(fp, " useless removes: %u\n", table->stats.removeMisses);
1030
- fprintf(fp, "removes that freed an entry: %u\n", table->stats.removeFrees);
1031
- fprintf(fp, " removes while enumerating: %u\n", table->stats.removeEnums);
1032
- fprintf(fp, " number of grows: %u\n", table->stats.grows);
1033
- fprintf(fp, " number of shrinks: %u\n", table->stats.shrinks);
1034
- fprintf(fp, " number of compresses: %u\n", table->stats.compresses);
1035
- fprintf(fp, "number of enumerate shrinks: %u\n", table->stats.enumShrinks);
1037
- if (dump && maxChainLen && hash2) {
1038
- fputs("Maximum hash chain:\n", fp);
1039
- hash1 = maxChainHash1;
1040
- hash2 = maxChainHash2;
1041
- entry = ADDRESS_ENTRY(table, hash1);
1044
- if (dump(table, entry, i++, fp) != PL_DHASH_NEXT)
1047
- hash1 &= sizeMask;
1048
- entry = ADDRESS_ENTRY(table, hash1);
1049
- } while (PL_DHASH_ENTRY_IS_BUSY(entry));
1052
-#endif /* PL_DHASHMETER */
1054
+++ b/xpcom/glue/pldhash.cpp
1056
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
1057
+/* ***** BEGIN LICENSE BLOCK *****
1058
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
1060
+ * The contents of this file are subject to the Mozilla Public License Version
1061
+ * 1.1 (the "License"); you may not use this file except in compliance with
1062
+ * the License. You may obtain a copy of the License at
1063
+ * http://www.mozilla.org/MPL/
1065
+ * Software distributed under the License is distributed on an "AS IS" basis,
1066
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
1067
+ * for the specific language governing rights and limitations under the
1070
+ * The Original Code is Mozilla JavaScript code.
1072
+ * The Initial Developer of the Original Code is
1073
+ * Netscape Communications Corporation.
1074
+ * Portions created by the Initial Developer are Copyright (C) 1999-2001
1075
+ * the Initial Developer. All Rights Reserved.
1078
+ * Brendan Eich <brendan@mozilla.org> (Original Author)
1079
+ * Chris Waterson <waterson@netscape.com>
1080
+ * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
1082
+ * Alternatively, the contents of this file may be used under the terms of
1083
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
1084
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
1085
+ * in which case the provisions of the GPL or the LGPL are applicable instead
1086
+ * of those above. If you wish to allow use of your version of this file only
1087
+ * under the terms of either the GPL or the LGPL, and not to allow others to
1088
+ * use your version of this file under the terms of the MPL, indicate your
1089
+ * decision by deleting the provisions above and replace them with the notice
1090
+ * and other provisions required by the GPL or the LGPL. If you do not delete
1091
+ * the provisions above, a recipient may use your version of this file under
1092
+ * the terms of any one of the MPL, the GPL or the LGPL.
1094
+ * ***** END LICENSE BLOCK ***** */
1097
+ * Double hashing implementation.
1098
+ * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!!
1101
+#include <stdlib.h>
1102
+#include <string.h>
1104
+#include "pldhash.h"
1105
+#include "nsDebug.h" /* for PR_ASSERT */
1107
+#ifdef PL_DHASHMETER
1108
+# if defined MOZILLA_CLIENT && defined DEBUG_XXXbrendan
1109
+# include "nsTraceMalloc.h"
1111
+# define METER(x) x
1113
+# define METER(x) /* nothing */
1117
+ * The following DEBUG-only code is used to assert that calls to one of
1118
+ * table->ops or to an enumerator do not cause re-entry into a call that
1119
+ * can mutate the table. The recursion level is stored in additional
1120
+ * space allocated at the end of the entry store to avoid changing
1121
+ * PLDHashTable, which could cause issues when mixing DEBUG and
1122
+ * non-DEBUG components.
1126
+#define JSDHASH_ONELINE_ASSERT PR_ASSERT
1127
+#define RECURSION_LEVEL(table_) (*(PRUint32*)(table_->entryStore + \
1128
+ PL_DHASH_TABLE_SIZE(table_) * \
1129
+ table_->entrySize))
1131
+ * Most callers that assert about the recursion level don't care about
1132
+ * this magical value because they are asserting that mutation is
1133
+ * allowed (and therefore the level is 0 or 1, depending on whether they
1134
+ * incremented it).
1136
+ * Only PL_DHashTableFinish needs to allow this special value.
1138
+#define IMMUTABLE_RECURSION_LEVEL ((PRUint32)-1)
1140
+#define RECURSION_LEVEL_SAFE_TO_FINISH(table_) \
1141
+ (RECURSION_LEVEL(table_) == 0 || \
1142
+ RECURSION_LEVEL(table_) == IMMUTABLE_RECURSION_LEVEL)
1144
+#define ENTRY_STORE_EXTRA sizeof(PRUint32)
1145
+#define INCREMENT_RECURSION_LEVEL(table_) \
1147
+ if (RECURSION_LEVEL(table_) != IMMUTABLE_RECURSION_LEVEL) \
1148
+ ++RECURSION_LEVEL(table_); \
1150
+#define DECREMENT_RECURSION_LEVEL(table_) \
1152
+ if (RECURSION_LEVEL(table_) != IMMUTABLE_RECURSION_LEVEL) { \
1153
+ NS_ASSERTION(RECURSION_LEVEL(table_) > 0, "RECURSION_LEVEL(table_) > 0"); \
1154
+ --RECURSION_LEVEL(table_); \
1160
+#define ENTRY_STORE_EXTRA 0
1161
+#define INCREMENT_RECURSION_LEVEL(table_) PR_BEGIN_MACRO PR_END_MACRO
1162
+#define DECREMENT_RECURSION_LEVEL(table_) PR_BEGIN_MACRO PR_END_MACRO
1164
+#endif /* defined(DEBUG) */
1167
+PL_DHashAllocTable(PLDHashTable *table, PRUint32 nbytes)
1169
+ return malloc(nbytes);
1173
+PL_DHashFreeTable(PLDHashTable *table, void *ptr)
1179
+PL_DHashStringKey(PLDHashTable *table, const void *key)
1182
+ const unsigned char *s;
1185
+ for (s = (const unsigned char *) key; *s != '\0'; s++)
1186
+ h = PR_ROTATE_LEFT32(h, 4) ^ *s;
1191
+PL_DHashVoidPtrKeyStub(PLDHashTable *table, const void *key)
1193
+ return (PLDHashNumber)(unsigned long)key >> 2;
1197
+PL_DHashMatchEntryStub(PLDHashTable *table,
1198
+ const PLDHashEntryHdr *entry,
1201
+ const PLDHashEntryStub *stub = (const PLDHashEntryStub *)entry;
1203
+ return stub->key == key;
1207
+PL_DHashMatchStringKey(PLDHashTable *table,
1208
+ const PLDHashEntryHdr *entry,
1211
+ const PLDHashEntryStub *stub = (const PLDHashEntryStub *)entry;
1213
+ /* XXX tolerate null keys on account of sloppy Mozilla callers. */
1214
+ return stub->key == key ||
1215
+ (stub->key && key &&
1216
+ strcmp((const char *) stub->key, (const char *) key) == 0);
1220
+PL_DHashMoveEntryStub(PLDHashTable *table,
1221
+ const PLDHashEntryHdr *from,
1222
+ PLDHashEntryHdr *to)
1224
+ memcpy(to, from, table->entrySize);
1228
+PL_DHashClearEntryStub(PLDHashTable *table, PLDHashEntryHdr *entry)
1230
+ memset(entry, 0, table->entrySize);
1234
+PL_DHashFreeStringKey(PLDHashTable *table, PLDHashEntryHdr *entry)
1236
+ const PLDHashEntryStub *stub = (const PLDHashEntryStub *)entry;
1238
+ free((void *) stub->key);
1239
+ memset(entry, 0, table->entrySize);
1243
+PL_DHashFinalizeStub(PLDHashTable *table)
1247
+static const PLDHashTableOps stub_ops = {
1248
+ PL_DHashAllocTable,
1249
+ PL_DHashFreeTable,
1250
+ PL_DHashVoidPtrKeyStub,
1251
+ PL_DHashMatchEntryStub,
1252
+ PL_DHashMoveEntryStub,
1253
+ PL_DHashClearEntryStub,
1254
+ PL_DHashFinalizeStub,
1258
+const PLDHashTableOps *
1259
+PL_DHashGetStubOps(void)
1265
+PL_NewDHashTable(const PLDHashTableOps *ops, void *data, PRUint32 entrySize,
1266
+ PRUint32 capacity)
1268
+ PLDHashTable *table;
1270
+ table = (PLDHashTable *) malloc(sizeof *table);
1273
+ if (!PL_DHashTableInit(table, ops, data, entrySize, capacity)) {
1281
+PL_DHashTableDestroy(PLDHashTable *table)
1283
+ PL_DHashTableFinish(table);
1288
+PL_DHashTableInit(PLDHashTable *table, const PLDHashTableOps *ops, void *data,
1289
+ PRUint32 entrySize, PRUint32 capacity)
1295
+ if (entrySize > 10 * sizeof(void *)) {
1297
+ "pldhash: for the table at address %p, the given entrySize"
1298
+ " of %lu %s favors chaining over double hashing.\n",
1300
+ (unsigned long) entrySize,
1301
+ (entrySize > 16 * sizeof(void*)) ? "definitely" : "probably");
1306
+ table->data = data;
1307
+ if (capacity < PL_DHASH_MIN_SIZE)
1308
+ capacity = PL_DHASH_MIN_SIZE;
1310
+ PR_CEILING_LOG2(log2, capacity);
1312
+ capacity = PR_BIT(log2);
1313
+ if (capacity >= PL_DHASH_SIZE_LIMIT)
1315
+ table->hashShift = PL_DHASH_BITS - log2;
1316
+ table->maxAlphaFrac = (PRUint8)(0x100 * PL_DHASH_DEFAULT_MAX_ALPHA);
1317
+ table->minAlphaFrac = (PRUint8)(0x100 * PL_DHASH_DEFAULT_MIN_ALPHA);
1318
+ table->entrySize = entrySize;
1319
+ table->entryCount = table->removedCount = 0;
1320
+ table->generation = 0;
1321
+ nbytes = capacity * entrySize;
1323
+ table->entryStore = (char *) ops->allocTable(table,
1324
+ nbytes + ENTRY_STORE_EXTRA);
1325
+ if (!table->entryStore)
1327
+ memset(table->entryStore, 0, nbytes);
1328
+ METER(memset(&table->stats, 0, sizeof table->stats));
1331
+ RECURSION_LEVEL(table) = 0;
1338
+ * Compute max and min load numbers (entry counts) from table params.
1340
+#define MAX_LOAD(table, size) (((table)->maxAlphaFrac * (size)) >> 8)
1341
+#define MIN_LOAD(table, size) (((table)->minAlphaFrac * (size)) >> 8)
1344
+PL_DHashTableSetAlphaBounds(PLDHashTable *table,
1351
+ * Reject obviously insane bounds, rather than trying to guess what the
1352
+ * buggy caller intended.
1354
+ NS_ASSERTION(0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha,
1355
+ "0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha");
1356
+ if (maxAlpha < 0.5 || 1 <= maxAlpha || minAlpha < 0)
1360
+ * Ensure that at least one entry will always be free. If maxAlpha at
1361
+ * minimum size leaves no entries free, reduce maxAlpha based on minimum
1362
+ * size and the precision limit of maxAlphaFrac's fixed point format.
1364
+ NS_ASSERTION(PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) >= 1,
1365
+ "PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) >= 1");
1366
+ if (PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) < 1) {
1367
+ maxAlpha = (float)
1368
+ (PL_DHASH_MIN_SIZE - PR_MAX(PL_DHASH_MIN_SIZE / 256, 1))
1369
+ / PL_DHASH_MIN_SIZE;
1373
+ * Ensure that minAlpha is strictly less than half maxAlpha. Take care
1374
+ * not to truncate an entry's worth of alpha when storing in minAlphaFrac
1375
+ * (8-bit fixed point format).
1377
+ NS_ASSERTION(minAlpha < maxAlpha / 2,
1378
+ "minAlpha < maxAlpha / 2");
1379
+ if (minAlpha >= maxAlpha / 2) {
1380
+ size = PL_DHASH_TABLE_SIZE(table);
1381
+ minAlpha = (size * maxAlpha - PR_MAX(size / 256, 1)) / (2 * size);
1384
+ table->maxAlphaFrac = (PRUint8)(maxAlpha * 256);
1385
+ table->minAlphaFrac = (PRUint8)(minAlpha * 256);
1389
+ * Double hashing needs the second hash code to be relatively prime to table
1390
+ * size, so we simply make hash2 odd.
1392
+#define HASH1(hash0, shift) ((hash0) >> (shift))
1393
+#define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1)
1396
+ * Reserve keyHash 0 for free entries and 1 for removed-entry sentinels. Note
1397
+ * that a removed-entry sentinel need be stored only if the removed entry had
1398
+ * a colliding entry added after it. Therefore we can use 1 as the collision
1399
+ * flag in addition to the removed-entry sentinel value. Multiplicative hash
1400
+ * uses the high order bits of keyHash, so this least-significant reservation
1401
+ * should not hurt the hash function's effectiveness much.
1403
+ * If you change any of these magic numbers, also update PL_DHASH_ENTRY_IS_LIVE
1404
+ * in pldhash.h. It used to be private to pldhash.c, but then became public to
1405
+ * assist iterator writers who inspect table->entryStore directly.
1407
+#define COLLISION_FLAG ((PLDHashNumber) 1)
1408
+#define MARK_ENTRY_FREE(entry) ((entry)->keyHash = 0)
1409
+#define MARK_ENTRY_REMOVED(entry) ((entry)->keyHash = 1)
1410
+#define ENTRY_IS_REMOVED(entry) ((entry)->keyHash == 1)
1411
+#define ENTRY_IS_LIVE(entry) PL_DHASH_ENTRY_IS_LIVE(entry)
1412
+#define ENSURE_LIVE_KEYHASH(hash0) if (hash0 < 2) hash0 -= 2; else (void)0
1414
+/* Match an entry's keyHash against an unstored one computed from a key. */
1415
+#define MATCH_ENTRY_KEYHASH(entry,hash0) \
1416
+ (((entry)->keyHash & ~COLLISION_FLAG) == (hash0))
1418
+/* Compute the address of the indexed entry in table. */
1419
+#define ADDRESS_ENTRY(table, index) \
1420
+ ((PLDHashEntryHdr *)((table)->entryStore + (index) * (table)->entrySize))
1423
+PL_DHashTableFinish(PLDHashTable *table)
1425
+ char *entryAddr, *entryLimit;
1426
+ PRUint32 entrySize;
1427
+ PLDHashEntryHdr *entry;
1429
+#ifdef DEBUG_XXXbrendan
1430
+ static FILE *dumpfp = NULL;
1431
+ if (!dumpfp) dumpfp = fopen("/tmp/pldhash.bigdump", "w");
1433
+#ifdef MOZILLA_CLIENT
1434
+ NS_TraceStack(1, dumpfp);
1436
+ PL_DHashTableDumpMeter(table, NULL, dumpfp);
1437
+ fputc('\n', dumpfp);
1441
+ INCREMENT_RECURSION_LEVEL(table);
1443
+ /* Call finalize before clearing entries, so it can enumerate them. */
1444
+ table->ops->finalize(table);
1446
+ /* Clear any remaining live entries. */
1447
+ entryAddr = table->entryStore;
1448
+ entrySize = table->entrySize;
1449
+ entryLimit = entryAddr + PL_DHASH_TABLE_SIZE(table) * entrySize;
1450
+ while (entryAddr < entryLimit) {
1451
+ entry = (PLDHashEntryHdr *)entryAddr;
1452
+ if (ENTRY_IS_LIVE(entry)) {
1453
+ METER(table->stats.removeEnums++);
1454
+ table->ops->clearEntry(table, entry);
1456
+ entryAddr += entrySize;
1459
+ DECREMENT_RECURSION_LEVEL(table);
1460
+ NS_ASSERTION(RECURSION_LEVEL_SAFE_TO_FINISH(table),
1461
+ "RECURSION_LEVEL_SAFE_TO_FINISH(table)");
1463
+ /* Free entry storage last. */
1464
+ table->ops->freeTable(table, table->entryStore);
1467
+static PLDHashEntryHdr * PL_DHASH_FASTCALL
1468
+SearchTable(PLDHashTable *table, const void *key, PLDHashNumber keyHash,
1469
+ PLDHashOperator op)
1471
+ PLDHashNumber hash1, hash2;
1472
+ int hashShift, sizeLog2;
1473
+ PLDHashEntryHdr *entry, *firstRemoved;
1474
+ PLDHashMatchEntry matchEntry;
1475
+ PRUint32 sizeMask;
1477
+ METER(table->stats.searches++);
1478
+ NS_ASSERTION(!(keyHash & COLLISION_FLAG),
1479
+ "!(keyHash & COLLISION_FLAG)");
1481
+ /* Compute the primary hash address. */
1482
+ hashShift = table->hashShift;
1483
+ hash1 = HASH1(keyHash, hashShift);
1484
+ entry = ADDRESS_ENTRY(table, hash1);
1486
+ /* Miss: return space for a new entry. */
1487
+ if (PL_DHASH_ENTRY_IS_FREE(entry)) {
1488
+ METER(table->stats.misses++);
1492
+ /* Hit: return entry. */
1493
+ matchEntry = table->ops->matchEntry;
1494
+ if (MATCH_ENTRY_KEYHASH(entry, keyHash) && matchEntry(table, entry, key)) {
1495
+ METER(table->stats.hits++);
1499
+ /* Collision: double hash. */
1500
+ sizeLog2 = PL_DHASH_BITS - table->hashShift;
1501
+ hash2 = HASH2(keyHash, sizeLog2, hashShift);
1502
+ sizeMask = PR_BITMASK(sizeLog2);
1504
+ /* Save the first removed entry pointer so PL_DHASH_ADD can recycle it. */
1505
+ firstRemoved = NULL;
1508
+ if (NS_UNLIKELY(ENTRY_IS_REMOVED(entry))) {
1509
+ if (!firstRemoved)
1510
+ firstRemoved = entry;
1512
+ if (op == PL_DHASH_ADD)
1513
+ entry->keyHash |= COLLISION_FLAG;
1516
+ METER(table->stats.steps++);
1518
+ hash1 &= sizeMask;
1520
+ entry = ADDRESS_ENTRY(table, hash1);
1521
+ if (PL_DHASH_ENTRY_IS_FREE(entry)) {
1522
+ METER(table->stats.misses++);
1523
+ return (firstRemoved && op == PL_DHASH_ADD) ? firstRemoved : entry;
1526
+ if (MATCH_ENTRY_KEYHASH(entry, keyHash) &&
1527
+ matchEntry(table, entry, key)) {
1528
+ METER(table->stats.hits++);
1538
+ * This is a copy of SearchTable, used by ChangeTable, hardcoded to
1539
+ * 1. assume |op == PL_DHASH_ADD|,
1540
+ * 2. assume that |key| will never match an existing entry, and
1541
+ * 3. assume that no entries have been removed from the current table
1543
+ * Avoiding the need for |key| means we can avoid needing a way to map
1544
+ * entries to keys, which means callers can use complex key types more
1547
+static PLDHashEntryHdr * PL_DHASH_FASTCALL
1548
+FindFreeEntry(PLDHashTable *table, PLDHashNumber keyHash)
1550
+ PLDHashNumber hash1, hash2;
1551
+ int hashShift, sizeLog2;
1552
+ PLDHashEntryHdr *entry;
1553
+ PRUint32 sizeMask;
1555
+ METER(table->stats.searches++);
1556
+ NS_ASSERTION(!(keyHash & COLLISION_FLAG),
1557
+ "!(keyHash & COLLISION_FLAG)");
1559
+ /* Compute the primary hash address. */
1560
+ hashShift = table->hashShift;
1561
+ hash1 = HASH1(keyHash, hashShift);
1562
+ entry = ADDRESS_ENTRY(table, hash1);
1564
+ /* Miss: return space for a new entry. */
1565
+ if (PL_DHASH_ENTRY_IS_FREE(entry)) {
1566
+ METER(table->stats.misses++);
1570
+ /* Collision: double hash. */
1571
+ sizeLog2 = PL_DHASH_BITS - table->hashShift;
1572
+ hash2 = HASH2(keyHash, sizeLog2, hashShift);
1573
+ sizeMask = PR_BITMASK(sizeLog2);
1576
+ NS_ASSERTION(!ENTRY_IS_REMOVED(entry),
1577
+ "!ENTRY_IS_REMOVED(entry)");
1578
+ entry->keyHash |= COLLISION_FLAG;
1580
+ METER(table->stats.steps++);
1582
+ hash1 &= sizeMask;
1584
+ entry = ADDRESS_ENTRY(table, hash1);
1585
+ if (PL_DHASH_ENTRY_IS_FREE(entry)) {
1586
+ METER(table->stats.misses++);
1596
+ChangeTable(PLDHashTable *table, int deltaLog2)
1598
+ int oldLog2, newLog2;
1599
+ PRUint32 oldCapacity, newCapacity;
1600
+ char *newEntryStore, *oldEntryStore, *oldEntryAddr;
1601
+ PRUint32 entrySize, i, nbytes;
1602
+ PLDHashEntryHdr *oldEntry, *newEntry;
1603
+ PLDHashMoveEntry moveEntry;
1605
+ PRUint32 recursionLevel;
1608
+ /* Look, but don't touch, until we succeed in getting new entry store. */
1609
+ oldLog2 = PL_DHASH_BITS - table->hashShift;
1610
+ newLog2 = oldLog2 + deltaLog2;
1611
+ oldCapacity = PR_BIT(oldLog2);
1612
+ newCapacity = PR_BIT(newLog2);
1613
+ if (newCapacity >= PL_DHASH_SIZE_LIMIT)
1615
+ entrySize = table->entrySize;
1616
+ nbytes = newCapacity * entrySize;
1618
+ newEntryStore = (char *) table->ops->allocTable(table,
1619
+ nbytes + ENTRY_STORE_EXTRA);
1620
+ if (!newEntryStore)
1623
+ /* We can't fail from here on, so update table parameters. */
1625
+ recursionLevel = RECURSION_LEVEL(table);
1627
+ table->hashShift = PL_DHASH_BITS - newLog2;
1628
+ table->removedCount = 0;
1629
+ table->generation++;
1631
+ /* Assign the new entry store to table. */
1632
+ memset(newEntryStore, 0, nbytes);
1633
+ oldEntryAddr = oldEntryStore = table->entryStore;
1634
+ table->entryStore = newEntryStore;
1635
+ moveEntry = table->ops->moveEntry;
1637
+ RECURSION_LEVEL(table) = recursionLevel;
1640
+ /* Copy only live entries, leaving removed ones behind. */
1641
+ for (i = 0; i < oldCapacity; i++) {
1642
+ oldEntry = (PLDHashEntryHdr *)oldEntryAddr;
1643
+ if (ENTRY_IS_LIVE(oldEntry)) {
1644
+ oldEntry->keyHash &= ~COLLISION_FLAG;
1645
+ newEntry = FindFreeEntry(table, oldEntry->keyHash);
1646
+ NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(newEntry),
1647
+ "PL_DHASH_ENTRY_IS_FREE(newEntry)");
1648
+ moveEntry(table, oldEntry, newEntry);
1649
+ newEntry->keyHash = oldEntry->keyHash;
1651
+ oldEntryAddr += entrySize;
1654
+ table->ops->freeTable(table, oldEntryStore);
1658
+PLDHashEntryHdr * PL_DHASH_FASTCALL
1659
+PL_DHashTableOperate(PLDHashTable *table, const void *key, PLDHashOperator op)
1661
+ PLDHashNumber keyHash;
1662
+ PLDHashEntryHdr *entry;
1666
+ NS_ASSERTION(op == PL_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0,
1667
+ "op == PL_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0");
1668
+ INCREMENT_RECURSION_LEVEL(table);
1670
+ keyHash = table->ops->hashKey(table, key);
1671
+ keyHash *= PL_DHASH_GOLDEN_RATIO;
1673
+ /* Avoid 0 and 1 hash codes, they indicate free and removed entries. */
1674
+ ENSURE_LIVE_KEYHASH(keyHash);
1675
+ keyHash &= ~COLLISION_FLAG;
1678
+ case PL_DHASH_LOOKUP:
1679
+ METER(table->stats.lookups++);
1680
+ entry = SearchTable(table, key, keyHash, op);
1683
+ case PL_DHASH_ADD:
1685
+ * If alpha is >= .75, grow or compress the table. If key is already
1686
+ * in the table, we may grow once more than necessary, but only if we
1687
+ * are on the edge of being overloaded.
1689
+ size = PL_DHASH_TABLE_SIZE(table);
1690
+ if (table->entryCount + table->removedCount >= MAX_LOAD(table, size)) {
1691
+ /* Compress if a quarter or more of all entries are removed. */
1692
+ if (table->removedCount >= size >> 2) {
1693
+ METER(table->stats.compresses++);
1696
+ METER(table->stats.grows++);
1701
+ * Grow or compress table, returning null if ChangeTable fails and
1702
+ * falling through might claim the last free entry.
1704
+ if (!ChangeTable(table, deltaLog2) &&
1705
+ table->entryCount + table->removedCount == size - 1) {
1706
+ METER(table->stats.addFailures++);
1713
+ * Look for entry after possibly growing, so we don't have to add it,
1714
+ * then skip it while growing the table and re-add it after.
1716
+ entry = SearchTable(table, key, keyHash, op);
1717
+ if (!ENTRY_IS_LIVE(entry)) {
1718
+ /* Initialize the entry, indicating that it's no longer free. */
1719
+ METER(table->stats.addMisses++);
1720
+ if (ENTRY_IS_REMOVED(entry)) {
1721
+ METER(table->stats.addOverRemoved++);
1722
+ table->removedCount--;
1723
+ keyHash |= COLLISION_FLAG;
1725
+ if (table->ops->initEntry &&
1726
+ !table->ops->initEntry(table, entry, key)) {
1727
+ /* We haven't claimed entry yet; fail with null return. */
1728
+ memset(entry + 1, 0, table->entrySize - sizeof *entry);
1732
+ entry->keyHash = keyHash;
1733
+ table->entryCount++;
1735
+ METER(else table->stats.addHits++);
1738
+ case PL_DHASH_REMOVE:
1739
+ entry = SearchTable(table, key, keyHash, op);
1740
+ if (ENTRY_IS_LIVE(entry)) {
1741
+ /* Clear this entry and mark it as "removed". */
1742
+ METER(table->stats.removeHits++);
1743
+ PL_DHashTableRawRemove(table, entry);
1745
+ /* Shrink if alpha is <= .25 and table isn't too small already. */
1746
+ size = PL_DHASH_TABLE_SIZE(table);
1747
+ if (size > PL_DHASH_MIN_SIZE &&
1748
+ table->entryCount <= MIN_LOAD(table, size)) {
1749
+ METER(table->stats.shrinks++);
1750
+ (void) ChangeTable(table, -1);
1753
+ METER(else table->stats.removeMisses++);
1758
+ NS_NOTREACHED("0");
1762
+ DECREMENT_RECURSION_LEVEL(table);
1768
+PL_DHashTableRawRemove(PLDHashTable *table, PLDHashEntryHdr *entry)
1770
+ PLDHashNumber keyHash; /* load first in case clearEntry goofs it */
1772
+ NS_ASSERTION(RECURSION_LEVEL(table) != IMMUTABLE_RECURSION_LEVEL,
1773
+ "RECURSION_LEVEL(table) != IMMUTABLE_RECURSION_LEVEL");
1775
+ NS_ASSERTION(PL_DHASH_ENTRY_IS_LIVE(entry),
1776
+ "PL_DHASH_ENTRY_IS_LIVE(entry)");
1777
+ keyHash = entry->keyHash;
1778
+ table->ops->clearEntry(table, entry);
1779
+ if (keyHash & COLLISION_FLAG) {
1780
+ MARK_ENTRY_REMOVED(entry);
1781
+ table->removedCount++;
1783
+ METER(table->stats.removeFrees++);
1784
+ MARK_ENTRY_FREE(entry);
1786
+ table->entryCount--;
1790
+PL_DHashTableEnumerate(PLDHashTable *table, PLDHashEnumerator etor, void *arg)
1792
+ char *entryAddr, *entryLimit;
1793
+ PRUint32 i, capacity, entrySize, ceiling;
1795
+ PLDHashEntryHdr *entry;
1796
+ PLDHashOperator op;
1798
+ INCREMENT_RECURSION_LEVEL(table);
1800
+ entryAddr = table->entryStore;
1801
+ entrySize = table->entrySize;
1802
+ capacity = PL_DHASH_TABLE_SIZE(table);
1803
+ entryLimit = entryAddr + capacity * entrySize;
1805
+ didRemove = PR_FALSE;
1806
+ while (entryAddr < entryLimit) {
1807
+ entry = (PLDHashEntryHdr *)entryAddr;
1808
+ if (ENTRY_IS_LIVE(entry)) {
1809
+ op = etor(table, entry, i++, arg);
1810
+ if (op & PL_DHASH_REMOVE) {
1811
+ METER(table->stats.removeEnums++);
1812
+ PL_DHashTableRawRemove(table, entry);
1813
+ didRemove = PR_TRUE;
1815
+ if (op & PL_DHASH_STOP)
1818
+ entryAddr += entrySize;
1821
+ NS_ASSERTION(!didRemove || RECURSION_LEVEL(table) == 1,
1822
+ "!didRemove || RECURSION_LEVEL(table) == 1");
1825
+ * Shrink or compress if a quarter or more of all entries are removed, or
1826
+ * if the table is underloaded according to the configured minimum alpha,
1827
+ * and is not minimal-size already. Do this only if we removed above, so
1828
+ * non-removing enumerations can count on stable table->entryStore until
1829
+ * the next non-lookup-Operate or removing-Enumerate.
1832
+ (table->removedCount >= capacity >> 2 ||
1833
+ (capacity > PL_DHASH_MIN_SIZE &&
1834
+ table->entryCount <= MIN_LOAD(table, capacity)))) {
1835
+ METER(table->stats.enumShrinks++);
1836
+ capacity = table->entryCount;
1837
+ capacity += capacity >> 1;
1838
+ if (capacity < PL_DHASH_MIN_SIZE)
1839
+ capacity = PL_DHASH_MIN_SIZE;
1841
+ PR_CEILING_LOG2(ceiling, capacity);
1842
+ ceiling -= PL_DHASH_BITS - table->hashShift;
1844
+ (void) ChangeTable(table, ceiling);
1847
+ DECREMENT_RECURSION_LEVEL(table);
1854
+PL_DHashMarkTableImmutable(PLDHashTable *table)
1856
+ RECURSION_LEVEL(table) = IMMUTABLE_RECURSION_LEVEL;
1860
+#ifdef PL_DHASHMETER
1864
+PL_DHashTableDumpMeter(PLDHashTable *table, PLDHashEnumerator dump, FILE *fp)
1867
+ PRUint32 entrySize, entryCount;
1868
+ int hashShift, sizeLog2;
1869
+ PRUint32 i, tableSize, sizeMask, chainLen, maxChainLen, chainCount;
1870
+ PLDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2;
1871
+ double sqsum, mean, variance, sigma;
1872
+ PLDHashEntryHdr *entry, *probe;
1874
+ entryAddr = table->entryStore;
1875
+ entrySize = table->entrySize;
1876
+ hashShift = table->hashShift;
1877
+ sizeLog2 = PL_DHASH_BITS - hashShift;
1878
+ tableSize = PL_DHASH_TABLE_SIZE(table);
1879
+ sizeMask = PR_BITMASK(sizeLog2);
1880
+ chainCount = maxChainLen = 0;
1884
+ for (i = 0; i < tableSize; i++) {
1885
+ entry = (PLDHashEntryHdr *)entryAddr;
1886
+ entryAddr += entrySize;
1887
+ if (!ENTRY_IS_LIVE(entry))
1889
+ hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, hashShift);
1890
+ saveHash1 = hash1;
1891
+ probe = ADDRESS_ENTRY(table, hash1);
1893
+ if (probe == entry) {
1894
+ /* Start of a (possibly unit-length) chain. */
1897
+ hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, sizeLog2,
1902
+ hash1 &= sizeMask;
1903
+ probe = ADDRESS_ENTRY(table, hash1);
1904
+ } while (probe != entry);
1906
+ sqsum += chainLen * chainLen;
1907
+ if (chainLen > maxChainLen) {
1908
+ maxChainLen = chainLen;
1909
+ maxChainHash1 = saveHash1;
1910
+ maxChainHash2 = hash2;
1914
+ entryCount = table->entryCount;
1915
+ if (entryCount && chainCount) {
1916
+ mean = (double)entryCount / chainCount;
1917
+ variance = chainCount * sqsum - entryCount * entryCount;
1918
+ if (variance < 0 || chainCount == 1)
1921
+ variance /= chainCount * (chainCount - 1);
1922
+ sigma = sqrt(variance);
1927
+ fprintf(fp, "Double hashing statistics:\n");
1928
+ fprintf(fp, " table size (in entries): %u\n", tableSize);
1929
+ fprintf(fp, " number of entries: %u\n", table->entryCount);
1930
+ fprintf(fp, " number of removed entries: %u\n", table->removedCount);
1931
+ fprintf(fp, " number of searches: %u\n", table->stats.searches);
1932
+ fprintf(fp, " number of hits: %u\n", table->stats.hits);
1933
+ fprintf(fp, " number of misses: %u\n", table->stats.misses);
1934
+ fprintf(fp, " mean steps per search: %g\n", table->stats.searches ?
1935
+ (double)table->stats.steps
1936
+ / table->stats.searches :
1938
+ fprintf(fp, " mean hash chain length: %g\n", mean);
1939
+ fprintf(fp, " standard deviation: %g\n", sigma);
1940
+ fprintf(fp, " maximum hash chain length: %u\n", maxChainLen);
1941
+ fprintf(fp, " number of lookups: %u\n", table->stats.lookups);
1942
+ fprintf(fp, " adds that made a new entry: %u\n", table->stats.addMisses);
1943
+ fprintf(fp, "adds that recycled removeds: %u\n", table->stats.addOverRemoved);
1944
+ fprintf(fp, " adds that found an entry: %u\n", table->stats.addHits);
1945
+ fprintf(fp, " add failures: %u\n", table->stats.addFailures);
1946
+ fprintf(fp, " useful removes: %u\n", table->stats.removeHits);
1947
+ fprintf(fp, " useless removes: %u\n", table->stats.removeMisses);
1948
+ fprintf(fp, "removes that freed an entry: %u\n", table->stats.removeFrees);
1949
+ fprintf(fp, " removes while enumerating: %u\n", table->stats.removeEnums);
1950
+ fprintf(fp, " number of grows: %u\n", table->stats.grows);
1951
+ fprintf(fp, " number of shrinks: %u\n", table->stats.shrinks);
1952
+ fprintf(fp, " number of compresses: %u\n", table->stats.compresses);
1953
+ fprintf(fp, "number of enumerate shrinks: %u\n", table->stats.enumShrinks);
1955
+ if (dump && maxChainLen && hash2) {
1956
+ fputs("Maximum hash chain:\n", fp);
1957
+ hash1 = maxChainHash1;
1958
+ hash2 = maxChainHash2;
1959
+ entry = ADDRESS_ENTRY(table, hash1);
1962
+ if (dump(table, entry, i++, fp) != PL_DHASH_NEXT)
1965
+ hash1 &= sizeMask;
1966
+ entry = ADDRESS_ENTRY(table, hash1);
1967
+ } while (PL_DHASH_ENTRY_IS_BUSY(entry));
1970
+#endif /* PL_DHASHMETER */