LCOV - app.info - xpcom/glue/pldhash.cpp

LCOV - code coverage report
| Current view: | directory - xpcom/glue - pldhash.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 273 | 172 | 63.0 %
| Date: | 2012-06-02 | Functions: | 26 | 12 | 46.2 %

           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                 :  *   L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
          26                 :  *
          27                 :  * Alternatively, the contents of this file may be used under the terms of
          28                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
          29                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          31                 :  * of those above. If you wish to allow use of your version of this file only
          32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          33                 :  * use your version of this file under the terms of the MPL, indicate your
          34                 :  * decision by deleting the provisions above and replace them with the notice
          35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          36                 :  * the provisions above, a recipient may use your version of this file under
          37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          38                 :  *
          39                 :  * ***** END LICENSE BLOCK ***** */
          40                 :
          41                 : /*
          42                 :  * Double hashing implementation.
          43                 :  *
          44                 :  * Try to keep this file in sync with js/src/jsdhash.cpp.
          45                 :  */
          46                 : #include <stdio.h>
          47                 : #include <stdlib.h>
          48                 : #include <string.h>
          49                 : #include "prbit.h"
          50                 : #include "pldhash.h"
          51                 : #include "mozilla/HashFunctions.h"
          52                 : #include "nsDebug.h"     /* for PR_ASSERT */
          53                 :
          54                 : #ifdef PL_DHASHMETER
          55                 : # if defined MOZILLA_CLIENT && defined DEBUG_XXXbrendan
          56                 : #  include "nsTraceMalloc.h"
          57                 : # endif
          58                 : # define METER(x)       x
          59                 : #else
          60                 : # define METER(x)       /* nothing */
          61                 : #endif
          62                 :
          63                 : /*
          64                 :  * The following DEBUG-only code is used to assert that calls to one of
          65                 :  * table->ops or to an enumerator do not cause re-entry into a call that
          66                 :  * can mutate the table.  The recursion level is stored in additional
          67                 :  * space allocated at the end of the entry store to avoid changing
          68                 :  * PLDHashTable, which could cause issues when mixing DEBUG and
          69                 :  * non-DEBUG components.
          70                 :  */
          71                 : #ifdef DEBUG
          72                 :
          73                 : #define RECURSION_LEVEL(table_) (*(PRUint32*)(table_->entryStore + \
          74                 :                                             PL_DHASH_TABLE_SIZE(table_) * \
          75                 :                                             table_->entrySize))
          76                 : /*
          77                 :  * Most callers that assert about the recursion level don't care about
          78                 :  * this magical value because they are asserting that mutation is
          79                 :  * allowed (and therefore the level is 0 or 1, depending on whether they
          80                 :  * incremented it).
          81                 :  *
          82                 :  * Only PL_DHashTableFinish needs to allow this special value.
          83                 :  */
          84                 : #define IMMUTABLE_RECURSION_LEVEL ((PRUint32)-1)
          85                 :
          86                 : #define RECURSION_LEVEL_SAFE_TO_FINISH(table_)                                \
          87                 :     (RECURSION_LEVEL(table_) == 0 ||                                          \
          88                 :      RECURSION_LEVEL(table_) == IMMUTABLE_RECURSION_LEVEL)
          89                 :
          90                 : #define ENTRY_STORE_EXTRA                   sizeof(PRUint32)
          91                 : #define INCREMENT_RECURSION_LEVEL(table_)                                     \
          92                 :     PR_BEGIN_MACRO                                                            \
          93                 :         if (RECURSION_LEVEL(table_) != IMMUTABLE_RECURSION_LEVEL)             \
          94                 :             ++RECURSION_LEVEL(table_);                                        \
          95                 :     PR_END_MACRO
          96                 : #define DECREMENT_RECURSION_LEVEL(table_)                                     \
          97                 :     PR_BEGIN_MACRO                                                            \
          98                 :         if (RECURSION_LEVEL(table_) != IMMUTABLE_RECURSION_LEVEL) {           \
          99                 :             NS_ASSERTION(RECURSION_LEVEL(table_) > 0, "RECURSION_LEVEL(table_) > 0");              \
         100                 :             --RECURSION_LEVEL(table_);                                        \
         101                 :         }                                                                     \
         102                 :     PR_END_MACRO
         103                 :
         104                 : #else
         105                 :
         106                 : #define ENTRY_STORE_EXTRA 0
         107                 : #define INCREMENT_RECURSION_LEVEL(table_)   PR_BEGIN_MACRO PR_END_MACRO
         108                 : #define DECREMENT_RECURSION_LEVEL(table_)   PR_BEGIN_MACRO PR_END_MACRO
         109                 :
         110                 : #endif /* defined(DEBUG) */
         111                 :
         112                 : using namespace mozilla;
         113                 :
         114                 : void *
         115              32 : PL_DHashAllocTable(PLDHashTable *table, PRUint32 nbytes)
         116                 : {
         117              32 :     return malloc(nbytes);
         118                 : }
         119                 :
         120                 : void
         121              32 : PL_DHashFreeTable(PLDHashTable *table, void *ptr)
         122                 : {
         123              32 :     free(ptr);
         124              32 : }
         125                 :
         126                 : PLDHashNumber
         127               0 : PL_DHashStringKey(PLDHashTable *table, const void *key)
         128                 : {
         129               0 :     return HashString(static_cast<const char*>(key));
         130                 : }
         131                 :
         132                 : PLDHashNumber
         133               0 : PL_DHashVoidPtrKeyStub(PLDHashTable *table, const void *key)
         134                 : {
         135               0 :     return (PLDHashNumber)(PRPtrdiff)key >> 2;
         136                 : }
         137                 :
         138                 : bool
         139               0 : PL_DHashMatchEntryStub(PLDHashTable *table,
         140                 :                        const PLDHashEntryHdr *entry,
         141                 :                        const void *key)
         142                 : {
         143               0 :     const PLDHashEntryStub *stub = (const PLDHashEntryStub *)entry;
         144                 :
         145               0 :     return stub->key == key;
         146                 : }
         147                 :
         148                 : bool
         149               0 : PL_DHashMatchStringKey(PLDHashTable *table,
         150                 :                        const PLDHashEntryHdr *entry,
         151                 :                        const void *key)
         152                 : {
         153               0 :     const PLDHashEntryStub *stub = (const PLDHashEntryStub *)entry;
         154                 :
         155                 :     /* XXX tolerate null keys on account of sloppy Mozilla callers. */
         156                 :     return stub->key == key ||
         157                 :            (stub->key && key &&
         158               0 :             strcmp((const char *) stub->key, (const char *) key) == 0);
         159                 : }
         160                 :
         161                 : void
         162              96 : PL_DHashMoveEntryStub(PLDHashTable *table,
         163                 :                       const PLDHashEntryHdr *from,
         164                 :                       PLDHashEntryHdr *to)
         165                 : {
         166              96 :     memcpy(to, from, table->entrySize);
         167              96 : }
         168                 :
         169                 : void
         170               0 : PL_DHashClearEntryStub(PLDHashTable *table, PLDHashEntryHdr *entry)
         171                 : {
         172               0 :     memset(entry, 0, table->entrySize);
         173               0 : }
         174                 :
         175                 : void
         176               0 : PL_DHashFreeStringKey(PLDHashTable *table, PLDHashEntryHdr *entry)
         177                 : {
         178               0 :     const PLDHashEntryStub *stub = (const PLDHashEntryStub *)entry;
         179                 :
         180               0 :     free((void *) stub->key);
         181               0 :     memset(entry, 0, table->entrySize);
         182               0 : }
         183                 :
         184                 : void
         185              16 : PL_DHashFinalizeStub(PLDHashTable *table)
         186                 : {
         187              16 : }
         188                 :
         189                 : static const PLDHashTableOps stub_ops = {
         190                 :     PL_DHashAllocTable,
         191                 :     PL_DHashFreeTable,
         192                 :     PL_DHashVoidPtrKeyStub,
         193                 :     PL_DHashMatchEntryStub,
         194                 :     PL_DHashMoveEntryStub,
         195                 :     PL_DHashClearEntryStub,
         196                 :     PL_DHashFinalizeStub,
         197                 :     NULL
         198                 : };
         199                 :
         200                 : const PLDHashTableOps *
         201               0 : PL_DHashGetStubOps(void)
         202                 : {
         203               0 :     return &stub_ops;
         204                 : }
         205                 :
         206                 : PLDHashTable *
         207               0 : PL_NewDHashTable(const PLDHashTableOps *ops, void *data, PRUint32 entrySize,
         208                 :                  PRUint32 capacity)
         209                 : {
         210                 :     PLDHashTable *table;
         211                 :
         212               0 :     table = (PLDHashTable *) malloc(sizeof *table);
         213               0 :     if (!table)
         214               0 :         return NULL;
         215               0 :     if (!PL_DHashTableInit(table, ops, data, entrySize, capacity)) {
         216               0 :         free(table);
         217               0 :         return NULL;
         218                 :     }
         219               0 :     return table;
         220                 : }
         221                 :
         222                 : void
         223               0 : PL_DHashTableDestroy(PLDHashTable *table)
         224                 : {
         225               0 :     PL_DHashTableFinish(table);
         226               0 :     free(table);
         227               0 : }
         228                 :
         229                 : bool
         230              16 : PL_DHashTableInit(PLDHashTable *table, const PLDHashTableOps *ops, void *data,
         231                 :                   PRUint32 entrySize, PRUint32 capacity)
         232                 : {
         233                 :     int log2;
         234                 :     PRUint32 nbytes;
         235                 :
         236                 : #ifdef DEBUG
         237              16 :     if (entrySize > 10 * sizeof(void *)) {
         238                 :         printf_stderr(
         239                 :                 "pldhash: for the table at address %p, the given entrySize"
         240                 :                 " of %lu %s favors chaining over double hashing.\n",
         241                 :                 (void *) table,
         242                 :                 (unsigned long) entrySize,
         243               0 :                 (entrySize > 16 * sizeof(void*)) ? "definitely" : "probably");
         244                 :     }
         245                 : #endif
         246                 :
         247              16 :     table->ops = ops;
         248              16 :     table->data = data;
         249              16 :     if (capacity < PL_DHASH_MIN_SIZE)
         250               0 :         capacity = PL_DHASH_MIN_SIZE;
         251                 :
         252              16 :     PR_CEILING_LOG2(log2, capacity);
         253                 :
         254              16 :     capacity = PR_BIT(log2);
         255              16 :     if (capacity >= PL_DHASH_SIZE_LIMIT)
         256               0 :         return false;
         257              16 :     table->hashShift = PL_DHASH_BITS - log2;
         258              16 :     table->maxAlphaFrac = (PRUint8)(0x100 * PL_DHASH_DEFAULT_MAX_ALPHA);
         259              16 :     table->minAlphaFrac = (PRUint8)(0x100 * PL_DHASH_DEFAULT_MIN_ALPHA);
         260              16 :     table->entrySize = entrySize;
         261              16 :     table->entryCount = table->removedCount = 0;
         262              16 :     table->generation = 0;
         263              16 :     nbytes = capacity * entrySize;
         264                 :
         265                 :     table->entryStore = (char *) ops->allocTable(table,
         266              16 :                                                  nbytes + ENTRY_STORE_EXTRA);
         267              16 :     if (!table->entryStore)
         268               0 :         return false;
         269              16 :     memset(table->entryStore, 0, nbytes);
         270                 :     METER(memset(&table->stats, 0, sizeof table->stats));
         271                 :
         272                 : #ifdef DEBUG
         273              16 :     RECURSION_LEVEL(table) = 0;
         274                 : #endif
         275                 :
         276              16 :     return true;
         277                 : }
         278                 :
         279                 : /*
         280                 :  * Compute max and min load numbers (entry counts) from table params.
         281                 :  */
         282                 : #define MAX_LOAD(table, size)   (((table)->maxAlphaFrac * (size)) >> 8)
         283                 : #define MIN_LOAD(table, size)   (((table)->minAlphaFrac * (size)) >> 8)
         284                 :
         285                 : void
         286               0 : PL_DHashTableSetAlphaBounds(PLDHashTable *table,
         287                 :                             float maxAlpha,
         288                 :                             float minAlpha)
         289                 : {
         290                 :     PRUint32 size;
         291                 :
         292                 :     /*
         293                 :      * Reject obviously insane bounds, rather than trying to guess what the
         294                 :      * buggy caller intended.
         295                 :      */
         296               0 :     NS_ASSERTION(0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha,
         297                 :                  "0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha");
         298               0 :     if (maxAlpha < 0.5 || 1 <= maxAlpha || minAlpha < 0)
         299               0 :         return;
         300                 :
         301                 :     /*
         302                 :      * Ensure that at least one entry will always be free.  If maxAlpha at
         303                 :      * minimum size leaves no entries free, reduce maxAlpha based on minimum
         304                 :      * size and the precision limit of maxAlphaFrac's fixed point format.
         305                 :      */
         306               0 :     NS_ASSERTION(PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) >= 1,
         307                 :                  "PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) >= 1");
         308               0 :     if (PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) < 1) {
         309                 :         maxAlpha = (float)
         310                 :                    (PL_DHASH_MIN_SIZE - PR_MAX(PL_DHASH_MIN_SIZE / 256, 1))
         311               0 :                    / PL_DHASH_MIN_SIZE;
         312                 :     }
         313                 :
         314                 :     /*
         315                 :      * Ensure that minAlpha is strictly less than half maxAlpha.  Take care
         316                 :      * not to truncate an entry's worth of alpha when storing in minAlphaFrac
         317                 :      * (8-bit fixed point format).
         318                 :      */
         319               0 :     NS_ASSERTION(minAlpha < maxAlpha / 2,
         320                 :                  "minAlpha < maxAlpha / 2");
         321               0 :     if (minAlpha >= maxAlpha / 2) {
         322               0 :         size = PL_DHASH_TABLE_SIZE(table);
         323               0 :         minAlpha = (size * maxAlpha - PR_MAX(size / 256, 1)) / (2 * size);
         324                 :     }
         325                 :
         326               0 :     table->maxAlphaFrac = (PRUint8)(maxAlpha * 256);
         327               0 :     table->minAlphaFrac = (PRUint8)(minAlpha * 256);
         328                 : }
         329                 :
         330                 : /*
         331                 :  * Double hashing needs the second hash code to be relatively prime to table
         332                 :  * size, so we simply make hash2 odd.
         333                 :  */
         334                 : #define HASH1(hash0, shift)         ((hash0) >> (shift))
         335                 : #define HASH2(hash0,log2,shift)     ((((hash0) << (log2)) >> (shift)) | 1)
         336                 :
         337                 : /*
         338                 :  * Reserve keyHash 0 for free entries and 1 for removed-entry sentinels.  Note
         339                 :  * that a removed-entry sentinel need be stored only if the removed entry had
         340                 :  * a colliding entry added after it.  Therefore we can use 1 as the collision
         341                 :  * flag in addition to the removed-entry sentinel value.  Multiplicative hash
         342                 :  * uses the high order bits of keyHash, so this least-significant reservation
         343                 :  * should not hurt the hash function's effectiveness much.
         344                 :  *
         345                 :  * If you change any of these magic numbers, also update PL_DHASH_ENTRY_IS_LIVE
         346                 :  * in pldhash.h.  It used to be private to pldhash.c, but then became public to
         347                 :  * assist iterator writers who inspect table->entryStore directly.
         348                 :  */
         349                 : #define COLLISION_FLAG              ((PLDHashNumber) 1)
         350                 : #define MARK_ENTRY_FREE(entry)      ((entry)->keyHash = 0)
         351                 : #define MARK_ENTRY_REMOVED(entry)   ((entry)->keyHash = 1)
         352                 : #define ENTRY_IS_REMOVED(entry)     ((entry)->keyHash == 1)
         353                 : #define ENTRY_IS_LIVE(entry)        PL_DHASH_ENTRY_IS_LIVE(entry)
         354                 : #define ENSURE_LIVE_KEYHASH(hash0)  if (hash0 < 2) hash0 -= 2; else (void)0
         355                 :
         356                 : /* Match an entry's keyHash against an unstored one computed from a key. */
         357                 : #define MATCH_ENTRY_KEYHASH(entry,hash0) \
         358                 :     (((entry)->keyHash & ~COLLISION_FLAG) == (hash0))
         359                 :
         360                 : /* Compute the address of the indexed entry in table. */
         361                 : #define ADDRESS_ENTRY(table, index) \
         362                 :     ((PLDHashEntryHdr *)((table)->entryStore + (index) * (table)->entrySize))
         363                 :
         364                 : void
         365              16 : PL_DHashTableFinish(PLDHashTable *table)
         366                 : {
         367                 :     char *entryAddr, *entryLimit;
         368                 :     PRUint32 entrySize;
         369                 :     PLDHashEntryHdr *entry;
         370                 :
         371                 : #ifdef DEBUG_XXXbrendan
         372                 :     static FILE *dumpfp = NULL;
         373                 :     if (!dumpfp) dumpfp = fopen("/tmp/pldhash.bigdump", "w");
         374                 :     if (dumpfp) {
         375                 : #ifdef MOZILLA_CLIENT
         376                 :         NS_TraceStack(1, dumpfp);
         377                 : #endif
         378                 :         PL_DHashTableDumpMeter(table, NULL, dumpfp);
         379                 :         fputc('\n', dumpfp);
         380                 :     }
         381                 : #endif
         382                 :
         383              16 :     INCREMENT_RECURSION_LEVEL(table);
         384                 :
         385                 :     /* Call finalize before clearing entries, so it can enumerate them. */
         386              16 :     table->ops->finalize(table);
         387                 :
         388                 :     /* Clear any remaining live entries. */
         389              16 :     entryAddr = table->entryStore;
         390              16 :     entrySize = table->entrySize;
         391              16 :     entryLimit = entryAddr + PL_DHASH_TABLE_SIZE(table) * entrySize;
         392             288 :     while (entryAddr < entryLimit) {
         393             256 :         entry = (PLDHashEntryHdr *)entryAddr;
         394             256 :         if (ENTRY_IS_LIVE(entry)) {
         395                 :             METER(table->stats.removeEnums++);
         396               4 :             table->ops->clearEntry(table, entry);
         397                 :         }
         398             256 :         entryAddr += entrySize;
         399                 :     }
         400                 :
         401              16 :     DECREMENT_RECURSION_LEVEL(table);
         402              16 :     NS_ASSERTION(RECURSION_LEVEL_SAFE_TO_FINISH(table),
         403                 :                  "RECURSION_LEVEL_SAFE_TO_FINISH(table)");
         404                 :
         405                 :     /* Free entry storage last. */
         406              16 :     table->ops->freeTable(table, table->entryStore);
         407              16 : }
         408                 :
         409                 : static PLDHashEntryHdr * PL_DHASH_FASTCALL
         410             295 : SearchTable(PLDHashTable *table, const void *key, PLDHashNumber keyHash,
         411                 :             PLDHashOperator op)
         412                 : {
         413                 :     PLDHashNumber hash1, hash2;
         414                 :     int hashShift, sizeLog2;
         415                 :     PLDHashEntryHdr *entry, *firstRemoved;
         416                 :     PLDHashMatchEntry matchEntry;
         417                 :     PRUint32 sizeMask;
         418                 :
         419                 :     METER(table->stats.searches++);
         420             295 :     NS_ASSERTION(!(keyHash & COLLISION_FLAG),
         421                 :                  "!(keyHash & COLLISION_FLAG)");
         422                 :
         423                 :     /* Compute the primary hash address. */
         424             295 :     hashShift = table->hashShift;
         425             295 :     hash1 = HASH1(keyHash, hashShift);
         426             295 :     entry = ADDRESS_ENTRY(table, hash1);
         427                 :
         428                 :     /* Miss: return space for a new entry. */
         429             295 :     if (PL_DHASH_ENTRY_IS_FREE(entry)) {
         430                 :         METER(table->stats.misses++);
         431             141 :         return entry;
         432                 :     }
         433                 :
         434                 :     /* Hit: return entry. */
         435             154 :     matchEntry = table->ops->matchEntry;
         436             154 :     if (MATCH_ENTRY_KEYHASH(entry, keyHash) && matchEntry(table, entry, key)) {
         437                 :         METER(table->stats.hits++);
         438             134 :         return entry;
         439                 :     }
         440                 :
         441                 :     /* Collision: double hash. */
         442              20 :     sizeLog2 = PL_DHASH_BITS - table->hashShift;
         443              20 :     hash2 = HASH2(keyHash, sizeLog2, hashShift);
         444              20 :     sizeMask = PR_BITMASK(sizeLog2);
         445                 :
         446                 :     /* Save the first removed entry pointer so PL_DHASH_ADD can recycle it. */
         447              20 :     firstRemoved = NULL;
         448                 :
         449              17 :     for (;;) {
         450              37 :         if (NS_UNLIKELY(ENTRY_IS_REMOVED(entry))) {
         451               0 :             if (!firstRemoved)
         452               0 :                 firstRemoved = entry;
         453                 :         } else {
         454              37 :             if (op == PL_DHASH_ADD)
         455              28 :                 entry->keyHash |= COLLISION_FLAG;
         456                 :         }
         457                 :
         458                 :         METER(table->stats.steps++);
         459              37 :         hash1 -= hash2;
         460              37 :         hash1 &= sizeMask;
         461                 :
         462              37 :         entry = ADDRESS_ENTRY(table, hash1);
         463              37 :         if (PL_DHASH_ENTRY_IS_FREE(entry)) {
         464                 :             METER(table->stats.misses++);
         465              17 :             return (firstRemoved && op == PL_DHASH_ADD) ? firstRemoved : entry;
         466                 :         }
         467                 :
         468              23 :         if (MATCH_ENTRY_KEYHASH(entry, keyHash) &&
         469               3 :             matchEntry(table, entry, key)) {
         470                 :             METER(table->stats.hits++);
         471               3 :             return entry;
         472                 :         }
         473                 :     }
         474                 :
         475                 :     /* NOTREACHED */
         476                 :     return NULL;
         477                 : }
         478                 :
         479                 : /*
         480                 :  * This is a copy of SearchTable, used by ChangeTable, hardcoded to
         481                 :  *   1. assume |op == PL_DHASH_ADD|,
         482                 :  *   2. assume that |key| will never match an existing entry, and
         483                 :  *   3. assume that no entries have been removed from the current table
         484                 :  *      structure.
         485                 :  * Avoiding the need for |key| means we can avoid needing a way to map
         486                 :  * entries to keys, which means callers can use complex key types more
         487                 :  * easily.
         488                 :  */
         489                 : static PLDHashEntryHdr * PL_DHASH_FASTCALL
         490              96 : FindFreeEntry(PLDHashTable *table, PLDHashNumber keyHash)
         491                 : {
         492                 :     PLDHashNumber hash1, hash2;
         493                 :     int hashShift, sizeLog2;
         494                 :     PLDHashEntryHdr *entry;
         495                 :     PRUint32 sizeMask;
         496                 :
         497                 :     METER(table->stats.searches++);
         498              96 :     NS_ASSERTION(!(keyHash & COLLISION_FLAG),
         499                 :                  "!(keyHash & COLLISION_FLAG)");
         500                 :
         501                 :     /* Compute the primary hash address. */
         502              96 :     hashShift = table->hashShift;
         503              96 :     hash1 = HASH1(keyHash, hashShift);
         504              96 :     entry = ADDRESS_ENTRY(table, hash1);
         505                 :
         506                 :     /* Miss: return space for a new entry. */
         507              96 :     if (PL_DHASH_ENTRY_IS_FREE(entry)) {
         508                 :         METER(table->stats.misses++);
         509              96 :         return entry;
         510                 :     }
         511                 :
         512                 :     /* Collision: double hash. */
         513               0 :     sizeLog2 = PL_DHASH_BITS - table->hashShift;
         514               0 :     hash2 = HASH2(keyHash, sizeLog2, hashShift);
         515               0 :     sizeMask = PR_BITMASK(sizeLog2);
         516                 :
         517               0 :     for (;;) {
         518               0 :         NS_ASSERTION(!ENTRY_IS_REMOVED(entry),
         519                 :                      "!ENTRY_IS_REMOVED(entry)");
         520               0 :         entry->keyHash |= COLLISION_FLAG;
         521                 :
         522                 :         METER(table->stats.steps++);
         523               0 :         hash1 -= hash2;
         524               0 :         hash1 &= sizeMask;
         525                 :
         526               0 :         entry = ADDRESS_ENTRY(table, hash1);
         527               0 :         if (PL_DHASH_ENTRY_IS_FREE(entry)) {
         528                 :             METER(table->stats.misses++);
         529               0 :             return entry;
         530                 :         }
         531                 :     }
         532                 :
         533                 :     /* NOTREACHED */
         534                 :     return NULL;
         535                 : }
         536                 :
         537                 : static bool
         538              16 : ChangeTable(PLDHashTable *table, int deltaLog2)
         539                 : {
         540                 :     int oldLog2, newLog2;
         541                 :     PRUint32 oldCapacity, newCapacity;
         542                 :     char *newEntryStore, *oldEntryStore, *oldEntryAddr;
         543                 :     PRUint32 entrySize, i, nbytes;
         544                 :     PLDHashEntryHdr *oldEntry, *newEntry;
         545                 :     PLDHashMoveEntry moveEntry;
         546                 : #ifdef DEBUG
         547                 :     PRUint32 recursionLevel;
         548                 : #endif
         549                 :
         550                 :     /* Look, but don't touch, until we succeed in getting new entry store. */
         551              16 :     oldLog2 = PL_DHASH_BITS - table->hashShift;
         552              16 :     newLog2 = oldLog2 + deltaLog2;
         553              16 :     oldCapacity = PR_BIT(oldLog2);
         554              16 :     newCapacity = PR_BIT(newLog2);
         555              16 :     if (newCapacity >= PL_DHASH_SIZE_LIMIT)
         556               0 :         return false;
         557              16 :     entrySize = table->entrySize;
         558              16 :     nbytes = newCapacity * entrySize;
         559                 :
         560                 :     newEntryStore = (char *) table->ops->allocTable(table,
         561              16 :                                                     nbytes + ENTRY_STORE_EXTRA);
         562              16 :     if (!newEntryStore)
         563               0 :         return false;
         564                 :
         565                 :     /* We can't fail from here on, so update table parameters. */
         566                 : #ifdef DEBUG
         567              16 :     recursionLevel = RECURSION_LEVEL(table);
         568                 : #endif
         569              16 :     table->hashShift = PL_DHASH_BITS - newLog2;
         570              16 :     table->removedCount = 0;
         571              16 :     table->generation++;
         572                 :
         573                 :     /* Assign the new entry store to table. */
         574              16 :     memset(newEntryStore, 0, nbytes);
         575              16 :     oldEntryAddr = oldEntryStore = table->entryStore;
         576              16 :     table->entryStore = newEntryStore;
         577              16 :     moveEntry = table->ops->moveEntry;
         578                 : #ifdef DEBUG
         579              16 :     RECURSION_LEVEL(table) = recursionLevel;
         580                 : #endif
         581                 :
         582                 :     /* Copy only live entries, leaving removed ones behind. */
         583             400 :     for (i = 0; i < oldCapacity; i++) {
         584             384 :         oldEntry = (PLDHashEntryHdr *)oldEntryAddr;
         585             384 :         if (ENTRY_IS_LIVE(oldEntry)) {
         586              96 :             oldEntry->keyHash &= ~COLLISION_FLAG;
         587              96 :             newEntry = FindFreeEntry(table, oldEntry->keyHash);
         588              96 :             NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(newEntry),
         589                 :                          "PL_DHASH_ENTRY_IS_FREE(newEntry)");
         590              96 :             moveEntry(table, oldEntry, newEntry);
         591              96 :             newEntry->keyHash = oldEntry->keyHash;
         592                 :         }
         593             384 :         oldEntryAddr += entrySize;
         594                 :     }
         595                 :
         596              16 :     table->ops->freeTable(table, oldEntryStore);
         597              16 :     return true;
         598                 : }
         599                 :
         600                 : PLDHashEntryHdr * PL_DHASH_FASTCALL
         601             295 : PL_DHashTableOperate(PLDHashTable *table, const void *key, PLDHashOperator op)
         602                 : {
         603                 :     PLDHashNumber keyHash;
         604                 :     PLDHashEntryHdr *entry;
         605                 :     PRUint32 size;
         606                 :     int deltaLog2;
         607                 :
         608             295 :     NS_ASSERTION(op == PL_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0,
         609                 :                  "op == PL_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0");
         610             295 :     INCREMENT_RECURSION_LEVEL(table);
         611                 :
         612             295 :     keyHash = table->ops->hashKey(table, key);
         613             295 :     keyHash *= PL_DHASH_GOLDEN_RATIO;
         614                 :
         615                 :     /* Avoid 0 and 1 hash codes, they indicate free and removed entries. */
         616             295 :     ENSURE_LIVE_KEYHASH(keyHash);
         617             295 :     keyHash &= ~COLLISION_FLAG;
         618                 :
         619             295 :     switch (op) {
         620                 :       case PL_DHASH_LOOKUP:
         621                 :         METER(table->stats.lookups++);
         622             154 :         entry = SearchTable(table, key, keyHash, op);
         623             154 :         break;
         624                 :
         625                 :       case PL_DHASH_ADD:
         626                 :         /*
         627                 :          * If alpha is >= .75, grow or compress the table.  If key is already
         628                 :          * in the table, we may grow once more than necessary, but only if we
         629                 :          * are on the edge of being overloaded.
         630                 :          */
         631             141 :         size = PL_DHASH_TABLE_SIZE(table);
         632             141 :         if (table->entryCount + table->removedCount >= MAX_LOAD(table, size)) {
         633                 :             /* Compress if a quarter or more of all entries are removed. */
         634               8 :             if (table->removedCount >= size >> 2) {
         635                 :                 METER(table->stats.compresses++);
         636               0 :                 deltaLog2 = 0;
         637                 :             } else {
         638                 :                 METER(table->stats.grows++);
         639               8 :                 deltaLog2 = 1;
         640                 :             }
         641                 :
         642                 :             /*
         643                 :              * Grow or compress table, returning null if ChangeTable fails and
         644                 :              * falling through might claim the last free entry.
         645                 :              */
         646               8 :             if (!ChangeTable(table, deltaLog2) &&
         647                 :                 table->entryCount + table->removedCount == size - 1) {
         648                 :                 METER(table->stats.addFailures++);
         649               0 :                 entry = NULL;
         650               0 :                 break;
         651                 :             }
         652                 :         }
         653                 :
         654                 :         /*
         655                 :          * Look for entry after possibly growing, so we don't have to add it,
         656                 :          * then skip it while growing the table and re-add it after.
         657                 :          */
         658             141 :         entry = SearchTable(table, key, keyHash, op);
         659             141 :         if (!ENTRY_IS_LIVE(entry)) {
         660                 :             /* Initialize the entry, indicating that it's no longer free. */
         661                 :             METER(table->stats.addMisses++);
         662             141 :             if (ENTRY_IS_REMOVED(entry)) {
         663                 :                 METER(table->stats.addOverRemoved++);
         664               0 :                 table->removedCount--;
         665               0 :                 keyHash |= COLLISION_FLAG;
         666                 :             }
         667             282 :             if (table->ops->initEntry &&
         668             141 :                 !table->ops->initEntry(table, entry, key)) {
         669                 :                 /* We haven't claimed entry yet; fail with null return. */
         670               0 :                 memset(entry + 1, 0, table->entrySize - sizeof *entry);
         671               0 :                 entry = NULL;
         672               0 :                 break;
         673                 :             }
         674             141 :             entry->keyHash = keyHash;
         675             141 :             table->entryCount++;
         676                 :         }
         677                 :         METER(else table->stats.addHits++);
         678             141 :         break;
         679                 :
         680                 :       case PL_DHASH_REMOVE:
         681               0 :         entry = SearchTable(table, key, keyHash, op);
         682               0 :         if (ENTRY_IS_LIVE(entry)) {
         683                 :             /* Clear this entry and mark it as "removed". */
         684                 :             METER(table->stats.removeHits++);
         685               0 :             PL_DHashTableRawRemove(table, entry);
         686                 :
         687                 :             /* Shrink if alpha is <= .25 and table isn't too small already. */
         688               0 :             size = PL_DHASH_TABLE_SIZE(table);
         689               0 :             if (size > PL_DHASH_MIN_SIZE &&
         690                 :                 table->entryCount <= MIN_LOAD(table, size)) {
         691                 :                 METER(table->stats.shrinks++);
         692               0 :                 (void) ChangeTable(table, -1);
         693                 :             }
         694                 :         }
         695                 :         METER(else table->stats.removeMisses++);
         696               0 :         entry = NULL;
         697               0 :         break;
         698                 :
         699                 :       default:
         700               0 :         NS_NOTREACHED("0");
         701               0 :         entry = NULL;
         702                 :     }
         703                 :
         704             295 :     DECREMENT_RECURSION_LEVEL(table);
         705                 :
         706             295 :     return entry;
         707                 : }
         708                 :
         709                 : void
         710             137 : PL_DHashTableRawRemove(PLDHashTable *table, PLDHashEntryHdr *entry)
         711                 : {
         712                 :     PLDHashNumber keyHash;      /* load first in case clearEntry goofs it */
         713                 :
         714             137 :     NS_ASSERTION(RECURSION_LEVEL(table) != IMMUTABLE_RECURSION_LEVEL,
         715                 :                  "RECURSION_LEVEL(table) != IMMUTABLE_RECURSION_LEVEL");
         716                 :
         717             137 :     NS_ASSERTION(PL_DHASH_ENTRY_IS_LIVE(entry),
         718                 :                  "PL_DHASH_ENTRY_IS_LIVE(entry)");
         719             137 :     keyHash = entry->keyHash;
         720             137 :     table->ops->clearEntry(table, entry);
         721             137 :     if (keyHash & COLLISION_FLAG) {
         722               6 :         MARK_ENTRY_REMOVED(entry);
         723               6 :         table->removedCount++;
         724                 :     } else {
         725                 :         METER(table->stats.removeFrees++);
         726             131 :         MARK_ENTRY_FREE(entry);
         727                 :     }
         728             137 :     table->entryCount--;
         729             137 : }
         730                 :
         731                 : PRUint32
         732              35 : PL_DHashTableEnumerate(PLDHashTable *table, PLDHashEnumerator etor, void *arg)
         733                 : {
         734                 :     char *entryAddr, *entryLimit;
         735                 :     PRUint32 i, capacity, entrySize, ceiling;
         736                 :     bool didRemove;
         737                 :     PLDHashEntryHdr *entry;
         738                 :     PLDHashOperator op;
         739                 :
         740              35 :     INCREMENT_RECURSION_LEVEL(table);
         741                 :
         742              35 :     entryAddr = table->entryStore;
         743              35 :     entrySize = table->entrySize;
         744              35 :     capacity = PL_DHASH_TABLE_SIZE(table);
         745              35 :     entryLimit = entryAddr + capacity * entrySize;
         746              35 :     i = 0;
         747              35 :     didRemove = false;
         748             886 :     while (entryAddr < entryLimit) {
         749             816 :         entry = (PLDHashEntryHdr *)entryAddr;
         750             816 :         if (ENTRY_IS_LIVE(entry)) {
         751             274 :             op = etor(table, entry, i++, arg);
         752             274 :             if (op & PL_DHASH_REMOVE) {
         753                 :                 METER(table->stats.removeEnums++);
         754             137 :                 PL_DHashTableRawRemove(table, entry);
         755             137 :                 didRemove = true;
         756                 :             }
         757             274 :             if (op & PL_DHASH_STOP)
         758               0 :                 break;
         759                 :         }
         760             816 :         entryAddr += entrySize;
         761                 :     }
         762                 :
         763              35 :     NS_ASSERTION(!didRemove || RECURSION_LEVEL(table) == 1,
         764                 :                  "!didRemove || RECURSION_LEVEL(table) == 1");
         765                 :
         766                 :     /*
         767                 :      * Shrink or compress if a quarter or more of all entries are removed, or
         768                 :      * if the table is underloaded according to the configured minimum alpha,
         769                 :      * and is not minimal-size already.  Do this only if we removed above, so
         770                 :      * non-removing enumerations can count on stable table->entryStore until
         771                 :      * the next non-lookup-Operate or removing-Enumerate.
         772                 :      */
         773              35 :     if (didRemove &&
         774                 :         (table->removedCount >= capacity >> 2 ||
         775                 :          (capacity > PL_DHASH_MIN_SIZE &&
         776                 :           table->entryCount <= MIN_LOAD(table, capacity)))) {
         777                 :         METER(table->stats.enumShrinks++);
         778               8 :         capacity = table->entryCount;
         779               8 :         capacity += capacity >> 1;
         780               8 :         if (capacity < PL_DHASH_MIN_SIZE)
         781               8 :             capacity = PL_DHASH_MIN_SIZE;
         782                 :
         783               8 :         PR_CEILING_LOG2(ceiling, capacity);
         784               8 :         ceiling -= PL_DHASH_BITS - table->hashShift;
         785                 :
         786               8 :         (void) ChangeTable(table, ceiling);
         787                 :     }
         788                 :
         789              35 :     DECREMENT_RECURSION_LEVEL(table);
         790                 :
         791              35 :     return i;
         792                 : }
         793                 :
         794                 : struct SizeOfEntryExcludingThisArg
         795                 : {
         796                 :     size_t total;
         797                 :     PLDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis;
         798                 :     nsMallocSizeOfFun mallocSizeOf;
         799                 :     void *arg;      // the arg passed by the user
         800                 : };
         801                 :
         802                 : static PLDHashOperator
         803               0 : SizeOfEntryExcludingThisEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
         804                 :                                    PRUint32 number, void *arg)
         805                 : {
         806               0 :     SizeOfEntryExcludingThisArg *e = (SizeOfEntryExcludingThisArg *)arg;
         807               0 :     e->total += e->sizeOfEntryExcludingThis(hdr, e->mallocSizeOf, e->arg);
         808               0 :     return PL_DHASH_NEXT;
         809                 : }
         810                 :
         811                 : size_t
         812               0 : PL_DHashTableSizeOfExcludingThis(const PLDHashTable *table,
         813                 :                                  PLDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis,
         814                 :                                  nsMallocSizeOfFun mallocSizeOf,
         815                 :                                  void *arg /* = NULL */)
         816                 : {
         817               0 :     size_t n = 0;
         818               0 :     n += mallocSizeOf(table->entryStore);
         819               0 :     if (sizeOfEntryExcludingThis) {
         820               0 :         SizeOfEntryExcludingThisArg arg2 = { 0, sizeOfEntryExcludingThis, mallocSizeOf, arg };
         821                 :         PL_DHashTableEnumerate(const_cast<PLDHashTable *>(table),
         822               0 :                                SizeOfEntryExcludingThisEnumerator, &arg2);
         823               0 :         n += arg2.total;
         824                 :     }
         825               0 :     return n;
         826                 : }
         827                 :
         828                 : size_t
         829               0 : PL_DHashTableSizeOfIncludingThis(const PLDHashTable *table,
         830                 :                                  PLDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis,
         831                 :                                  nsMallocSizeOfFun mallocSizeOf,
         832                 :                                  void *arg /* = NULL */)
         833                 : {
         834               0 :     return mallocSizeOf(table) +
         835                 :            PL_DHashTableSizeOfExcludingThis(table, sizeOfEntryExcludingThis,
         836               0 :                                             mallocSizeOf, arg);
         837                 : }
         838                 :
         839                 : #ifdef DEBUG
         840                 : void
         841               0 : PL_DHashMarkTableImmutable(PLDHashTable *table)
         842                 : {
         843               0 :     RECURSION_LEVEL(table) = IMMUTABLE_RECURSION_LEVEL;
         844               0 : }
         845                 : #endif
         846                 :
         847                 : #ifdef PL_DHASHMETER
         848                 : #include <math.h>
         849                 :
         850                 : void
         851                 : PL_DHashTableDumpMeter(PLDHashTable *table, PLDHashEnumerator dump, FILE *fp)
         852                 : {
         853                 :     char *entryAddr;
         854                 :     PRUint32 entrySize, entryCount;
         855                 :     int hashShift, sizeLog2;
         856                 :     PRUint32 i, tableSize, sizeMask, chainLen, maxChainLen, chainCount;
         857                 :     PLDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2;
         858                 :     double sqsum, mean, variance, sigma;
         859                 :     PLDHashEntryHdr *entry, *probe;
         860                 :
         861                 :     entryAddr = table->entryStore;
         862                 :     entrySize = table->entrySize;
         863                 :     hashShift = table->hashShift;
         864                 :     sizeLog2 = PL_DHASH_BITS - hashShift;
         865                 :     tableSize = PL_DHASH_TABLE_SIZE(table);
         866                 :     sizeMask = PR_BITMASK(sizeLog2);
         867                 :     chainCount = maxChainLen = 0;
         868                 :     hash2 = 0;
         869                 :     sqsum = 0;
         870                 :
         871                 :     for (i = 0; i < tableSize; i++) {
         872                 :         entry = (PLDHashEntryHdr *)entryAddr;
         873                 :         entryAddr += entrySize;
         874                 :         if (!ENTRY_IS_LIVE(entry))
         875                 :             continue;
         876                 :         hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, hashShift);
         877                 :         saveHash1 = hash1;
         878                 :         probe = ADDRESS_ENTRY(table, hash1);
         879                 :         chainLen = 1;
         880                 :         if (probe == entry) {
         881                 :             /* Start of a (possibly unit-length) chain. */
         882                 :             chainCount++;
         883                 :         } else {
         884                 :             hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, sizeLog2,
         885                 :                           hashShift);
         886                 :             do {
         887                 :                 chainLen++;
         888                 :                 hash1 -= hash2;
         889                 :                 hash1 &= sizeMask;
         890                 :                 probe = ADDRESS_ENTRY(table, hash1);
         891                 :             } while (probe != entry);
         892                 :         }
         893                 :         sqsum += chainLen * chainLen;
         894                 :         if (chainLen > maxChainLen) {
         895                 :             maxChainLen = chainLen;
         896                 :             maxChainHash1 = saveHash1;
         897                 :             maxChainHash2 = hash2;
         898                 :         }
         899                 :     }
         900                 :
         901                 :     entryCount = table->entryCount;
         902                 :     if (entryCount && chainCount) {
         903                 :         mean = (double)entryCount / chainCount;
         904                 :         variance = chainCount * sqsum - entryCount * entryCount;
         905                 :         if (variance < 0 || chainCount == 1)
         906                 :             variance = 0;
         907                 :         else
         908                 :             variance /= chainCount * (chainCount - 1);
         909                 :         sigma = sqrt(variance);
         910                 :     } else {
         911                 :         mean = sigma = 0;
         912                 :     }
         913                 :
         914                 :     fprintf(fp, "Double hashing statistics:\n");
         915                 :     fprintf(fp, "    table size (in entries): %u\n", tableSize);
         916                 :     fprintf(fp, "          number of entries: %u\n", table->entryCount);
         917                 :     fprintf(fp, "  number of removed entries: %u\n", table->removedCount);
         918                 :     fprintf(fp, "         number of searches: %u\n", table->stats.searches);
         919                 :     fprintf(fp, "             number of hits: %u\n", table->stats.hits);
         920                 :     fprintf(fp, "           number of misses: %u\n", table->stats.misses);
         921                 :     fprintf(fp, "      mean steps per search: %g\n", table->stats.searches ?
         922                 :                                                      (double)table->stats.steps
         923                 :                                                      / table->stats.searches :
         924                 :                                                      0.);
         925                 :     fprintf(fp, "     mean hash chain length: %g\n", mean);
         926                 :     fprintf(fp, "         standard deviation: %g\n", sigma);
         927                 :     fprintf(fp, "  maximum hash chain length: %u\n", maxChainLen);
         928                 :     fprintf(fp, "          number of lookups: %u\n", table->stats.lookups);
         929                 :     fprintf(fp, " adds that made a new entry: %u\n", table->stats.addMisses);
         930                 :     fprintf(fp, "adds that recycled removeds: %u\n", table->stats.addOverRemoved);
         931                 :     fprintf(fp, "   adds that found an entry: %u\n", table->stats.addHits);
         932                 :     fprintf(fp, "               add failures: %u\n", table->stats.addFailures);
         933                 :     fprintf(fp, "             useful removes: %u\n", table->stats.removeHits);
         934                 :     fprintf(fp, "            useless removes: %u\n", table->stats.removeMisses);
         935                 :     fprintf(fp, "removes that freed an entry: %u\n", table->stats.removeFrees);
         936                 :     fprintf(fp, "  removes while enumerating: %u\n", table->stats.removeEnums);
         937                 :     fprintf(fp, "            number of grows: %u\n", table->stats.grows);
         938                 :     fprintf(fp, "          number of shrinks: %u\n", table->stats.shrinks);
         939                 :     fprintf(fp, "       number of compresses: %u\n", table->stats.compresses);
         940                 :     fprintf(fp, "number of enumerate shrinks: %u\n", table->stats.enumShrinks);
         941                 :
         942                 :     if (dump && maxChainLen && hash2) {
         943                 :         fputs("Maximum hash chain:\n", fp);
         944                 :         hash1 = maxChainHash1;
         945                 :         hash2 = maxChainHash2;
         946                 :         entry = ADDRESS_ENTRY(table, hash1);
         947                 :         i = 0;
         948                 :         do {
         949                 :             if (dump(table, entry, i++, fp) != PL_DHASH_NEXT)
         950                 :                 break;
         951                 :             hash1 -= hash2;
         952                 :             hash1 &= sizeMask;
         953                 :             entry = ADDRESS_ENTRY(table, hash1);
         954                 :         } while (PL_DHASH_ENTRY_IS_BUSY(entry));
         955                 :     }
         956                 : }
         957                 : #endif /* PL_DHASHMETER */

Generated by: LCOV version 1.7