LCOV - code coverage report
Current view: directory - objdir/dist/include/js - HashTable.h (source / functions) Found Hit Coverage
Test: app.info Lines: 397 374 94.2 %
Date: 2012-06-02 Functions: 2678 2275 85.0 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=8 sw=4 et tw=99 ft=cpp:
       3                 :  *
       4                 :  * ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
      18                 :  * November 13, 2009.
      19                 :  *
      20                 :  * The Initial Developer of the Original Code is
      21                 :  *   the Mozilla Corporation.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Brendan Eich <brendan@mozilla.org> (Original Author)
      25                 :  *   Chris Waterson <waterson@netscape.com>
      26                 :  *   L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
      27                 :  *   Luke Wagner <lw@mozilla.com>
      28                 :  *
      29                 :  * Alternatively, the contents of this file may be used under the terms of
      30                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      31                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      32                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      33                 :  * of those above. If you wish to allow use of your version of this file only
      34                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      35                 :  * use your version of this file under the terms of the MPL, indicate your
      36                 :  * decision by deleting the provisions above and replace them with the notice
      37                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      38                 :  * the provisions above, a recipient may use your version of this file under
      39                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      40                 :  *
      41                 :  * ***** END LICENSE BLOCK ***** */
      42                 : 
      43                 : #ifndef jshashtable_h_
      44                 : #define jshashtable_h_
      45                 : 
      46                 : #include "TemplateLib.h"
      47                 : #include "Utility.h"
      48                 : 
      49                 : namespace js {
      50                 : 
      51                 : class TempAllocPolicy;
      52                 : 
      53                 : /* Integral types for all hash functions. */
      54                 : typedef uint32_t HashNumber;
      55                 : 
      56                 : /*****************************************************************************/
      57                 : 
      58                 : namespace detail {
      59                 : 
      60                 : template <class T, class HashPolicy, class AllocPolicy>
      61                 : class HashTable;
      62                 : 
      63                 : template <class T>
      64       657687540 : class HashTableEntry {
      65                 :     HashNumber keyHash;
      66                 : 
      67                 :     typedef typename tl::StripConst<T>::result NonConstT;
      68                 : 
      69                 :     static const HashNumber sFreeKey = 0;
      70                 :     static const HashNumber sRemovedKey = 1;
      71                 :     static const HashNumber sCollisionBit = 1;
      72                 : 
      73                 :     template <class, class, class> friend class HashTable;
      74                 : 
      75      -696343730 :     static bool isLiveHash(HashNumber hash)
      76                 :     {
      77      -696343730 :         return hash > sRemovedKey;
      78                 :     }
      79                 : 
      80                 :   public:
      81       662645548 :     HashTableEntry() : keyHash(0), t() {}
      82                 :     HashTableEntry(MoveRef<HashTableEntry> rhs) : keyHash(rhs->keyHash), t(Move(rhs->t)) { }
      83                 :     void operator=(const HashTableEntry &rhs) { keyHash = rhs.keyHash; t = rhs.t; }
      84       148567417 :     void operator=(MoveRef<HashTableEntry> rhs) { keyHash = rhs->keyHash; t = Move(rhs->t); }
      85                 : 
      86                 :     NonConstT t;
      87                 : 
      88      1044717016 :     bool isFree() const           { return keyHash == sFreeKey; }
      89        21094972 :     void setFree()                { keyHash = sFreeKey; t = T(); }
      90       482904968 :     bool isRemoved() const        { return keyHash == sRemovedKey; }
      91         8041974 :     void setRemoved()             { keyHash = sRemovedKey; t = T(); }
      92     -1886809747 :     bool isLive() const           { return isLiveHash(keyHash); }
      93       128758205 :     void setLive(HashNumber hn)   { JS_ASSERT(isLiveHash(hn)); keyHash = hn; }
      94                 : 
      95        30060835 :     void setCollision()           { JS_ASSERT(isLive()); keyHash |= sCollisionBit; }
      96       321950952 :     void setCollision(HashNumber collisionBit) {
      97       321950952 :         JS_ASSERT(isLive()); keyHash |= collisionBit;
      98       321950952 :     }
      99       143607921 :     void unsetCollision()         { JS_ASSERT(isLive()); keyHash &= ~sCollisionBit; }
     100       172744867 :     bool hasCollision() const     { JS_ASSERT(isLive()); return keyHash & sCollisionBit; }
     101       490491896 :     bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; }
     102       143607921 :     HashNumber getKeyHash() const { JS_ASSERT(!hasCollision()); return keyHash; }
     103                 : };
     104                 : 
     105                 : /*
     106                 :  * js::detail::HashTable is an implementation detail of the js::HashMap and
     107                 :  * js::HashSet templates. For js::Hash{Map,Set} API documentation and examples,
     108                 :  * skip to the end of the detail namespace.
     109                 :  */
     110                 : 
     111                 : /* Reusable implementation of HashMap and HashSet. */
     112                 : template <class T, class HashPolicy, class AllocPolicy>
     113                 : class HashTable : private AllocPolicy
     114                 : {
     115                 :     typedef typename tl::StripConst<T>::result NonConstT;
     116                 :     typedef typename HashPolicy::KeyType Key;
     117                 :     typedef typename HashPolicy::Lookup Lookup;
     118                 : 
     119                 :   public:
     120                 :     typedef HashTableEntry<T> Entry;
     121                 : 
     122                 :     /*
     123                 :      * A nullable pointer to a hash table element. A Ptr |p| can be tested
     124                 :      * either explicitly |if (p.found()) p->...| or using boolean conversion
     125                 :      * |if (p) p->...|. Ptr objects must not be used after any mutating hash
     126                 :      * table operations unless |generation()| is tested.
     127                 :      */
     128                 :     class Ptr
     129                 :     {
     130                 :         friend class HashTable;
     131                 :         typedef void (Ptr::* ConvertibleToBool)();
     132               0 :         void nonNull() {}
     133                 : 
     134                 :         Entry *entry;
     135                 : 
     136                 :       protected:
     137       515552538 :         Ptr(Entry &entry) : entry(&entry) {}
     138                 : 
     139                 :       public:
     140                 :         /* Leaves Ptr uninitialized. */
     141        59315819 :         Ptr() {
     142                 : #ifdef DEBUG
     143        59315819 :             entry = (Entry *)0xbad;
     144                 : #endif
     145        59315819 :         }
     146                 : 
     147       681651875 :         bool found() const                    { return entry->isLive(); }
     148       493578156 :         operator ConvertibleToBool() const    { return found() ? &Ptr::nonNull : 0; }
     149                 :         bool operator==(const Ptr &rhs) const { JS_ASSERT(found() && rhs.found()); return entry == rhs.entry; }
     150                 :         bool operator!=(const Ptr &rhs) const { return !(*this == rhs); }
     151                 : 
     152        61197240 :         T &operator*() const                  { return entry->t; }
     153       138754087 :         T *operator->() const                 { return &entry->t; }
     154                 :     };
     155                 : 
     156                 :     /* A Ptr that can be used to add a key after a failed lookup. */
     157                 :     class AddPtr : public Ptr
     158                 :     {
     159                 :         friend class HashTable;
     160                 :         HashNumber keyHash;
     161                 : #ifdef DEBUG
     162                 :         uint64_t mutationCount;
     163                 : 
     164       261548343 :         AddPtr(Entry &entry, HashNumber hn, uint64_t mutationCount)
     165       261548343 :             : Ptr(entry), keyHash(hn), mutationCount(mutationCount) {}
     166                 : #else
     167                 :         AddPtr(Entry &entry, HashNumber hn) : Ptr(entry), keyHash(hn) {}
     168                 : #endif
     169                 :       public:
     170                 :         /* Leaves AddPtr uninitialized. */
     171        32318814 :         AddPtr() {}
     172                 :     };
     173                 : 
     174                 :     /*
     175                 :      * A collection of hash table entries. The collection is enumerated by
     176                 :      * calling |front()| followed by |popFront()| as long as |!empty()|. As
     177                 :      * with Ptr/AddPtr, Range objects must not be used after any mutating hash
     178                 :      * table operation unless the |generation()| is tested.
     179                 :      */
     180                 :     class Range
     181                 :     {
     182                 :       protected:
     183                 :         friend class HashTable;
     184                 : 
     185         1621016 :         Range(Entry *c, Entry *e) : cur(c), end(e) {
     186        68731482 :             while (cur < end && !cur->isLive())
     187        65489450 :                 ++cur;
     188         1621016 :         }
     189                 : 
     190                 :         Entry *cur, *end;
     191                 : 
     192                 :       public:
     193         3448687 :         Range() : cur(NULL), end(NULL) {}
     194                 : 
     195       749481267 :         bool empty() const {
     196       749481267 :             return cur == end;
     197                 :         }
     198                 : 
     199       257743706 :         T &front() const {
     200       257743706 :             JS_ASSERT(!empty());
     201       257743706 :             return cur->t;
     202                 :         }
     203                 : 
     204       244775368 :         void popFront() {
     205       244775368 :             JS_ASSERT(!empty());
     206       947376398 :             while (++cur < end && !cur->isLive())
     207       457825662 :                 continue;
     208       244775368 :         }
     209                 :     };
     210                 : 
     211                 :     /*
     212                 :      * A Range whose lifetime delimits a mutating enumeration of a hash table.
     213                 :      * Since rehashing when elements were removed during enumeration would be
     214                 :      * bad, it is postponed until |endEnumeration()| is called. If
     215                 :      * |endEnumeration()| is not called before an Enum's constructor, it will
     216                 :      * be called automatically. Since |endEnumeration()| touches the hash
     217                 :      * table, the user must ensure that the hash table is still alive when this
     218                 :      * happens.
     219                 :      */
     220                 :     class Enum : public Range
     221                 :     {
     222                 :         friend class HashTable;
     223                 : 
     224                 :         HashTable &table;
     225                 :         bool removed;
     226                 : 
     227                 :         /* Not copyable. */
     228                 :         Enum(const Enum &);
     229                 :         void operator=(const Enum &);
     230                 : 
     231                 :       public:
     232                 :         template<class Map> explicit
     233          805797 :         Enum(Map &map) : Range(map.all()), table(map.impl), removed(false) {}
     234                 : 
     235                 :         /*
     236                 :          * Removes the |front()| element from the table, leaving |front()|
     237                 :          * invalid until the next call to |popFront()|. For example:
     238                 :          *
     239                 :          *   HashSet<int> s;
     240                 :          *   for (HashSet<int>::Enum e(s); !e.empty(); e.popFront())
     241                 :          *     if (e.front() == 42)
     242                 :          *       e.removeFront();
     243                 :          */
     244        27078302 :         void removeFront() {
     245        27078302 :             table.remove(*this->cur);
     246        27078302 :             removed = true;
     247        27078302 :         }
     248                 : 
     249                 :         /* Potentially rehashes the table. */
     250          805797 :         ~Enum() {
     251          805797 :             if (removed)
     252          216473 :                 table.checkUnderloaded();
     253          805797 :         }
     254                 : 
     255                 :         /* Can be used to end the enumeration before the destructor. */
     256                 :         void endEnumeration() {
     257                 :             if (removed) {
     258                 :                 table.checkUnderloaded();
     259                 :                 removed = false;
     260                 :             }
     261                 :         }
     262                 :     };
     263                 : 
     264                 :   private:
     265                 :     uint32_t    hashShift;      /* multiplicative hash shift */
     266                 :     uint32_t    entryCount;     /* number of entries in table */
     267                 :     uint32_t    gen;            /* entry storage generation number */
     268                 :     uint32_t    removedCount;   /* removed entry sentinels in table */
     269                 :     Entry       *table;         /* entry storage */
     270                 : 
     271         6054815 :     void setTableSizeLog2(unsigned sizeLog2) {
     272         6054815 :         hashShift = sHashBits - sizeLog2;
     273         6054815 :     }
     274                 : 
     275                 : #ifdef DEBUG
     276                 :     mutable struct Stats {
     277                 :         uint32_t        searches;       /* total number of table searches */
     278                 :         uint32_t        steps;          /* hash chain links traversed */
     279                 :         uint32_t        hits;           /* searches that found key */
     280                 :         uint32_t        misses;         /* searches that didn't find key */
     281                 :         uint32_t        addOverRemoved; /* adds that recycled a removed entry */
     282                 :         uint32_t        removes;        /* calls to remove */
     283                 :         uint32_t        removeFrees;    /* calls to remove that freed the entry */
     284                 :         uint32_t        grows;          /* table expansions */
     285                 :         uint32_t        shrinks;        /* table contractions */
     286                 :         uint32_t        compresses;     /* table compressions */
     287                 :     } stats;
     288                 : #   define METER(x) x
     289                 : #else
     290                 : #   define METER(x)
     291                 : #endif
     292                 : 
     293                 : #ifdef DEBUG
     294                 :     friend class js::ReentrancyGuard;
     295                 :     mutable bool entered;
     296                 :     uint64_t     mutationCount;
     297                 : #endif
     298                 : 
     299                 :     /* The default initial capacity is 16, but you can ask for as small as 4. */
     300                 :     static const unsigned sMinSizeLog2  = 2;
     301                 :     static const unsigned sMinSize      = 1 << sMinSizeLog2;
     302                 :     static const unsigned sDefaultInitSizeLog2 = 4;
     303                 :   public:
     304                 :     static const unsigned sDefaultInitSize = 1 << sDefaultInitSizeLog2;
     305                 :   private:
     306                 :     static const unsigned sMaxInit      = JS_BIT(23);
     307                 :     static const unsigned sMaxCapacity  = JS_BIT(24);
     308                 :     static const unsigned sHashBits     = tl::BitSize<HashNumber>::result;
     309                 :     static const uint8_t  sMinAlphaFrac = 64;  /* (0x100 * .25) taken from jsdhash.h */
     310                 :     static const uint8_t  sMaxAlphaFrac = 192; /* (0x100 * .75) taken from jsdhash.h */
     311                 :     static const uint8_t  sInvMaxAlpha  = 171; /* (ceil(0x100 / .75) >> 1) */
     312                 :     static const HashNumber sGoldenRatio  = 0x9E3779B9U;       /* taken from jsdhash.h */
     313                 :     static const HashNumber sFreeKey = Entry::sFreeKey;
     314                 :     static const HashNumber sRemovedKey = Entry::sRemovedKey;
     315                 :     static const HashNumber sCollisionBit = Entry::sCollisionBit;
     316                 : 
     317                 :     static void staticAsserts()
     318                 :     {
     319                 :         /* Rely on compiler "constant overflow warnings". */
     320                 :         JS_STATIC_ASSERT(((sMaxInit * sInvMaxAlpha) >> 7) < sMaxCapacity);
     321                 :         JS_STATIC_ASSERT((sMaxCapacity * sInvMaxAlpha) <= UINT32_MAX);
     322                 :         JS_STATIC_ASSERT((sMaxCapacity * sizeof(Entry)) <= UINT32_MAX);
     323                 :     }
     324                 : 
     325      1061707812 :     static bool isLiveHash(HashNumber hash)
     326                 :     {
     327      1061707812 :         return Entry::isLiveHash(hash);
     328                 :     }
     329                 : 
     330       515552538 :     static HashNumber prepareHash(const Lookup& l)
     331                 :     {
     332       515552538 :         HashNumber keyHash = HashPolicy::hash(l);
     333                 : 
     334                 :         /* Improve keyHash distribution. */
     335       515552538 :         keyHash *= sGoldenRatio;
     336                 : 
     337                 :         /* Avoid reserved hash codes. */
     338       515552538 :         if (!isLiveHash(keyHash))
     339       125536907 :             keyHash -= (sRemovedKey + 1);
     340       515552538 :         return keyHash & ~sCollisionBit;
     341                 :     }
     342                 : 
     343         6054815 :     static Entry *createTable(AllocPolicy &alloc, uint32_t capacity)
     344                 :     {
     345         6054815 :         Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry));
     346         6054815 :         if (!newTable)
     347               0 :             return NULL;
     348       663740867 :         for (Entry *e = newTable, *end = e + capacity; e < end; ++e)
     349       657686052 :             new(e) Entry();
     350         6054815 :         return newTable;
     351                 :     }
     352                 : 
     353         6054645 :     static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity)
     354                 :     {
     355       663731609 :         for (Entry *e = oldTable, *end = e + capacity; e < end; ++e)
     356       657676964 :             e->~Entry();
     357         6054645 :         alloc.free_(oldTable);
     358         6054645 :     }
     359                 : 
     360                 :   public:
     361         5529820 :     HashTable(AllocPolicy ap)
     362                 :       : AllocPolicy(ap),
     363                 :         hashShift(sHashBits),
     364                 :         entryCount(0),
     365                 :         gen(0),
     366                 :         removedCount(0),
     367                 :         table(NULL)
     368                 : #ifdef DEBUG
     369                 :         , entered(false),
     370         5529820 :         mutationCount(0)
     371                 : #endif
     372         5529820 :     {}
     373                 : 
     374         4895336 :     bool init(uint32_t length)
     375                 :     {
     376                 :         /* Make sure that init isn't called twice. */
     377         4895336 :         JS_ASSERT(table == NULL);
     378                 : 
     379                 :         /*
     380                 :          * Correct for sMaxAlphaFrac such that the table will not resize
     381                 :          * when adding 'length' entries.
     382                 :          */
     383         4895336 :         if (length > sMaxInit) {
     384               0 :             this->reportAllocOverflow();
     385               0 :             return false;
     386                 :         }
     387         4895336 :         uint32_t capacity = (length * sInvMaxAlpha) >> 7;
     388                 : 
     389         4895336 :         if (capacity < sMinSize)
     390         1328764 :             capacity = sMinSize;
     391                 : 
     392                 :         /* FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). */
     393         4895336 :         uint32_t roundUp = sMinSize, roundUpLog2 = sMinSizeLog2;
     394        22492266 :         while (roundUp < capacity) {
     395        12701594 :             roundUp <<= 1;
     396        12701594 :             ++roundUpLog2;
     397                 :         }
     398                 : 
     399         4895336 :         capacity = roundUp;
     400         4895336 :         JS_ASSERT(capacity <= sMaxCapacity);
     401                 : 
     402         4895336 :         table = createTable(*this, capacity);
     403         4895336 :         if (!table)
     404               0 :             return false;
     405                 : 
     406         4895336 :         setTableSizeLog2(roundUpLog2);
     407         4895336 :         METER(memset(&stats, 0, sizeof(stats)));
     408         4895336 :         return true;
     409                 :     }
     410                 : 
     411        81837972 :     bool initialized() const
     412                 :     {
     413        81837972 :         return !!table;
     414                 :     }
     415                 : 
     416         5529637 :     ~HashTable()
     417                 :     {
     418         5529637 :         if (table)
     419         4868161 :             destroyTable(*this, table, capacity());
     420         5529637 :     }
     421                 : 
     422                 :   private:
     423       690570253 :     static HashNumber hash1(HashNumber hash0, uint32_t shift) {
     424       690570253 :         return hash0 >> shift;
     425                 :     }
     426                 : 
     427       161432262 :     static HashNumber hash2(HashNumber hash0, uint32_t log2, uint32_t shift) {
     428       161432262 :         return ((hash0 << log2) >> shift) | 1;
     429                 :     }
     430                 : 
     431       128259723 :     bool overloaded() {
     432       128259723 :         return entryCount + removedCount >= ((sMaxAlphaFrac * capacity()) >> 8);
     433                 :     }
     434                 : 
     435         2275117 :     bool underloaded() {
     436         2275117 :         uint32_t tableCapacity = capacity();
     437                 :         return tableCapacity > sMinSize &&
     438         2275117 :                entryCount <= ((sMinAlphaFrac * tableCapacity) >> 8);
     439                 :     }
     440                 : 
     441       173172113 :     static bool match(Entry &e, const Lookup &l) {
     442       173172113 :         return HashPolicy::match(HashPolicy::getKey(e.t), l);
     443                 :     }
     444                 : 
     445       546155274 :     Entry &lookup(const Lookup &l, HashNumber keyHash, unsigned collisionBit) const
     446                 :     {
     447       546155274 :         JS_ASSERT(isLiveHash(keyHash));
     448       546155274 :         JS_ASSERT(!(keyHash & sCollisionBit));
     449       546155274 :         JS_ASSERT(collisionBit == 0 || collisionBit == sCollisionBit);
     450       546155274 :         JS_ASSERT(table);
     451       546155274 :         METER(stats.searches++);
     452                 : 
     453                 :         /* Compute the primary hash address. */
     454       546155274 :         HashNumber h1 = hash1(keyHash, hashShift);
     455       546155274 :         Entry *entry = &table[h1];
     456                 : 
     457                 :         /* Miss: return space for a new entry. */
     458       546155274 :         if (entry->isFree()) {
     459       288174012 :             METER(stats.misses++);
     460       288174012 :             return *entry;
     461                 :         }
     462                 : 
     463                 :         /* Hit: return entry. */
     464       257981262 :         if (entry->matchHash(keyHash) && match(*entry, l)) {
     465       117520068 :             METER(stats.hits++);
     466       117520068 :             return *entry;
     467                 :         }
     468                 : 
     469                 :         /* Collision: double hash. */
     470       140461194 :         unsigned sizeLog2 = sHashBits - hashShift;
     471       140461194 :         HashNumber h2 = hash2(keyHash, sizeLog2, hashShift);
     472       140461194 :         HashNumber sizeMask = (HashNumber(1) << sizeLog2) - 1;
     473                 : 
     474                 :         /* Save the first removed entry pointer so we can recycle later. */
     475       140461194 :         Entry *firstRemoved = NULL;
     476                 : 
     477       183624734 :         while(true) {
     478       324085928 :             if (JS_UNLIKELY(entry->isRemoved())) {
     479         2134976 :                 if (!firstRemoved)
     480         1526308 :                     firstRemoved = entry;
     481                 :             } else {
     482       321950952 :                 entry->setCollision(collisionBit);
     483                 :             }
     484                 : 
     485       324085928 :             METER(stats.steps++);
     486       324085928 :             h1 -= h2;
     487       324085928 :             h1 &= sizeMask;
     488                 : 
     489       324085928 :             entry = &table[h1];
     490       324085928 :             if (entry->isFree()) {
     491        91575294 :                 METER(stats.misses++);
     492        91575294 :                 return firstRemoved ? *firstRemoved : *entry;
     493                 :             }
     494                 : 
     495       232510634 :             if (entry->matchHash(keyHash) && match(*entry, l)) {
     496        48885900 :                 METER(stats.hits++);
     497        48885900 :                 return *entry;
     498                 :             }
     499                 :         }
     500                 :     }
     501                 : 
     502                 :     /*
     503                 :      * This is a copy of lookup hardcoded to the assumptions:
     504                 :      *   1. the lookup is a lookupForAdd
     505                 :      *   2. the key, whose |keyHash| has been passed is not in the table,
     506                 :      *   3. no entries have been removed from the table.
     507                 :      * This specialized search avoids the need for recovering lookup values
     508                 :      * from entries, which allows more flexible Lookup/Key types.
     509                 :      */
     510       144414979 :     Entry &findFreeEntry(HashNumber keyHash)
     511                 :     {
     512       144414979 :         METER(stats.searches++);
     513       144414979 :         JS_ASSERT(!(keyHash & sCollisionBit));
     514                 : 
     515                 :         /* N.B. the |keyHash| has already been distributed. */
     516                 : 
     517                 :         /* Compute the primary hash address. */
     518       144414979 :         HashNumber h1 = hash1(keyHash, hashShift);
     519       144414979 :         Entry *entry = &table[h1];
     520                 : 
     521                 :         /* Miss: return space for a new entry. */
     522       144414979 :         if (entry->isFree()) {
     523       123443911 :             METER(stats.misses++);
     524       123443911 :             return *entry;
     525                 :         }
     526                 : 
     527                 :         /* Collision: double hash. */
     528        20971068 :         unsigned sizeLog2 = sHashBits - hashShift;
     529        20971068 :         HashNumber h2 = hash2(keyHash, sizeLog2, hashShift);
     530        20971068 :         HashNumber sizeMask = (HashNumber(1) << sizeLog2) - 1;
     531                 : 
     532         9089767 :         while(true) {
     533        30060835 :             JS_ASSERT(!entry->isRemoved());
     534        30060835 :             entry->setCollision();
     535                 : 
     536        30060835 :             METER(stats.steps++);
     537        30060835 :             h1 -= h2;
     538        30060835 :             h1 &= sizeMask;
     539                 : 
     540        30060835 :             entry = &table[h1];
     541        30060835 :             if (entry->isFree()) {
     542        20971068 :                 METER(stats.misses++);
     543        20971068 :                 return *entry;
     544                 :             }
     545                 :         }
     546                 :     }
     547                 : 
     548         1159479 :     bool changeTableSize(int deltaLog2)
     549                 :     {
     550                 :         /* Look, but don't touch, until we succeed in getting new entry store. */
     551         1159479 :         Entry *oldTable = table;
     552         1159479 :         uint32_t oldCap = capacity();
     553         1159479 :         uint32_t newLog2 = sHashBits - hashShift + deltaLog2;
     554         1159479 :         uint32_t newCapacity = JS_BIT(newLog2);
     555         1159479 :         if (newCapacity > sMaxCapacity) {
     556               0 :             this->reportAllocOverflow();
     557               0 :             return false;
     558                 :         }
     559                 : 
     560         1159479 :         Entry *newTable = createTable(*this, newCapacity);
     561         1159479 :         if (!newTable)
     562               0 :             return false;
     563                 : 
     564                 :         /* We can't fail from here on, so update table parameters. */
     565         1159479 :         setTableSizeLog2(newLog2);
     566         1159479 :         removedCount = 0;
     567         1159479 :         gen++;
     568         1159479 :         table = newTable;
     569                 : 
     570                 :         /* Copy only live entries, leaving removed ones behind. */
     571       291210031 :         for (Entry *src = oldTable, *end = src + oldCap; src < end; ++src) {
     572       290050552 :             if (src->isLive()) {
     573       143607921 :                 src->unsetCollision();
     574       143607921 :                 findFreeEntry(src->getKeyHash()) = Move(*src);
     575                 :             }
     576                 :         }
     577                 : 
     578         1159479 :         destroyTable(*this, oldTable, oldCap);
     579         1159479 :         return true;
     580                 :     }
     581                 : 
     582        29136946 :     void remove(Entry &e)
     583                 :     {
     584        29136946 :         METER(stats.removes++);
     585        29136946 :         if (e.hasCollision()) {
     586         8041974 :             e.setRemoved();
     587         8041974 :             removedCount++;
     588                 :         } else {
     589        21094972 :             METER(stats.removeFrees++);
     590        21094972 :             e.setFree();
     591                 :         }
     592        29136946 :         entryCount--;
     593                 : #ifdef DEBUG
     594        29136946 :         mutationCount++;
     595                 : #endif
     596        29136946 :     }
     597                 : 
     598         2275117 :     void checkUnderloaded()
     599                 :     {
     600         2275117 :         if (underloaded()) {
     601          352421 :             METER(stats.shrinks++);
     602          352421 :             (void) changeTableSize(-1);
     603                 :         }
     604         2275117 :     }
     605                 : 
     606                 :   public:
     607          132537 :     void clear()
     608                 :     {
     609                 :         if (tl::IsPodType<Entry>::result) {
     610           91686 :             memset(table, 0, sizeof(*table) * capacity());
     611                 :         } else {
     612           40851 :             uint32_t tableCapacity = capacity();
     613         5000347 :             for (Entry *e = table, *end = table + tableCapacity; e < end; ++e)
     614         4959496 :                 *e = Move(Entry());
     615                 :         }
     616          132537 :         removedCount = 0;
     617          132537 :         entryCount = 0;
     618                 : #ifdef DEBUG
     619          132537 :         mutationCount++;
     620                 : #endif
     621          132537 :     }
     622                 : 
     623           27005 :     void finish()
     624                 :     {
     625           27005 :         JS_ASSERT(!entered);
     626                 : 
     627           27005 :         if (!table)
     628               0 :             return;
     629                 :         
     630           27005 :         destroyTable(*this, table, capacity());
     631           27005 :         table = NULL;
     632           27005 :         gen++;
     633           27005 :         entryCount = 0;
     634           27005 :         removedCount = 0;
     635                 : #ifdef DEBUG
     636           27005 :         mutationCount++;
     637                 : #endif
     638                 :     }
     639                 : 
     640         1621016 :     Range all() const {
     641         1621016 :         return Range(table, table + capacity());
     642                 :     }
     643                 : 
     644        34210721 :     bool empty() const {
     645        34210721 :         return !entryCount;
     646                 :     }
     647                 : 
     648        14046173 :     uint32_t count() const {
     649        14046173 :         return entryCount;
     650                 :     }
     651                 : 
     652       139150096 :     uint32_t capacity() const {
     653       139150096 :         return JS_BIT(sHashBits - hashShift);
     654                 :     }
     655                 : 
     656         7055108 :     uint32_t generation() const {
     657         7055108 :         return gen;
     658                 :     }
     659                 : 
     660            1254 :     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
     661            1254 :         return mallocSizeOf(table);
     662                 :     }
     663                 : 
     664                 :     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
     665                 :         return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
     666                 :     }
     667                 : 
     668       254004195 :     Ptr lookup(const Lookup &l) const {
     669       508008390 :         ReentrancyGuard g(*this);
     670       254004195 :         HashNumber keyHash = prepareHash(l);
     671       254004195 :         return Ptr(lookup(l, keyHash, 0));
     672                 :     }
     673                 : 
     674       261548343 :     AddPtr lookupForAdd(const Lookup &l) const {
     675       523096686 :         ReentrancyGuard g(*this);
     676       261548343 :         HashNumber keyHash = prepareHash(l);
     677       261548343 :         Entry &entry = lookup(l, keyHash, sCollisionBit);
     678                 : #ifdef DEBUG
     679       261548343 :         return AddPtr(entry, keyHash, mutationCount);
     680                 : #else
     681                 :         return AddPtr(entry, keyHash);
     682                 : #endif
     683                 :     }
     684                 : 
     685       128758205 :     bool add(AddPtr &p)
     686                 :     {
     687       257516410 :         ReentrancyGuard g(*this);
     688       128758205 :         JS_ASSERT(mutationCount == p.mutationCount);
     689       128758205 :         JS_ASSERT(table);
     690       128758205 :         JS_ASSERT(!p.found());
     691       128758205 :         JS_ASSERT(!(p.keyHash & sCollisionBit));
     692                 : 
     693                 :         /*
     694                 :          * Changing an entry from removed to live does not affect whether we
     695                 :          * are overloaded and can be handled separately.
     696                 :          */
     697       128758205 :         if (p.entry->isRemoved()) {
     698          498482 :             METER(stats.addOverRemoved++);
     699          498482 :             removedCount--;
     700          498482 :             p.keyHash |= sCollisionBit;
     701                 :         } else {
     702                 :             /* If alpha is >= .75, grow or compress the table. */
     703       128259723 :             if (overloaded()) {
     704                 :                 /* Compress if a quarter or more of all entries are removed. */
     705                 :                 int deltaLog2;
     706          807058 :                 if (removedCount >= (capacity() >> 2)) {
     707             509 :                     METER(stats.compresses++);
     708             509 :                     deltaLog2 = 0;
     709                 :                 } else {
     710          806549 :                     METER(stats.grows++);
     711          806549 :                     deltaLog2 = 1;
     712                 :                 }
     713                 : 
     714          807058 :                 if (!changeTableSize(deltaLog2))
     715               0 :                     return false;
     716                 : 
     717                 :                 /* Preserve the validity of |p.entry|. */
     718          807058 :                 p.entry = &findFreeEntry(p.keyHash);
     719                 :             }
     720                 :         }
     721                 : 
     722       128758205 :         p.entry->setLive(p.keyHash);
     723       128758205 :         entryCount++;
     724                 : #ifdef DEBUG
     725       128758205 :         mutationCount++;
     726                 : #endif
     727       128758205 :         return true;
     728                 :     }
     729                 : 
     730                 :     /*
     731                 :      * There is an important contract between the caller and callee for this
     732                 :      * function: if add() returns true, the caller must assign the T value
     733                 :      * which produced p before using the hashtable again.
     734                 :      */
     735        26734986 :     bool add(AddPtr &p, T** pentry)
     736                 :     {
     737        26734986 :         if (!add(p))
     738               0 :             return false;
     739        26734986 :         *pentry = &p.entry->t;
     740        26734986 :         return true;
     741                 :     }
     742                 : 
     743       102023219 :     bool add(AddPtr &p, const T &t)
     744                 :     {
     745       102023219 :         if (!add(p))
     746               0 :             return false;
     747       102023219 :         p.entry->t = t;
     748       102023219 :         return true;
     749                 :     }
     750                 : 
     751        30602736 :     bool relookupOrAdd(AddPtr& p, const Lookup &l, const T& t)
     752                 :     {
     753                 : #ifdef DEBUG
     754        30602736 :         p.mutationCount = mutationCount;
     755                 : #endif
     756                 :         {
     757        61205472 :             ReentrancyGuard g(*this);
     758        30602736 :             p.entry = &lookup(l, p.keyHash, sCollisionBit);
     759                 :         }
     760        30602736 :         return p.found() || add(p, t);
     761                 :     }
     762                 : 
     763         2058644 :     void remove(Ptr p)
     764                 :     {
     765         4117288 :         ReentrancyGuard g(*this);
     766         2058644 :         JS_ASSERT(p.found());
     767         2058644 :         remove(*p.entry);
     768         2058644 :         checkUnderloaded();
     769         2058644 :     }
     770                 : #undef METER
     771                 : };
     772                 : 
     773                 : }  /* namespace detail */
     774                 : 
     775                 : /*****************************************************************************/
     776                 : 
     777                 : /*
     778                 :  * Hash policy
     779                 :  *
     780                 :  * A hash policy P for a hash table with key-type Key must provide:
     781                 :  *  - a type |P::Lookup| to use to lookup table entries;
     782                 :  *  - a static member function |P::hash| with signature
     783                 :  *
     784                 :  *      static js::HashNumber hash(Lookup)
     785                 :  *
     786                 :  *    to use to hash the lookup type; and
     787                 :  *  - a static member function |P::match| with signature
     788                 :  *
     789                 :  *      static bool match(Key, Lookup)
     790                 :  *
     791                 :  *    to use to test equality of key and lookup values.
     792                 :  *
     793                 :  * Normally, Lookup = Key. In general, though, different values and types of
     794                 :  * values can be used to lookup and store. If a Lookup value |l| is != to the
     795                 :  * added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.:
     796                 :  *
     797                 :  *   js::HashSet<Key, P>::AddPtr p = h.lookup(l);
     798                 :  *   if (!p) {
     799                 :  *     assert(P::match(k, l));  // must hold
     800                 :  *     h.add(p, k);
     801                 :  *   }
     802                 :  */
     803                 : 
     804                 : /* Default hashing policies. */
     805                 : template <class Key>
     806                 : struct DefaultHasher
     807                 : {
     808                 :     typedef Key Lookup;
     809                 :     static HashNumber hash(const Lookup &l) {
     810                 :         /* Hash if can implicitly cast to hash number type. */
     811                 :         return l;
     812                 :     }
     813                 :     static bool match(const Key &k, const Lookup &l) {
     814                 :         /* Use builtin or overloaded operator==. */
     815                 :         return k == l;
     816                 :     }
     817                 : };
     818                 : 
     819                 : /*
     820                 :  * Pointer hashing policy that strips the lowest zeroBits when calculating the
     821                 :  * hash to improve key distribution.
     822                 :  */
     823                 : template <typename Key, size_t zeroBits>
     824                 : struct PointerHasher
     825                 : {
     826                 :     typedef Key Lookup;
     827        84797681 :     static HashNumber hash(const Lookup &l) {
     828        84797681 :         size_t word = reinterpret_cast<size_t>(l) >> zeroBits;
     829                 :         JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
     830                 : #if JS_BYTES_PER_WORD == 4
     831        84797681 :         return HashNumber(word);
     832                 : #else
     833                 :         JS_STATIC_ASSERT(sizeof word == 8);
     834                 :         return HashNumber((word >> 32) ^ word);
     835                 : #endif
     836                 :     }
     837        22884365 :     static bool match(const Key &k, const Lookup &l) {
     838        22884365 :         return k == l;
     839                 :     }
     840                 : };
     841                 : 
     842                 : template <typename Key, size_t zeroBits>
     843                 : struct TaggedPointerHasher
     844                 : {
     845                 :     typedef Key Lookup;
     846                 : 
     847                 :     static HashNumber hash(const Lookup &l) {
     848                 :         return PointerHasher<Key, zeroBits>::hash(l);
     849                 :     }
     850                 : 
     851                 :     static const uintptr_t COMPARE_MASK = uintptr_t(-1) - 1;
     852                 : 
     853                 :     static bool match(const Key &k, const Lookup &l) {
     854                 :         return (uintptr_t(k) & COMPARE_MASK) == uintptr_t(l);
     855                 :     }
     856                 : };
     857                 : 
     858                 : /*
     859                 :  * Specialized hashing policy for pointer types. It assumes that the type is
     860                 :  * at least word-aligned. For types with smaller size use PointerHasher.
     861                 :  */
     862                 : template <class T>
     863                 : struct DefaultHasher<T *>: PointerHasher<T *, tl::FloorLog2<sizeof(void *)>::result> { };
     864                 : 
     865                 : /* Looking for a hasher for jsid?  Try the DefaultHasher<jsid> in jsatom.h. */
     866                 : 
     867                 : template <class Key, class Value>
     868                 : class HashMapEntry
     869        20771116 : {
     870                 :     template <class, class, class> friend class detail::HashTable;
     871                 :     template <class> friend class detail::HashTableEntry;
     872         1041747 :     void operator=(const HashMapEntry &rhs) {
     873         1041747 :         const_cast<Key &>(key) = rhs.key;
     874         1041747 :         value = rhs.value;
     875         1041747 :     }
     876                 : 
     877                 :   public:
     878       160611976 :     HashMapEntry() : key(), value() {}
     879                 : 
     880                 :     template<typename KeyInput, typename ValueInput>
     881           73283 :     HashMapEntry(const KeyInput &k, const ValueInput &v) : key(k), value(v) {}
     882                 : 
     883                 :     HashMapEntry(MoveRef<HashMapEntry> rhs) 
     884                 :       : key(Move(rhs->key)), value(Move(rhs->value)) { }
     885        36007820 :     void operator=(MoveRef<HashMapEntry> rhs) {
     886        36007820 :         const_cast<Key &>(key) = Move(rhs->key);
     887        36007820 :         value = Move(rhs->value);
     888        36007820 :     }
     889                 : 
     890                 :     const Key key;
     891                 :     Value value;
     892                 : };
     893                 : 
     894                 : namespace tl {
     895                 : 
     896                 : template <class T>
     897                 : struct IsPodType<detail::HashTableEntry<T> > {
     898                 :     static const bool result = IsPodType<T>::result;
     899                 : };
     900                 : 
     901                 : template <class K, class V>
     902                 : struct IsPodType<HashMapEntry<K, V> >
     903                 : {
     904                 :     static const bool result = IsPodType<K>::result && IsPodType<V>::result;
     905                 : };
     906                 : 
     907                 : } /* namespace tl */
     908                 : 
     909                 : /*
     910                 :  * JS-friendly, STL-like container providing a hash-based map from keys to
     911                 :  * values. In particular, HashMap calls constructors and destructors of all
     912                 :  * objects added so non-PODs may be used safely.
     913                 :  *
     914                 :  * Key/Value requirements:
     915                 :  *  - default constructible, copyable, destructible, assignable
     916                 :  * HashPolicy requirements:
     917                 :  *  - see "Hash policy" above (default js::DefaultHasher<Key>)
     918                 :  * AllocPolicy:
     919                 :  *  - see "Allocation policies" in jsalloc.h
     920                 :  *
     921                 :  * N.B: HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members
     922                 :  *      called by HashMap must not call back into the same HashMap object.
     923                 :  * N.B: Due to the lack of exception handling, the user must call |init()|.
     924                 :  */
     925                 : template <class Key,
     926                 :           class Value,
     927                 :           class HashPolicy = DefaultHasher<Key>,
     928                 :           class AllocPolicy = TempAllocPolicy>
     929                 : class HashMap
     930         2308178 : {
     931                 :   public:
     932                 :     typedef typename HashPolicy::Lookup Lookup;
     933                 : 
     934                 :     typedef HashMapEntry<Key, Value> Entry;
     935                 : 
     936                 :   private:
     937                 :     /* Implement HashMap using HashTable. Lift |Key| operations to |Entry|. */
     938                 :     struct MapHashPolicy : HashPolicy
     939                 :     {
     940                 :         typedef Key KeyType;
     941        24439222 :         static const Key &getKey(Entry &e) { return e.key; }
     942                 :     };
     943                 :     typedef detail::HashTable<Entry, MapHashPolicy, AllocPolicy> Impl;
     944                 : 
     945                 :     friend class Impl::Enum;
     946                 : 
     947                 :     /* Not implicitly copyable (expensive). May add explicit |clone| later. */
     948                 :     HashMap(const HashMap &);
     949                 :     HashMap &operator=(const HashMap &);
     950                 : 
     951                 :     Impl impl;
     952                 : 
     953                 :   public:
     954                 :     /*
     955                 :      * HashMap construction is fallible (due to OOM); thus the user must call
     956                 :      * init after constructing a HashMap and check the return value.
     957                 :      */
     958         2308197 :     HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {}
     959         1766650 :     bool init(uint32_t len = Impl::sDefaultInitSize)  { return impl.init(len); }
     960         7306773 :     bool initialized() const                          { return impl.initialized(); }
     961                 : 
     962                 :     /*
     963                 :      * Return whether the given lookup value is present in the map. E.g.:
     964                 :      *
     965                 :      *   typedef HashMap<int,char> HM;
     966                 :      *   HM h;
     967                 :      *   if (HM::Ptr p = h.lookup(3)) {
     968                 :      *     const HM::Entry &e = *p; // p acts like a pointer to Entry
     969                 :      *     assert(p->key == 3);     // Entry contains the key
     970                 :      *     char val = p->value;     // and value
     971                 :      *   }
     972                 :      *
     973                 :      * Also see the definition of Ptr in HashTable above (with T = Entry).
     974                 :      */
     975                 :     typedef typename Impl::Ptr Ptr;
     976        18757702 :     Ptr lookup(const Lookup &l) const                 { return impl.lookup(l); }
     977                 : 
     978                 :     /* Assuming |p.found()|, remove |*p|. */
     979          841325 :     void remove(Ptr p)                                { impl.remove(p); }
     980                 : 
     981                 :     /*
     982                 :      * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
     983                 :      * insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using
     984                 :      * |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.:
     985                 :      *
     986                 :      *   typedef HashMap<int,char> HM;
     987                 :      *   HM h;
     988                 :      *   HM::AddPtr p = h.lookupForAdd(3);
     989                 :      *   if (!p) {
     990                 :      *     if (!h.add(p, 3, 'a'))
     991                 :      *       return false;
     992                 :      *   }
     993                 :      *   const HM::Entry &e = *p;   // p acts like a pointer to Entry
     994                 :      *   assert(p->key == 3);       // Entry contains the key
     995                 :      *   char val = p->value;       // and value
     996                 :      *
     997                 :      * Also see the definition of AddPtr in HashTable above (with T = Entry).
     998                 :      *
     999                 :      * N.B. The caller must ensure that no mutating hash table operations
    1000                 :      * occur between a pair of |lookupForAdd| and |add| calls. To avoid
    1001                 :      * looking up the key a second time, the caller may use the more efficient
    1002                 :      * relookupOrAdd method. This method reuses part of the hashing computation
    1003                 :      * to more efficiently insert the key if it has not been added. For
    1004                 :      * example, a mutation-handling version of the previous example:
    1005                 :      *
    1006                 :      *    HM::AddPtr p = h.lookupForAdd(3);
    1007                 :      *    if (!p) {
    1008                 :      *      call_that_may_mutate_h();
    1009                 :      *      if (!h.relookupOrAdd(p, 3, 'a'))
    1010                 :      *        return false;
    1011                 :      *    }
    1012                 :      *    const HM::Entry &e = *p;
    1013                 :      *    assert(p->key == 3);
    1014                 :      *    char val = p->value;
    1015                 :      */
    1016                 :     typedef typename Impl::AddPtr AddPtr;
    1017        40202329 :     AddPtr lookupForAdd(const Lookup &l) const {
    1018        40202329 :         return impl.lookupForAdd(l);
    1019                 :     }
    1020                 : 
    1021                 :     template<typename KeyInput, typename ValueInput>
    1022        23391882 :     bool add(AddPtr &p, const KeyInput &k, const ValueInput &v) {
    1023                 :         Entry *pentry;
    1024        23391882 :         if (!impl.add(p, &pentry))
    1025               0 :             return false;
    1026        23391882 :         const_cast<Key &>(pentry->key) = k;
    1027        23391882 :         pentry->value = v;
    1028        23391882 :         return true;
    1029                 :     }
    1030                 : 
    1031         3343104 :     bool add(AddPtr &p, const Key &k, MoveRef<Value> v) {
    1032                 :         Entry *pentry;
    1033         3343104 :         if (!impl.add(p, &pentry))
    1034               0 :             return false;
    1035         3343104 :         const_cast<Key &>(pentry->key) = k;
    1036         3343104 :         pentry->value = v;
    1037         3343104 :         return true;
    1038                 :     }
    1039                 : 
    1040                 :     bool add(AddPtr &p, const Key &k) {
    1041                 :         Entry *pentry;
    1042                 :         if (!impl.add(p, &pentry))
    1043                 :             return false;
    1044                 :         const_cast<Key &>(pentry->key) = k;
    1045                 :         return true;
    1046                 :     }
    1047                 : 
    1048                 :     template<typename KeyInput, typename ValueInput>
    1049           73283 :     bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) {
    1050           73283 :         return impl.relookupOrAdd(p, k, Entry(k, v));
    1051                 :     }
    1052                 : 
    1053                 :     /*
    1054                 :      * |all()| returns a Range containing |count()| elements. E.g.:
    1055                 :      *
    1056                 :      *   typedef HashMap<int,char> HM;
    1057                 :      *   HM h;
    1058                 :      *   for (HM::Range r = h.all(); !r.empty(); r.popFront())
    1059                 :      *     char c = r.front().value;
    1060                 :      *
    1061                 :      * Also see the definition of Range in HashTable above (with T = Entry).
    1062                 :      */
    1063                 :     typedef typename Impl::Range Range;
    1064          523816 :     Range all() const                                 { return impl.all(); }
    1065        12139730 :     uint32_t count() const                            { return impl.count(); }
    1066                 :     size_t capacity() const                           { return impl.capacity(); }
    1067                 :     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
    1068                 :         return impl.sizeOfExcludingThis(mallocSizeOf);
    1069                 :     }
    1070               0 :     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
    1071                 :         /* 
    1072                 :          * Don't just call |impl.sizeOfExcludingThis()| because there's no
    1073                 :          * guarantee that |impl| is the first field in HashMap.
    1074                 :          */
    1075               0 :         return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
    1076                 :     }
    1077                 : 
    1078                 :     /*
    1079                 :      * Typedef for the enumeration class. An Enum may be used to examine and
    1080                 :      * remove table entries:
    1081                 :      *
    1082                 :      *   typedef HashMap<int,char> HM;
    1083                 :      *   HM s;
    1084                 :      *   for (HM::Enum e(s); !e.empty(); e.popFront())
    1085                 :      *     if (e.front().value == 'l')
    1086                 :      *       e.removeFront();
    1087                 :      *
    1088                 :      * Table resize may occur in Enum's destructor. Also see the definition of
    1089                 :      * Enum in HashTable above (with T = Entry).
    1090                 :      */
    1091                 :     typedef typename Impl::Enum Enum;
    1092                 : 
    1093                 :     /*
    1094                 :      * Remove all entries. This does not shrink the table. For that consider
    1095                 :      * using the finish() method.
    1096                 :      */
    1097          112629 :     void clear()                                      { impl.clear(); }
    1098                 : 
    1099                 :     /*
    1100                 :      * Remove all the entries and release all internal buffers. The map must
    1101                 :      * be initialized again before any use.
    1102                 :      */
    1103           27005 :     void finish()                                     { impl.finish(); }
    1104                 : 
    1105                 :    /* Does the table contain any entries? */
    1106           70736 :     bool empty() const                                { return impl.empty(); }
    1107                 : 
    1108                 :     /*
    1109                 :      * If |generation()| is the same before and after a HashMap operation,
    1110                 :      * pointers into the table remain valid.
    1111                 :      */
    1112         6693774 :     unsigned generation() const                       { return impl.generation(); }
    1113                 : 
    1114                 :     /* Shorthand operations: */
    1115                 : 
    1116            2548 :     bool has(const Lookup &l) const {
    1117            2548 :         return impl.lookup(l) != NULL;
    1118                 :     }
    1119                 : 
    1120                 :     /* Overwrite existing value with v. Return NULL on oom. */
    1121                 :     template<typename KeyInput, typename ValueInput>
    1122         2677218 :     Entry *put(const KeyInput &k, const ValueInput &v) {
    1123         2677218 :         AddPtr p = lookupForAdd(k);
    1124         2677218 :         if (p) {
    1125             577 :             p->value = v;
    1126             577 :             return &*p;
    1127                 :         }
    1128         2676641 :         return add(p, k, v) ? &*p : NULL;
    1129                 :     }
    1130                 : 
    1131                 :     /* Like put, but assert that the given key is not already present. */
    1132         2273690 :     bool putNew(const Key &k, const Value &v) {
    1133         2273690 :         AddPtr p = lookupForAdd(k);
    1134         2273690 :         JS_ASSERT(!p);
    1135         2273690 :         return add(p, k, v);
    1136                 :     }
    1137                 : 
    1138                 :     /* Add (k,defaultValue) if k no found. Return false-y Ptr on oom. */
    1139               0 :     Ptr lookupWithDefault(const Key &k, const Value &defaultValue) {
    1140               0 :         AddPtr p = lookupForAdd(k);
    1141               0 :         if (p)
    1142               0 :             return p;
    1143               0 :         (void)add(p, k, defaultValue);  /* p is left false-y on oom. */
    1144               0 :         return p;
    1145                 :     }
    1146                 : 
    1147                 :     /* Remove if present. */
    1148          493431 :     void remove(const Lookup &l) {
    1149          493431 :         if (Ptr p = lookup(l))
    1150          493431 :             remove(p);
    1151          493431 :     }
    1152                 : };
    1153                 : 
    1154                 : /*
    1155                 :  * JS-friendly, STL-like container providing a hash-based set of values. In
    1156                 :  * particular, HashSet calls constructors and destructors of all objects added
    1157                 :  * so non-PODs may be used safely.
    1158                 :  *
    1159                 :  * T requirements:
    1160                 :  *  - default constructible, copyable, destructible, assignable
    1161                 :  * HashPolicy requirements:
    1162                 :  *  - see "Hash policy" above (default js::DefaultHasher<Key>)
    1163                 :  * AllocPolicy:
    1164                 :  *  - see "Allocation policies" in jsalloc.h
    1165                 :  *
    1166                 :  * N.B: HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by
    1167                 :  *      HashSet must not call back into the same HashSet object.
    1168                 :  * N.B: Due to the lack of exception handling, the user must call |init()|.
    1169                 :  */
    1170                 : template <class T, class HashPolicy = DefaultHasher<T>, class AllocPolicy = TempAllocPolicy>
    1171                 : class HashSet
    1172         3221459 : {
    1173                 :     typedef typename HashPolicy::Lookup Lookup;
    1174                 : 
    1175                 :     /* Implement HashSet in terms of HashTable. */
    1176                 :     struct SetOps : HashPolicy {
    1177                 :         typedef T KeyType;
    1178       148732891 :         static const KeyType &getKey(const T &t) { return t; }
    1179                 :     };
    1180                 :     typedef detail::HashTable<const T, SetOps, AllocPolicy> Impl;
    1181                 : 
    1182                 :     friend class Impl::Enum;
    1183                 : 
    1184                 :     /* Not implicitly copyable (expensive). May add explicit |clone| later. */
    1185                 :     HashSet(const HashSet &);
    1186                 :     HashSet &operator=(const HashSet &);
    1187                 : 
    1188                 :     Impl impl;
    1189                 : 
    1190                 :   public:
    1191                 :     /*
    1192                 :      * HashSet construction is fallible (due to OOM); thus the user must call
    1193                 :      * init after constructing a HashSet and check the return value.
    1194                 :      */
    1195         3221623 :     HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {}
    1196         3128686 :     bool init(uint32_t len = Impl::sDefaultInitSize)  { return impl.init(len); }
    1197        74531199 :     bool initialized() const                          { return impl.initialized(); }
    1198                 : 
    1199                 :     /*
    1200                 :      * Return whether the given lookup value is present in the map. E.g.:
    1201                 :      *
    1202                 :      *   typedef HashSet<int> HS;
    1203                 :      *   HS h;
    1204                 :      *   if (HS::Ptr p = h.lookup(3)) {
    1205                 :      *     assert(*p == 3);   // p acts like a pointer to int
    1206                 :      *   }
    1207                 :      *
    1208                 :      * Also see the definition of Ptr in HashTable above.
    1209                 :      */
    1210                 :     typedef typename Impl::Ptr Ptr;
    1211        19553866 :     Ptr lookup(const Lookup &l) const                 { return impl.lookup(l); }
    1212                 : 
    1213                 :     /* Assuming |p.found()|, remove |*p|. */
    1214         1217319 :     void remove(Ptr p)                                { impl.remove(p); }
    1215                 : 
    1216                 :     /*
    1217                 :      * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
    1218                 :      * insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using
    1219                 :      * |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.:
    1220                 :      *
    1221                 :      *   typedef HashSet<int> HS;
    1222                 :      *   HS h;
    1223                 :      *   HS::AddPtr p = h.lookupForAdd(3);
    1224                 :      *   if (!p) {
    1225                 :      *     if (!h.add(p, 3))
    1226                 :      *       return false;
    1227                 :      *   }
    1228                 :      *   assert(*p == 3);   // p acts like a pointer to int
    1229                 :      *
    1230                 :      * Also see the definition of AddPtr in HashTable above.
    1231                 :      *
    1232                 :      * N.B. The caller must ensure that no mutating hash table operations
    1233                 :      * occur between a pair of |lookupForAdd| and |add| calls. To avoid
    1234                 :      * looking up the key a second time, the caller may use the more efficient
    1235                 :      * relookupOrAdd method. This method reuses part of the hashing computation
    1236                 :      * to more efficiently insert the key if it has not been added. For
    1237                 :      * example, a mutation-handling version of the previous example:
    1238                 :      *
    1239                 :      *    HS::AddPtr p = h.lookupForAdd(3);
    1240                 :      *    if (!p) {
    1241                 :      *      call_that_may_mutate_h();
    1242                 :      *      if (!h.relookupOrAdd(p, 3, 3))
    1243                 :      *        return false;
    1244                 :      *    }
    1245                 :      *    assert(*p == 3);
    1246                 :      *
    1247                 :      * Note that relookupOrAdd(p,l,t) performs Lookup using l and adds the
    1248                 :      * entry t, where the caller ensures match(l,t).
    1249                 :      */
    1250                 :     typedef typename Impl::AddPtr AddPtr;
    1251       221346014 :     AddPtr lookupForAdd(const Lookup &l) const {
    1252       221346014 :         return impl.lookupForAdd(l);
    1253                 :     }
    1254                 : 
    1255        71420483 :     bool add(AddPtr &p, const T &t) {
    1256        71420483 :         return impl.add(p, t);
    1257                 :     }
    1258                 : 
    1259        30529453 :     bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) {
    1260        30529453 :         return impl.relookupOrAdd(p, l, t);
    1261                 :     }
    1262                 : 
    1263                 :     /*
    1264                 :      * |all()| returns a Range containing |count()| elements:
    1265                 :      *
    1266                 :      *   typedef HashSet<int> HS;
    1267                 :      *   HS h;
    1268                 :      *   for (HS::Range r = h.all(); !r.empty(); r.popFront())
    1269                 :      *     int i = r.front();
    1270                 :      *
    1271                 :      * Also see the definition of Range in HashTable above.
    1272                 :      */
    1273                 :     typedef typename Impl::Range Range;
    1274         1097200 :     Range all() const                                 { return impl.all(); }
    1275         1906443 :     uint32_t count() const                            { return impl.count(); }
    1276                 :     size_t capacity() const                           { return impl.capacity(); }
    1277              48 :     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
    1278              48 :         return impl.sizeOfExcludingThis(mallocSizeOf);
    1279                 :     }
    1280            1206 :     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
    1281                 :         /* 
    1282                 :          * Don't just call |impl.sizeOfExcludingThis()| because there's no
    1283                 :          * guarantee that |impl| is the first field in HashSet.
    1284                 :          */
    1285            1206 :         return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
    1286                 :     }
    1287                 : 
    1288                 :     /*
    1289                 :      * Typedef for the enumeration class. An Enum may be used to examine and
    1290                 :      * remove table entries:
    1291                 :      *
    1292                 :      *   typedef HashSet<int> HS;
    1293                 :      *   HS s;
    1294                 :      *   for (HS::Enum e(s); !e.empty(); e.popFront())
    1295                 :      *     if (e.front() == 42)
    1296                 :      *       e.removeFront();
    1297                 :      *
    1298                 :      * Table resize may occur in Enum's destructor. Also see the definition of
    1299                 :      * Enum in HashTable above.
    1300                 :      */
    1301                 :     typedef typename Impl::Enum Enum;
    1302                 : 
    1303                 :     /*
    1304                 :      * Remove all entries. This does not shrink the table. For that consider
    1305                 :      * using the finish() method.
    1306                 :      */
    1307           19908 :     void clear()                                      { impl.clear(); }
    1308                 : 
    1309                 :     /*
    1310                 :      * Remove all the entries and release all internal buffers. The set must
    1311                 :      * be initialized again before any use.
    1312                 :      */
    1313               0 :     void finish()                                     { impl.finish(); }
    1314                 : 
    1315                 :     /* Does the table contain any entries? */
    1316        34139985 :     bool empty() const                                { return impl.empty(); }
    1317                 : 
    1318                 :     /*
    1319                 :      * If |generation()| is the same before and after a HashSet operation,
    1320                 :      * pointers into the table remain valid.
    1321                 :      */
    1322          361334 :     unsigned generation() const                       { return impl.generation(); }
    1323                 : 
    1324                 :     /* Shorthand operations: */
    1325                 : 
    1326       215690079 :     bool has(const Lookup &l) const {
    1327       215690079 :         return impl.lookup(l) != NULL;
    1328                 :     }
    1329                 : 
    1330                 :     /* Overwrite existing value with v. Return NULL on oom. */
    1331          620963 :     const T *put(const T &t) {
    1332          620963 :         AddPtr p = lookupForAdd(t);
    1333          620963 :         return p ? &*p : (add(p, t) ? &*p : NULL);
    1334                 :     }
    1335                 : 
    1336                 :     /* Like put, but assert that the given key is not already present. */
    1337                 :     bool putNew(const T &t) {
    1338                 :         AddPtr p = lookupForAdd(t);
    1339                 :         JS_ASSERT(!p);
    1340                 :         return add(p, t);
    1341                 :     }
    1342                 : 
    1343         3331435 :     bool putNew(const Lookup &l, const T &t) {
    1344         3331435 :         AddPtr p = lookupForAdd(l);
    1345         3331435 :         JS_ASSERT(!p);
    1346         3331435 :         return add(p, t);
    1347                 :     }
    1348                 : 
    1349         1017233 :     void remove(const Lookup &l) {
    1350         1017233 :         if (Ptr p = lookup(l))
    1351         1017233 :             remove(p);
    1352         1017233 :     }
    1353                 : };
    1354                 : 
    1355                 : }  /* namespace js */
    1356                 : 
    1357                 : #endif

Generated by: LCOV version 1.7