LCOV - code coverage report
Current view: directory - js/src - jsdhash.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 276 230 83.3 %
Date: 2012-06-02 Functions: 26 21 80.8 %

       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 xpcom/glue/pldhash.cpp.
      45                 :  */
      46                 : #include <stdio.h>
      47                 : #include <stdlib.h>
      48                 : #include <string.h>
      49                 : #include "jsdhash.h"
      50                 : #include "jsutil.h"
      51                 : 
      52                 : using namespace js;
      53                 : 
      54                 : #ifdef JS_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                 :  * JSDHashTable, which could cause issues when mixing DEBUG and
      69                 :  * non-DEBUG components.
      70                 :  */
      71                 : #ifdef DEBUG
      72                 : 
      73                 : #define JSDHASH_ONELINE_ASSERT JS_ASSERT
      74                 : #define RECURSION_LEVEL(table_) (*(uint32_t*)(table_->entryStore +            \
      75                 :                                               JS_DHASH_TABLE_SIZE(table_) *   \
      76                 :                                               table_->entrySize))
      77                 : /*
      78                 :  * Most callers that assert about the recursion level don't care about
      79                 :  * this magical value because they are asserting that mutation is
      80                 :  * allowed (and therefore the level is 0 or 1, depending on whether they
      81                 :  * incremented it).
      82                 :  *
      83                 :  * Only PL_DHashTableFinish needs to allow this special value.
      84                 :  */
      85                 : #define IMMUTABLE_RECURSION_LEVEL UINT32_MAX
      86                 : 
      87                 : #define RECURSION_LEVEL_SAFE_TO_FINISH(table_)                                \
      88                 :     (RECURSION_LEVEL(table_) == 0 ||                                          \
      89                 :      RECURSION_LEVEL(table_) == IMMUTABLE_RECURSION_LEVEL)
      90                 : 
      91                 : #define ENTRY_STORE_EXTRA                   sizeof(uint32_t)
      92                 : #define INCREMENT_RECURSION_LEVEL(table_)                                     \
      93                 :     JS_BEGIN_MACRO                                                            \
      94                 :         if (RECURSION_LEVEL(table_) != IMMUTABLE_RECURSION_LEVEL)             \
      95                 :             ++RECURSION_LEVEL(table_);                                        \
      96                 :     JS_END_MACRO
      97                 : #define DECREMENT_RECURSION_LEVEL(table_)                                     \
      98                 :     JS_BEGIN_MACRO                                                            \
      99                 :         if (RECURSION_LEVEL(table_) != IMMUTABLE_RECURSION_LEVEL) {           \
     100                 :             JS_ASSERT(RECURSION_LEVEL(table_) > 0);                           \
     101                 :             --RECURSION_LEVEL(table_);                                        \
     102                 :         }                                                                     \
     103                 :     JS_END_MACRO
     104                 : 
     105                 : #else
     106                 : 
     107                 : #define ENTRY_STORE_EXTRA 0
     108                 : #define INCREMENT_RECURSION_LEVEL(table_)   JS_BEGIN_MACRO JS_END_MACRO
     109                 : #define DECREMENT_RECURSION_LEVEL(table_)   JS_BEGIN_MACRO JS_END_MACRO
     110                 : 
     111                 : #endif /* defined(DEBUG) */
     112                 : 
     113                 : JS_PUBLIC_API(void *)
     114          144158 : JS_DHashAllocTable(JSDHashTable *table, uint32_t nbytes)
     115                 : {
     116          144158 :     return OffTheBooks::malloc_(nbytes);
     117                 : }
     118                 : 
     119                 : JS_PUBLIC_API(void)
     120          144135 : JS_DHashFreeTable(JSDHashTable *table, void *ptr)
     121                 : {
     122          144135 :     UnwantedForeground::free_(ptr);
     123          144135 : }
     124                 : 
     125                 : JS_PUBLIC_API(JSDHashNumber)
     126               0 : JS_DHashStringKey(JSDHashTable *table, const void *key)
     127                 : {
     128                 :     JSDHashNumber h;
     129                 :     const unsigned char *s;
     130                 : 
     131               0 :     h = 0;
     132               0 :     for (s = (const unsigned char *) key; *s != '\0'; s++)
     133               0 :         h = JS_ROTATE_LEFT32(h, 4) ^ *s;
     134               0 :     return h;
     135                 : }
     136                 : 
     137                 : JS_PUBLIC_API(JSDHashNumber)
     138         8286606 : JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key)
     139                 : {
     140         8286606 :     return (JSDHashNumber)(uintptr_t)key >> 2;
     141                 : }
     142                 : 
     143                 : JS_PUBLIC_API(JSBool)
     144         4223493 : JS_DHashMatchEntryStub(JSDHashTable *table,
     145                 :                        const JSDHashEntryHdr *entry,
     146                 :                        const void *key)
     147                 : {
     148         4223493 :     const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry;
     149                 : 
     150         4223493 :     return stub->key == key;
     151                 : }
     152                 : 
     153                 : JS_PUBLIC_API(JSBool)
     154               0 : JS_DHashMatchStringKey(JSDHashTable *table,
     155                 :                        const JSDHashEntryHdr *entry,
     156                 :                        const void *key)
     157                 : {
     158               0 :     const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry;
     159                 : 
     160                 :     /* XXX tolerate null keys on account of sloppy Mozilla callers. */
     161                 :     return stub->key == key ||
     162                 :            (stub->key && key &&
     163               0 :             strcmp((const char *) stub->key, (const char *) key) == 0);
     164                 : }
     165                 : 
     166                 : JS_PUBLIC_API(void)
     167         3307662 : JS_DHashMoveEntryStub(JSDHashTable *table,
     168                 :                       const JSDHashEntryHdr *from,
     169                 :                       JSDHashEntryHdr *to)
     170                 : {
     171         3307662 :     js_memcpy(to, from, table->entrySize);
     172         3307662 : }
     173                 : 
     174                 : JS_PUBLIC_API(void)
     175         2338863 : JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry)
     176                 : {
     177         2338863 :     memset(entry, 0, table->entrySize);
     178         2338863 : }
     179                 : 
     180                 : JS_PUBLIC_API(void)
     181               0 : JS_DHashFreeStringKey(JSDHashTable *table, JSDHashEntryHdr *entry)
     182                 : {
     183               0 :     const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry;
     184                 : 
     185               0 :     UnwantedForeground::free_((void *) stub->key);
     186               0 :     memset(entry, 0, table->entrySize);
     187               0 : }
     188                 : 
     189                 : JS_PUBLIC_API(void)
     190           61873 : JS_DHashFinalizeStub(JSDHashTable *table)
     191                 : {
     192           61873 : }
     193                 : 
     194                 : static const JSDHashTableOps stub_ops = {
     195                 :     JS_DHashAllocTable,
     196                 :     JS_DHashFreeTable,
     197                 :     JS_DHashVoidPtrKeyStub,
     198                 :     JS_DHashMatchEntryStub,
     199                 :     JS_DHashMoveEntryStub,
     200                 :     JS_DHashClearEntryStub,
     201                 :     JS_DHashFinalizeStub,
     202                 :     NULL
     203                 : };
     204                 : 
     205                 : JS_PUBLIC_API(const JSDHashTableOps *)
     206           54876 : JS_DHashGetStubOps(void)
     207                 : {
     208           54876 :     return &stub_ops;
     209                 : }
     210                 : 
     211                 : JS_PUBLIC_API(JSDHashTable *)
     212           60465 : JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32_t entrySize,
     213                 :                  uint32_t capacity)
     214                 : {
     215                 :     JSDHashTable *table;
     216                 : 
     217           60465 :     table = (JSDHashTable *) OffTheBooks::malloc_(sizeof *table);
     218           60465 :     if (!table)
     219               0 :         return NULL;
     220           60465 :     if (!JS_DHashTableInit(table, ops, data, entrySize, capacity)) {
     221               0 :         Foreground::free_(table);
     222               0 :         return NULL;
     223                 :     }
     224           60465 :     return table;
     225                 : }
     226                 : 
     227                 : JS_PUBLIC_API(void)
     228           60443 : JS_DHashTableDestroy(JSDHashTable *table)
     229                 : {
     230           60443 :     JS_DHashTableFinish(table);
     231           60443 :     UnwantedForeground::free_(table);
     232           60443 : }
     233                 : 
     234                 : JS_PUBLIC_API(JSBool)
     235           61896 : JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data,
     236                 :                   uint32_t entrySize, uint32_t capacity)
     237                 : {
     238                 :     int log2;
     239                 :     uint32_t nbytes;
     240                 : 
     241                 : #ifdef DEBUG
     242           61896 :     if (entrySize > 10 * sizeof(void *)) {
     243                 :         fprintf(stderr,
     244                 :                 "jsdhash: for the table at address %p, the given entrySize"
     245                 :                 " of %lu %s favors chaining over double hashing.\n",
     246                 :                 (void *) table,
     247                 :                 (unsigned long) entrySize,
     248               0 :                 (entrySize > 16 * sizeof(void*)) ? "definitely" : "probably");
     249                 :     }
     250                 : #endif
     251                 : 
     252           61896 :     table->ops = ops;
     253           61896 :     table->data = data;
     254           61896 :     if (capacity < JS_DHASH_MIN_SIZE)
     255            1404 :         capacity = JS_DHASH_MIN_SIZE;
     256                 : 
     257           61896 :     JS_CEILING_LOG2(log2, capacity);
     258                 : 
     259           61896 :     capacity = JS_BIT(log2);
     260           61896 :     if (capacity >= JS_DHASH_SIZE_LIMIT)
     261               0 :         return JS_FALSE;
     262           61896 :     table->hashShift = JS_DHASH_BITS - log2;
     263           61896 :     table->maxAlphaFrac = (uint8_t)(0x100 * JS_DHASH_DEFAULT_MAX_ALPHA);
     264           61896 :     table->minAlphaFrac = (uint8_t)(0x100 * JS_DHASH_DEFAULT_MIN_ALPHA);
     265           61896 :     table->entrySize = entrySize;
     266           61896 :     table->entryCount = table->removedCount = 0;
     267           61896 :     table->generation = 0;
     268           61896 :     nbytes = capacity * entrySize;
     269                 : 
     270                 :     table->entryStore = (char *) ops->allocTable(table,
     271           61896 :                                                  nbytes + ENTRY_STORE_EXTRA);
     272           61896 :     if (!table->entryStore)
     273               0 :         return JS_FALSE;
     274           61896 :     memset(table->entryStore, 0, nbytes);
     275                 :     METER(memset(&table->stats, 0, sizeof table->stats));
     276                 : 
     277                 : #ifdef DEBUG
     278           61896 :     RECURSION_LEVEL(table) = 0;
     279                 : #endif
     280                 : 
     281           61896 :     return JS_TRUE;
     282                 : }
     283                 : 
     284                 : /*
     285                 :  * Compute max and min load numbers (entry counts) from table params.
     286                 :  */
     287                 : #define MAX_LOAD(table, size)   (((table)->maxAlphaFrac * (size)) >> 8)
     288                 : #define MIN_LOAD(table, size)   (((table)->minAlphaFrac * (size)) >> 8)
     289                 : 
     290                 : JS_PUBLIC_API(void)
     291               0 : JS_DHashTableSetAlphaBounds(JSDHashTable *table,
     292                 :                             float maxAlpha,
     293                 :                             float minAlpha)
     294                 : {
     295                 :     uint32_t size;
     296                 : 
     297                 :     /*
     298                 :      * Reject obviously insane bounds, rather than trying to guess what the
     299                 :      * buggy caller intended.
     300                 :      */
     301               0 :     JS_ASSERT(0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha);
     302               0 :     if (maxAlpha < 0.5 || 1 <= maxAlpha || minAlpha < 0)
     303               0 :         return;
     304                 : 
     305                 :     /*
     306                 :      * Ensure that at least one entry will always be free.  If maxAlpha at
     307                 :      * minimum size leaves no entries free, reduce maxAlpha based on minimum
     308                 :      * size and the precision limit of maxAlphaFrac's fixed point format.
     309                 :      */
     310               0 :     JS_ASSERT(JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) >= 1);
     311               0 :     if (JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) < 1) {
     312                 :         maxAlpha = (float)
     313                 :                    (JS_DHASH_MIN_SIZE - JS_MAX(JS_DHASH_MIN_SIZE / 256, 1))
     314               0 :                    / JS_DHASH_MIN_SIZE;
     315                 :     }
     316                 : 
     317                 :     /*
     318                 :      * Ensure that minAlpha is strictly less than half maxAlpha.  Take care
     319                 :      * not to truncate an entry's worth of alpha when storing in minAlphaFrac
     320                 :      * (8-bit fixed point format).
     321                 :      */
     322               0 :     JS_ASSERT(minAlpha < maxAlpha / 2);
     323               0 :     if (minAlpha >= maxAlpha / 2) {
     324               0 :         size = JS_DHASH_TABLE_SIZE(table);
     325               0 :         minAlpha = (size * maxAlpha - JS_MAX(size / 256, 1)) / (2 * size);
     326                 :     }
     327                 : 
     328               0 :     table->maxAlphaFrac = (uint8_t)(maxAlpha * 256);
     329               0 :     table->minAlphaFrac = (uint8_t)(minAlpha * 256);
     330                 : }
     331                 : 
     332                 : /*
     333                 :  * Double hashing needs the second hash code to be relatively prime to table
     334                 :  * size, so we simply make hash2 odd.
     335                 :  */
     336                 : #define HASH1(hash0, shift)         ((hash0) >> (shift))
     337                 : #define HASH2(hash0,log2,shift)     ((((hash0) << (log2)) >> (shift)) | 1)
     338                 : 
     339                 : /*
     340                 :  * Reserve keyHash 0 for free entries and 1 for removed-entry sentinels.  Note
     341                 :  * that a removed-entry sentinel need be stored only if the removed entry had
     342                 :  * a colliding entry added after it.  Therefore we can use 1 as the collision
     343                 :  * flag in addition to the removed-entry sentinel value.  Multiplicative hash
     344                 :  * uses the high order bits of keyHash, so this least-significant reservation
     345                 :  * should not hurt the hash function's effectiveness much.
     346                 :  *
     347                 :  * If you change any of these magic numbers, also update JS_DHASH_ENTRY_IS_LIVE
     348                 :  * in jsdhash.h.  It used to be private to jsdhash.c, but then became public to
     349                 :  * assist iterator writers who inspect table->entryStore directly.
     350                 :  */
     351                 : #define COLLISION_FLAG              ((JSDHashNumber) 1)
     352                 : #define MARK_ENTRY_FREE(entry)      ((entry)->keyHash = 0)
     353                 : #define MARK_ENTRY_REMOVED(entry)   ((entry)->keyHash = 1)
     354                 : #define ENTRY_IS_REMOVED(entry)     ((entry)->keyHash == 1)
     355                 : #define ENTRY_IS_LIVE(entry)        JS_DHASH_ENTRY_IS_LIVE(entry)
     356                 : #define ENSURE_LIVE_KEYHASH(hash0)  if (hash0 < 2) hash0 -= 2; else (void)0
     357                 : 
     358                 : /* Match an entry's keyHash against an unstored one computed from a key. */
     359                 : #define MATCH_ENTRY_KEYHASH(entry,hash0) \
     360                 :     (((entry)->keyHash & ~COLLISION_FLAG) == (hash0))
     361                 : 
     362                 : /* Compute the address of the indexed entry in table. */
     363                 : #define ADDRESS_ENTRY(table, index) \
     364                 :     ((JSDHashEntryHdr *)((table)->entryStore + (index) * (table)->entrySize))
     365                 : 
     366                 : JS_PUBLIC_API(void)
     367           61873 : JS_DHashTableFinish(JSDHashTable *table)
     368                 : {
     369                 :     char *entryAddr, *entryLimit;
     370                 :     uint32_t entrySize;
     371                 :     JSDHashEntryHdr *entry;
     372                 : 
     373                 : #ifdef DEBUG_XXXbrendan
     374                 :     static FILE *dumpfp = NULL;
     375                 :     if (!dumpfp) dumpfp = fopen("/tmp/jsdhash.bigdump", "w");
     376                 :     if (dumpfp) {
     377                 : #ifdef MOZILLA_CLIENT
     378                 :         NS_TraceStack(1, dumpfp);
     379                 : #endif
     380                 :         JS_DHashTableDumpMeter(table, NULL, dumpfp);
     381                 :         fputc('\n', dumpfp);
     382                 :     }
     383                 : #endif
     384                 : 
     385           61873 :     INCREMENT_RECURSION_LEVEL(table);
     386                 : 
     387                 :     /* Call finalize before clearing entries, so it can enumerate them. */
     388           61873 :     table->ops->finalize(table);
     389                 : 
     390                 :     /* Clear any remaining live entries. */
     391           61873 :     entryAddr = table->entryStore;
     392           61873 :     entrySize = table->entrySize;
     393           61873 :     entryLimit = entryAddr + JS_DHASH_TABLE_SIZE(table) * entrySize;
     394         2236050 :     while (entryAddr < entryLimit) {
     395         2112304 :         entry = (JSDHashEntryHdr *)entryAddr;
     396         2112304 :         if (ENTRY_IS_LIVE(entry)) {
     397                 :             METER(table->stats.removeEnums++);
     398          170462 :             table->ops->clearEntry(table, entry);
     399                 :         }
     400         2112304 :         entryAddr += entrySize;
     401                 :     }
     402                 : 
     403           61873 :     DECREMENT_RECURSION_LEVEL(table);
     404           61873 :     JS_ASSERT(RECURSION_LEVEL_SAFE_TO_FINISH(table));
     405                 : 
     406                 :     /* Free entry storage last. */
     407           61873 :     table->ops->freeTable(table, table->entryStore);
     408           61873 : }
     409                 : 
     410                 : static JSDHashEntryHdr * JS_DHASH_FASTCALL
     411        12734916 : SearchTable(JSDHashTable *table, const void *key, JSDHashNumber keyHash,
     412                 :             JSDHashOperator op)
     413                 : {
     414                 :     JSDHashNumber hash1, hash2;
     415                 :     int hashShift, sizeLog2;
     416                 :     JSDHashEntryHdr *entry, *firstRemoved;
     417                 :     JSDHashMatchEntry matchEntry;
     418                 :     uint32_t sizeMask;
     419                 : 
     420                 :     METER(table->stats.searches++);
     421        12734916 :     JS_ASSERT(!(keyHash & COLLISION_FLAG));
     422                 : 
     423                 :     /* Compute the primary hash address. */
     424        12734916 :     hashShift = table->hashShift;
     425        12734916 :     hash1 = HASH1(keyHash, hashShift);
     426        12734916 :     entry = ADDRESS_ENTRY(table, hash1);
     427                 : 
     428                 :     /* Miss: return space for a new entry. */
     429        12734916 :     if (JS_DHASH_ENTRY_IS_FREE(entry)) {
     430                 :         METER(table->stats.misses++);
     431         2590636 :         return entry;
     432                 :     }
     433                 : 
     434                 :     /* Hit: return entry. */
     435        10144280 :     matchEntry = table->ops->matchEntry;
     436        10144280 :     if (MATCH_ENTRY_KEYHASH(entry, keyHash) && matchEntry(table, entry, key)) {
     437                 :         METER(table->stats.hits++);
     438         5418228 :         return entry;
     439                 :     }
     440                 : 
     441                 :     /* Collision: double hash. */
     442         4726052 :     sizeLog2 = JS_DHASH_BITS - table->hashShift;
     443         4726052 :     hash2 = HASH2(keyHash, sizeLog2, hashShift);
     444         4726052 :     sizeMask = JS_BITMASK(sizeLog2);
     445                 : 
     446                 :     /* Save the first removed entry pointer so JS_DHASH_ADD can recycle it. */
     447         4726052 :     firstRemoved = NULL;
     448                 : 
     449         4922653 :     for (;;) {
     450         9648705 :         if (JS_UNLIKELY(ENTRY_IS_REMOVED(entry))) {
     451          798018 :             if (!firstRemoved)
     452          600248 :                 firstRemoved = entry;
     453                 :         } else {
     454         8850687 :             if (op == JS_DHASH_ADD)
     455         2541318 :                 entry->keyHash |= COLLISION_FLAG;
     456                 :         }
     457                 : 
     458                 :         METER(table->stats.steps++);
     459         9648705 :         hash1 -= hash2;
     460         9648705 :         hash1 &= sizeMask;
     461                 : 
     462         9648705 :         entry = ADDRESS_ENTRY(table, hash1);
     463         9648705 :         if (JS_DHASH_ENTRY_IS_FREE(entry)) {
     464                 :             METER(table->stats.misses++);
     465         2087511 :             return (firstRemoved && op == JS_DHASH_ADD) ? firstRemoved : entry;
     466                 :         }
     467                 : 
     468        10201430 :         if (MATCH_ENTRY_KEYHASH(entry, keyHash) &&
     469         2640236 :             matchEntry(table, entry, key)) {
     470                 :             METER(table->stats.hits++);
     471         2638541 :             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 JSDHashEntryHdr * JS_DHASH_FASTCALL
     490         3307662 : FindFreeEntry(JSDHashTable *table, JSDHashNumber keyHash)
     491                 : {
     492                 :     JSDHashNumber hash1, hash2;
     493                 :     int hashShift, sizeLog2;
     494                 :     JSDHashEntryHdr *entry;
     495                 :     uint32_t sizeMask;
     496                 : 
     497                 :     METER(table->stats.searches++);
     498         3307662 :     JS_ASSERT(!(keyHash & COLLISION_FLAG));
     499                 : 
     500                 :     /* Compute the primary hash address. */
     501         3307662 :     hashShift = table->hashShift;
     502         3307662 :     hash1 = HASH1(keyHash, hashShift);
     503         3307662 :     entry = ADDRESS_ENTRY(table, hash1);
     504                 : 
     505                 :     /* Miss: return space for a new entry. */
     506         3307662 :     if (JS_DHASH_ENTRY_IS_FREE(entry)) {
     507                 :         METER(table->stats.misses++);
     508         2649995 :         return entry;
     509                 :     }
     510                 : 
     511                 :     /* Collision: double hash. */
     512          657667 :     sizeLog2 = JS_DHASH_BITS - table->hashShift;
     513          657667 :     hash2 = HASH2(keyHash, sizeLog2, hashShift);
     514          657667 :     sizeMask = JS_BITMASK(sizeLog2);
     515                 : 
     516          251740 :     for (;;) {
     517          909407 :         JS_ASSERT(!ENTRY_IS_REMOVED(entry));
     518          909407 :         entry->keyHash |= COLLISION_FLAG;
     519                 : 
     520                 :         METER(table->stats.steps++);
     521          909407 :         hash1 -= hash2;
     522          909407 :         hash1 &= sizeMask;
     523                 : 
     524          909407 :         entry = ADDRESS_ENTRY(table, hash1);
     525          909407 :         if (JS_DHASH_ENTRY_IS_FREE(entry)) {
     526                 :             METER(table->stats.misses++);
     527          657667 :             return entry;
     528                 :         }
     529                 :     }
     530                 : 
     531                 :     /* NOTREACHED */
     532                 :     return NULL;
     533                 : }
     534                 : 
     535                 : static JSBool
     536           82262 : ChangeTable(JSDHashTable *table, int deltaLog2)
     537                 : {
     538                 :     int oldLog2, newLog2;
     539                 :     uint32_t oldCapacity, newCapacity;
     540                 :     char *newEntryStore, *oldEntryStore, *oldEntryAddr;
     541                 :     uint32_t entrySize, i, nbytes;
     542                 :     JSDHashEntryHdr *oldEntry, *newEntry;
     543                 :     JSDHashMoveEntry moveEntry;
     544                 : #ifdef DEBUG
     545                 :     uint32_t recursionLevel;
     546                 : #endif
     547                 : 
     548                 :     /* Look, but don't touch, until we succeed in getting new entry store. */
     549           82262 :     oldLog2 = JS_DHASH_BITS - table->hashShift;
     550           82262 :     newLog2 = oldLog2 + deltaLog2;
     551           82262 :     oldCapacity = JS_BIT(oldLog2);
     552           82262 :     newCapacity = JS_BIT(newLog2);
     553           82262 :     if (newCapacity >= JS_DHASH_SIZE_LIMIT)
     554               0 :         return JS_FALSE;
     555           82262 :     entrySize = table->entrySize;
     556           82262 :     nbytes = newCapacity * entrySize;
     557                 : 
     558                 :     newEntryStore = (char *) table->ops->allocTable(table,
     559           82262 :                                                     nbytes + ENTRY_STORE_EXTRA);
     560           82262 :     if (!newEntryStore)
     561               0 :         return JS_FALSE;
     562                 : 
     563                 :     /* We can't fail from here on, so update table parameters. */
     564                 : #ifdef DEBUG
     565           82262 :     recursionLevel = RECURSION_LEVEL(table);
     566                 : #endif
     567           82262 :     table->hashShift = JS_DHASH_BITS - newLog2;
     568           82262 :     table->removedCount = 0;
     569           82262 :     table->generation++;
     570                 : 
     571                 :     /* Assign the new entry store to table. */
     572           82262 :     memset(newEntryStore, 0, nbytes);
     573           82262 :     oldEntryAddr = oldEntryStore = table->entryStore;
     574           82262 :     table->entryStore = newEntryStore;
     575           82262 :     moveEntry = table->ops->moveEntry;
     576                 : #ifdef DEBUG
     577           82262 :     RECURSION_LEVEL(table) = recursionLevel;
     578                 : #endif
     579                 : 
     580                 :     /* Copy only live entries, leaving removed ones behind. */
     581         9175350 :     for (i = 0; i < oldCapacity; i++) {
     582         9093088 :         oldEntry = (JSDHashEntryHdr *)oldEntryAddr;
     583         9093088 :         if (ENTRY_IS_LIVE(oldEntry)) {
     584         3307662 :             oldEntry->keyHash &= ~COLLISION_FLAG;
     585         3307662 :             newEntry = FindFreeEntry(table, oldEntry->keyHash);
     586         3307662 :             JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(newEntry));
     587         3307662 :             moveEntry(table, oldEntry, newEntry);
     588         3307662 :             newEntry->keyHash = oldEntry->keyHash;
     589                 :         }
     590         9093088 :         oldEntryAddr += entrySize;
     591                 :     }
     592                 : 
     593           82262 :     table->ops->freeTable(table, oldEntryStore);
     594           82262 :     return JS_TRUE;
     595                 : }
     596                 : 
     597                 : JS_PUBLIC_API(JSDHashEntryHdr *) JS_DHASH_FASTCALL
     598        12734916 : JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op)
     599                 : {
     600                 :     JSDHashNumber keyHash;
     601                 :     JSDHashEntryHdr *entry;
     602                 :     uint32_t size;
     603                 :     int deltaLog2;
     604                 : 
     605        12734916 :     JS_ASSERT(op == JS_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0);
     606        12734916 :     INCREMENT_RECURSION_LEVEL(table);
     607                 : 
     608        12734916 :     keyHash = table->ops->hashKey(table, key);
     609        12734916 :     keyHash *= JS_DHASH_GOLDEN_RATIO;
     610                 : 
     611                 :     /* Avoid 0 and 1 hash codes, they indicate free and removed entries. */
     612        12734916 :     ENSURE_LIVE_KEYHASH(keyHash);
     613        12734916 :     keyHash &= ~COLLISION_FLAG;
     614                 : 
     615        12734916 :     switch (op) {
     616                 :       case JS_DHASH_LOOKUP:
     617                 :         METER(table->stats.lookups++);
     618         7996520 :         entry = SearchTable(table, key, keyHash, op);
     619         7996520 :         break;
     620                 : 
     621                 :       case JS_DHASH_ADD:
     622                 :         /*
     623                 :          * If alpha is >= .75, grow or compress the table.  If key is already
     624                 :          * in the table, we may grow once more than necessary, but only if we
     625                 :          * are on the edge of being overloaded.
     626                 :          */
     627         2705567 :         size = JS_DHASH_TABLE_SIZE(table);
     628         2705567 :         if (table->entryCount + table->removedCount >= MAX_LOAD(table, size)) {
     629                 :             /* Compress if a quarter or more of all entries are removed. */
     630           21414 :             if (table->removedCount >= size >> 2) {
     631                 :                 METER(table->stats.compresses++);
     632             138 :                 deltaLog2 = 0;
     633                 :             } else {
     634                 :                 METER(table->stats.grows++);
     635           21276 :                 deltaLog2 = 1;
     636                 :             }
     637                 : 
     638                 :             /*
     639                 :              * Grow or compress table, returning null if ChangeTable fails and
     640                 :              * falling through might claim the last free entry.
     641                 :              */
     642           21414 :             if (!ChangeTable(table, deltaLog2) &&
     643                 :                 table->entryCount + table->removedCount == size - 1) {
     644                 :                 METER(table->stats.addFailures++);
     645               0 :                 entry = NULL;
     646               0 :                 break;
     647                 :             }
     648                 :         }
     649                 : 
     650                 :         /*
     651                 :          * Look for entry after possibly growing, so we don't have to add it,
     652                 :          * then skip it while growing the table and re-add it after.
     653                 :          */
     654         2705567 :         entry = SearchTable(table, key, keyHash, op);
     655         2705567 :         if (!ENTRY_IS_LIVE(entry)) {
     656                 :             /* Initialize the entry, indicating that it's no longer free. */
     657                 :             METER(table->stats.addMisses++);
     658         2339278 :             if (ENTRY_IS_REMOVED(entry)) {
     659                 :                 METER(table->stats.addOverRemoved++);
     660          130498 :                 table->removedCount--;
     661          130498 :                 keyHash |= COLLISION_FLAG;
     662                 :             }
     663         2339278 :             if (table->ops->initEntry &&
     664               0 :                 !table->ops->initEntry(table, entry, key)) {
     665                 :                 /* We haven't claimed entry yet; fail with null return. */
     666               0 :                 memset(entry + 1, 0, table->entrySize - sizeof *entry);
     667               0 :                 entry = NULL;
     668               0 :                 break;
     669                 :             }
     670         2339278 :             entry->keyHash = keyHash;
     671         2339278 :             table->entryCount++;
     672                 :         }
     673                 :         METER(else table->stats.addHits++);
     674         2705567 :         break;
     675                 : 
     676                 :       case JS_DHASH_REMOVE:
     677         2032829 :         entry = SearchTable(table, key, keyHash, op);
     678         2032829 :         if (ENTRY_IS_LIVE(entry)) {
     679                 :             /* Clear this entry and mark it as "removed". */
     680                 :             METER(table->stats.removeHits++);
     681         1680755 :             JS_DHashTableRawRemove(table, entry);
     682                 : 
     683                 :             /* Shrink if alpha is <= .25 and table isn't too small already. */
     684         1680755 :             size = JS_DHASH_TABLE_SIZE(table);
     685         1680755 :             if (size > JS_DHASH_MIN_SIZE &&
     686                 :                 table->entryCount <= MIN_LOAD(table, size)) {
     687                 :                 METER(table->stats.shrinks++);
     688           46218 :                 (void) ChangeTable(table, -1);
     689                 :             }
     690                 :         }
     691                 :         METER(else table->stats.removeMisses++);
     692         2032829 :         entry = NULL;
     693         2032829 :         break;
     694                 : 
     695                 :       default:
     696               0 :         JS_ASSERT(0);
     697               0 :         entry = NULL;
     698                 :     }
     699                 : 
     700        12734916 :     DECREMENT_RECURSION_LEVEL(table);
     701                 : 
     702        12734916 :     return entry;
     703                 : }
     704                 : 
     705                 : JS_PUBLIC_API(void)
     706         2168706 : JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry)
     707                 : {
     708                 :     JSDHashNumber keyHash;      /* load first in case clearEntry goofs it */
     709                 : 
     710         2168706 :     JS_ASSERT(RECURSION_LEVEL(table) != IMMUTABLE_RECURSION_LEVEL);
     711                 : 
     712         2168706 :     JS_ASSERT(JS_DHASH_ENTRY_IS_LIVE(entry));
     713         2168706 :     keyHash = entry->keyHash;
     714         2168706 :     table->ops->clearEntry(table, entry);
     715         2168706 :     if (keyHash & COLLISION_FLAG) {
     716          667156 :         MARK_ENTRY_REMOVED(entry);
     717          667156 :         table->removedCount++;
     718                 :     } else {
     719                 :         METER(table->stats.removeFrees++);
     720         1501550 :         MARK_ENTRY_FREE(entry);
     721                 :     }
     722         2168706 :     table->entryCount--;
     723         2168706 : }
     724                 : 
     725                 : JS_PUBLIC_API(uint32_t)
     726         1329799 : JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg)
     727                 : {
     728                 :     char *entryAddr, *entryLimit;
     729                 :     uint32_t i, capacity, entrySize, ceiling;
     730                 :     JSBool didRemove;
     731                 :     JSDHashEntryHdr *entry;
     732                 :     JSDHashOperator op;
     733                 : 
     734         1329799 :     INCREMENT_RECURSION_LEVEL(table);
     735                 : 
     736         1329799 :     entryAddr = table->entryStore;
     737         1329799 :     entrySize = table->entrySize;
     738         1329799 :     capacity = JS_DHASH_TABLE_SIZE(table);
     739         1329799 :     entryLimit = entryAddr + capacity * entrySize;
     740         1329799 :     i = 0;
     741         1329799 :     didRemove = JS_FALSE;
     742        63789950 :     while (entryAddr < entryLimit) {
     743        61130352 :         entry = (JSDHashEntryHdr *)entryAddr;
     744        61130352 :         if (ENTRY_IS_LIVE(entry)) {
     745        14236898 :             op = etor(table, entry, i++, arg);
     746        14236898 :             if (op & JS_DHASH_REMOVE) {
     747                 :                 METER(table->stats.removeEnums++);
     748          487951 :                 JS_DHashTableRawRemove(table, entry);
     749          487951 :                 didRemove = JS_TRUE;
     750                 :             }
     751        14236898 :             if (op & JS_DHASH_STOP)
     752               0 :                 break;
     753                 :         }
     754        61130352 :         entryAddr += entrySize;
     755                 :     }
     756                 : 
     757         1329799 :     JS_ASSERT(!didRemove || RECURSION_LEVEL(table) == 1);
     758                 : 
     759                 :     /*
     760                 :      * Shrink or compress if a quarter or more of all entries are removed, or
     761                 :      * if the table is underloaded according to the configured minimum alpha,
     762                 :      * and is not minimal-size already.  Do this only if we removed above, so
     763                 :      * non-removing enumerations can count on stable table->entryStore until
     764                 :      * the next non-lookup-Operate or removing-Enumerate.
     765                 :      */
     766         1329799 :     if (didRemove &&
     767                 :         (table->removedCount >= capacity >> 2 ||
     768                 :          (capacity > JS_DHASH_MIN_SIZE &&
     769                 :           table->entryCount <= MIN_LOAD(table, capacity)))) {
     770                 :         METER(table->stats.enumShrinks++);
     771           14630 :         capacity = table->entryCount;
     772           14630 :         capacity += capacity >> 1;
     773           14630 :         if (capacity < JS_DHASH_MIN_SIZE)
     774           11300 :             capacity = JS_DHASH_MIN_SIZE;
     775                 : 
     776           14630 :         JS_CEILING_LOG2(ceiling, capacity);
     777           14630 :         ceiling -= JS_DHASH_BITS - table->hashShift;
     778                 : 
     779           14630 :         (void) ChangeTable(table, ceiling);
     780                 :     }
     781                 : 
     782         1329799 :     DECREMENT_RECURSION_LEVEL(table);
     783                 : 
     784         1329799 :     return i;
     785                 : }
     786                 : 
     787                 : struct SizeOfEntryExcludingThisArg
     788                 : {
     789                 :     size_t total;
     790                 :     JSDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis;
     791                 :     JSMallocSizeOfFun mallocSizeOf;
     792                 :     void *arg;      // the arg passed by the user
     793                 : };
     794                 : 
     795                 : static JSDHashOperator
     796            3002 : SizeOfEntryExcludingThisEnumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
     797                 :                                    uint32_t number, void *arg)
     798                 : {
     799            3002 :     SizeOfEntryExcludingThisArg *e = (SizeOfEntryExcludingThisArg *)arg;
     800            3002 :     e->total += e->sizeOfEntryExcludingThis(hdr, e->mallocSizeOf, e->arg);
     801            3002 :     return JS_DHASH_NEXT;
     802                 : }
     803                 : 
     804                 : extern JS_PUBLIC_API(size_t)
     805             192 : JS_DHashTableSizeOfExcludingThis(const JSDHashTable *table,
     806                 :                                  JSDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis,
     807                 :                                  JSMallocSizeOfFun mallocSizeOf,
     808                 :                                  void *arg /* = NULL */)
     809                 : {
     810             192 :     size_t n = 0;
     811             192 :     n += mallocSizeOf(table->entryStore);
     812             192 :     if (sizeOfEntryExcludingThis) {
     813             186 :         SizeOfEntryExcludingThisArg arg2 = { 0, sizeOfEntryExcludingThis, mallocSizeOf, arg };
     814                 :         JS_DHashTableEnumerate(const_cast<JSDHashTable *>(table),
     815             186 :                                SizeOfEntryExcludingThisEnumerator, &arg2);
     816             186 :         n += arg2.total;
     817                 :     }
     818             192 :     return n;
     819                 : }
     820                 : 
     821                 : extern JS_PUBLIC_API(size_t)
     822             189 : JS_DHashTableSizeOfIncludingThis(const JSDHashTable *table,
     823                 :                                  JSDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis,
     824                 :                                  JSMallocSizeOfFun mallocSizeOf,
     825                 :                                  void *arg /* = NULL */)
     826                 : {
     827             189 :     return mallocSizeOf(table) +
     828                 :            JS_DHashTableSizeOfExcludingThis(table, sizeOfEntryExcludingThis,
     829             189 :                                             mallocSizeOf, arg);
     830                 : }
     831                 : 
     832                 : #ifdef DEBUG
     833                 : JS_PUBLIC_API(void)
     834               0 : JS_DHashMarkTableImmutable(JSDHashTable *table)
     835                 : {
     836               0 :     RECURSION_LEVEL(table) = IMMUTABLE_RECURSION_LEVEL;
     837               0 : }
     838                 : #endif
     839                 : 
     840                 : #ifdef JS_DHASHMETER
     841                 : #include <math.h>
     842                 : 
     843                 : JS_PUBLIC_API(void)
     844                 : JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp)
     845                 : {
     846                 :     char *entryAddr;
     847                 :     uint32_t entrySize, entryCount;
     848                 :     int hashShift, sizeLog2;
     849                 :     uint32_t i, tableSize, sizeMask, chainLen, maxChainLen, chainCount;
     850                 :     JSDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2;
     851                 :     double sqsum, mean, variance, sigma;
     852                 :     JSDHashEntryHdr *entry, *probe;
     853                 : 
     854                 :     entryAddr = table->entryStore;
     855                 :     entrySize = table->entrySize;
     856                 :     hashShift = table->hashShift;
     857                 :     sizeLog2 = JS_DHASH_BITS - hashShift;
     858                 :     tableSize = JS_DHASH_TABLE_SIZE(table);
     859                 :     sizeMask = JS_BITMASK(sizeLog2);
     860                 :     chainCount = maxChainLen = 0;
     861                 :     hash2 = 0;
     862                 :     sqsum = 0;
     863                 : 
     864                 :     for (i = 0; i < tableSize; i++) {
     865                 :         entry = (JSDHashEntryHdr *)entryAddr;
     866                 :         entryAddr += entrySize;
     867                 :         if (!ENTRY_IS_LIVE(entry))
     868                 :             continue;
     869                 :         hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, hashShift);
     870                 :         saveHash1 = hash1;
     871                 :         probe = ADDRESS_ENTRY(table, hash1);
     872                 :         chainLen = 1;
     873                 :         if (probe == entry) {
     874                 :             /* Start of a (possibly unit-length) chain. */
     875                 :             chainCount++;
     876                 :         } else {
     877                 :             hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, sizeLog2,
     878                 :                           hashShift);
     879                 :             do {
     880                 :                 chainLen++;
     881                 :                 hash1 -= hash2;
     882                 :                 hash1 &= sizeMask;
     883                 :                 probe = ADDRESS_ENTRY(table, hash1);
     884                 :             } while (probe != entry);
     885                 :         }
     886                 :         sqsum += chainLen * chainLen;
     887                 :         if (chainLen > maxChainLen) {
     888                 :             maxChainLen = chainLen;
     889                 :             maxChainHash1 = saveHash1;
     890                 :             maxChainHash2 = hash2;
     891                 :         }
     892                 :     }
     893                 : 
     894                 :     entryCount = table->entryCount;
     895                 :     if (entryCount && chainCount) {
     896                 :         mean = (double)entryCount / chainCount;
     897                 :         variance = chainCount * sqsum - entryCount * entryCount;
     898                 :         if (variance < 0 || chainCount == 1)
     899                 :             variance = 0;
     900                 :         else
     901                 :             variance /= chainCount * (chainCount - 1);
     902                 :         sigma = sqrt(variance);
     903                 :     } else {
     904                 :         mean = sigma = 0;
     905                 :     }
     906                 : 
     907                 :     fprintf(fp, "Double hashing statistics:\n");
     908                 :     fprintf(fp, "    table size (in entries): %u\n", tableSize);
     909                 :     fprintf(fp, "          number of entries: %u\n", table->entryCount);
     910                 :     fprintf(fp, "  number of removed entries: %u\n", table->removedCount);
     911                 :     fprintf(fp, "         number of searches: %u\n", table->stats.searches);
     912                 :     fprintf(fp, "             number of hits: %u\n", table->stats.hits);
     913                 :     fprintf(fp, "           number of misses: %u\n", table->stats.misses);
     914                 :     fprintf(fp, "      mean steps per search: %g\n", table->stats.searches ?
     915                 :                                                      (double)table->stats.steps
     916                 :                                                      / table->stats.searches :
     917                 :                                                      0.);
     918                 :     fprintf(fp, "     mean hash chain length: %g\n", mean);
     919                 :     fprintf(fp, "         standard deviation: %g\n", sigma);
     920                 :     fprintf(fp, "  maximum hash chain length: %u\n", maxChainLen);
     921                 :     fprintf(fp, "          number of lookups: %u\n", table->stats.lookups);
     922                 :     fprintf(fp, " adds that made a new entry: %u\n", table->stats.addMisses);
     923                 :     fprintf(fp, "adds that recycled removeds: %u\n", table->stats.addOverRemoved);
     924                 :     fprintf(fp, "   adds that found an entry: %u\n", table->stats.addHits);
     925                 :     fprintf(fp, "               add failures: %u\n", table->stats.addFailures);
     926                 :     fprintf(fp, "             useful removes: %u\n", table->stats.removeHits);
     927                 :     fprintf(fp, "            useless removes: %u\n", table->stats.removeMisses);
     928                 :     fprintf(fp, "removes that freed an entry: %u\n", table->stats.removeFrees);
     929                 :     fprintf(fp, "  removes while enumerating: %u\n", table->stats.removeEnums);
     930                 :     fprintf(fp, "            number of grows: %u\n", table->stats.grows);
     931                 :     fprintf(fp, "          number of shrinks: %u\n", table->stats.shrinks);
     932                 :     fprintf(fp, "       number of compresses: %u\n", table->stats.compresses);
     933                 :     fprintf(fp, "number of enumerate shrinks: %u\n", table->stats.enumShrinks);
     934                 : 
     935                 :     if (dump && maxChainLen && hash2) {
     936                 :         fputs("Maximum hash chain:\n", fp);
     937                 :         hash1 = maxChainHash1;
     938                 :         hash2 = maxChainHash2;
     939                 :         entry = ADDRESS_ENTRY(table, hash1);
     940                 :         i = 0;
     941                 :         do {
     942                 :             if (dump(table, entry, i++, fp) != JS_DHASH_NEXT)
     943                 :                 break;
     944                 :             hash1 -= hash2;
     945                 :             hash1 &= sizeMask;
     946                 :             entry = ADDRESS_ENTRY(table, hash1);
     947                 :         } while (JS_DHASH_ENTRY_IS_BUSY(entry));
     948                 :     }
     949                 : }
     950                 : #endif /* JS_DHASHMETER */

Generated by: LCOV version 1.7