LCOV - code coverage report
Current view: directory - objdir/dist/include/js - HashTable.h (source / functions) Found Hit Coverage
Test: app.info Lines: 429 403 93.9 %
Date: 2012-04-07 Functions: 2975 2528 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       533808308 : 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     -1922557132 :     static bool isLiveHash(HashNumber hash)
      76                 :     {
      77     -1922557132 :         return hash > sRemovedKey;
      78                 :     }
      79                 : 
      80                 :   public:
      81       540800196 :     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       126752654 :     void operator=(MoveRef<HashTableEntry> rhs) { keyHash = rhs->keyHash; t = Move(rhs->t); }
      85                 : 
      86                 :     NonConstT t;
      87                 : 
      88       806717889 :     bool isFree() const           { return keyHash == sFreeKey; }
      89        12248994 :     void setFree()                { keyHash = sFreeKey; t = T(); }
      90       355932938 :     bool isRemoved() const        { return keyHash == sRemovedKey; }
      91         3999617 :     void setRemoved()             { keyHash = sRemovedKey; t = T(); }
      92      1422174543 :     bool isLive() const           { return isLiveHash(keyHash); }
      93       102395950 :     void setLive(HashNumber hn)   { JS_ASSERT(isLiveHash(hn)); keyHash = hn; }
      94                 : 
      95        22399004 :     void setCollision()           { JS_ASSERT(isLive()); keyHash |= sCollisionBit; }
      96       230454619 :     void setCollision(HashNumber collisionBit) {
      97       230454619 :         JS_ASSERT(isLive()); keyHash |= collisionBit;
      98       230454619 :     }
      99       119750190 :     void unsetCollision()         { keyHash &= ~sCollisionBit; }
     100       135998801 :     bool hasCollision() const     { JS_ASSERT(isLive()); return keyHash & sCollisionBit; }
     101       335262681 :     bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; }
     102       119750190 :     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       414634394 :         Ptr(Entry &entry) : entry(&entry) {}
     138                 : 
     139                 :       public:
     140                 :         /* Leaves Ptr uninitialized. */
     141         9487930 :         Ptr() {
     142                 : #ifdef DEBUG
     143         9487930 :             entry = (Entry *)0xbad;
     144                 : #endif
     145         9487930 :         }
     146                 : 
     147       532129659 :         bool found() const                    { return entry->isLive(); }
     148       408247616 :         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        45595323 :         T &operator*() const                  { return entry->t; }
     153        54646749 :         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       186703248 :     {
     159                 :         friend class HashTable;
     160                 :         HashNumber keyHash;
     161                 :         DebugOnly<uint64_t> mutationCount;
     162                 : 
     163       180578947 :         AddPtr(Entry &entry, HashNumber hn) : Ptr(entry), keyHash(hn) {}
     164                 :       public:
     165                 :         /* Leaves AddPtr uninitialized. */
     166         4790663 :         AddPtr() {}
     167                 :     };
     168                 : 
     169                 :     /*
     170                 :      * A collection of hash table entries. The collection is enumerated by
     171                 :      * calling |front()| followed by |popFront()| as long as |!empty()|. As
     172                 :      * with Ptr/AddPtr, Range objects must not be used after any mutating hash
     173                 :      * table operation unless the |generation()| is tested.
     174                 :      */
     175                 :     class Range
     176                 :     {
     177                 :       protected:
     178                 :         friend class HashTable;
     179                 : 
     180         1069429 :         Range(Entry *c, Entry *e) : cur(c), end(e) {
     181        56305919 :             while (cur < end && !cur->isLive())
     182        54167061 :                 ++cur;
     183         1069429 :         }
     184                 : 
     185                 :         Entry *cur, *end;
     186                 : 
     187                 :       public:
     188          127476 :         Range() : cur(NULL), end(NULL) {}
     189                 : 
     190       215289624 :         bool empty() const {
     191       215289624 :             return cur == end;
     192                 :         }
     193                 : 
     194        76553596 :         T &front() const {
     195        76553596 :             JS_ASSERT(!empty());
     196        76553596 :             return cur->t;
     197                 :         }
     198                 : 
     199        68839776 :         void popFront() {
     200        68839776 :             JS_ASSERT(!empty());
     201       296111544 :             while (++cur < end && !cur->isLive())
     202       158431992 :                 continue;
     203        68839776 :         }
     204                 :     };
     205                 : 
     206                 :     /*
     207                 :      * A Range whose lifetime delimits a mutating enumeration of a hash table.
     208                 :      * Since rehashing when elements were removed during enumeration would be
     209                 :      * bad, it is postponed until |endEnumeration()| is called. If
     210                 :      * |endEnumeration()| is not called before an Enum's constructor, it will
     211                 :      * be called automatically. Since |endEnumeration()| touches the hash
     212                 :      * table, the user must ensure that the hash table is still alive when this
     213                 :      * happens.
     214                 :      */
     215                 :     class Enum : public Range
     216                 :     {
     217                 :         friend class HashTable;
     218                 : 
     219                 :         HashTable &table;
     220                 :         bool added;
     221                 :         bool removed;
     222                 : 
     223                 :         /* Not copyable. */
     224                 :         Enum(const Enum &);
     225                 :         void operator=(const Enum &);
     226                 : 
     227                 :       public:
     228                 :         template<class Map> explicit
     229          601929 :         Enum(Map &map) : Range(map.all()), table(map.impl), added(false), removed(false) {}
     230                 : 
     231                 :         /*
     232                 :          * Removes the |front()| element from the table, leaving |front()|
     233                 :          * invalid until the next call to |popFront()|. For example:
     234                 :          *
     235                 :          *   HashSet<int> s;
     236                 :          *   for (HashSet<int>::Enum e(s); !e.empty(); e.popFront())
     237                 :          *     if (e.front() == 42)
     238                 :          *       e.removeFront();
     239                 :          */
     240        15387131 :         void removeFront() {
     241        15387131 :             table.remove(*this->cur);
     242        15387131 :             removed = true;
     243        15387131 :         }
     244                 : 
     245                 :         /*
     246                 :          * Removes the |front()| element and re-inserts it into the table with
     247                 :          * a new key at the new Lookup position.  |front()| is invalid after
     248                 :          * this operation until the next call to |popFront()|.
     249                 :          */
     250          299938 :         void rekeyFront(Key &k) {
     251          299938 :             JS_ASSERT(&k != &HashPolicy::getKey(this->cur->t));
     252          299938 :             JS_ASSERT(!table.match(*this->cur, k));
     253          299938 :             Entry e = *this->cur;
     254          299938 :             HashPolicy::setKey(e.t, k);
     255          299938 :             table.remove(*this->cur);
     256          299938 :             table.add(k, e);
     257          299938 :             added = true;
     258          299938 :         }
     259                 : 
     260                 :         /* Potentially rehashes the table. */
     261          601929 :         ~Enum() {
     262          601929 :             if (added)
     263              40 :                 table.checkOverloaded();
     264          601929 :             if (removed)
     265          164833 :                 table.checkUnderloaded();
     266          601929 :         }
     267                 : 
     268                 :         /* Can be used to end the enumeration before the destructor. */
     269                 :         void endEnumeration() {
     270                 :             if (added) {
     271                 :                 table.checkOverloaded();
     272                 :                 added = false;
     273                 :             }
     274                 :             if (removed) {
     275                 :                 table.checkUnderloaded();
     276                 :                 removed = false;
     277                 :             }
     278                 :         }
     279                 :     };
     280                 : 
     281                 :   private:
     282                 :     uint32_t    hashShift;      /* multiplicative hash shift */
     283                 :     uint32_t    entryCount;     /* number of entries in table */
     284                 :     uint32_t    gen;            /* entry storage generation number */
     285                 :     uint32_t    removedCount;   /* removed entry sentinels in table */
     286                 :     Entry       *table;         /* entry storage */
     287                 : 
     288         3838054 :     void setTableSizeLog2(unsigned sizeLog2) {
     289         3838054 :         hashShift = sHashBits - sizeLog2;
     290         3838054 :     }
     291                 : 
     292                 : #ifdef DEBUG
     293                 :     mutable struct Stats {
     294                 :         uint32_t        searches;       /* total number of table searches */
     295                 :         uint32_t        steps;          /* hash chain links traversed */
     296                 :         uint32_t        hits;           /* searches that found key */
     297                 :         uint32_t        misses;         /* searches that didn't find key */
     298                 :         uint32_t        addOverRemoved; /* adds that recycled a removed entry */
     299                 :         uint32_t        removes;        /* calls to remove */
     300                 :         uint32_t        removeFrees;    /* calls to remove that freed the entry */
     301                 :         uint32_t        grows;          /* table expansions */
     302                 :         uint32_t        shrinks;        /* table contractions */
     303                 :         uint32_t        compresses;     /* table compressions */
     304                 :     } stats;
     305                 : #   define METER(x) x
     306                 : #else
     307                 : #   define METER(x)
     308                 : #endif
     309                 : 
     310                 :     friend class js::ReentrancyGuard;
     311                 :     mutable DebugOnly<bool> entered;
     312                 :     DebugOnly<uint64_t>     mutationCount;
     313                 : 
     314                 :     /* The default initial capacity is 16, but you can ask for as small as 4. */
     315                 :     static const unsigned sMinSizeLog2  = 2;
     316                 :     static const unsigned sMinSize      = 1 << sMinSizeLog2;
     317                 :     static const unsigned sDefaultInitSizeLog2 = 4;
     318                 :   public:
     319                 :     static const unsigned sDefaultInitSize = 1 << sDefaultInitSizeLog2;
     320                 :   private:
     321                 :     static const unsigned sMaxInit      = JS_BIT(23);
     322                 :     static const unsigned sMaxCapacity  = JS_BIT(24);
     323                 :     static const unsigned sHashBits     = tl::BitSize<HashNumber>::result;
     324                 :     static const uint8_t  sMinAlphaFrac = 64;  /* (0x100 * .25) taken from jsdhash.h */
     325                 :     static const uint8_t  sMaxAlphaFrac = 192; /* (0x100 * .75) taken from jsdhash.h */
     326                 :     static const uint8_t  sInvMaxAlpha  = 171; /* (ceil(0x100 / .75) >> 1) */
     327                 :     static const HashNumber sGoldenRatio  = 0x9E3779B9U;       /* taken from jsdhash.h */
     328                 :     static const HashNumber sFreeKey = Entry::sFreeKey;
     329                 :     static const HashNumber sRemovedKey = Entry::sRemovedKey;
     330                 :     static const HashNumber sCollisionBit = Entry::sCollisionBit;
     331                 : 
     332                 :     static void staticAsserts()
     333                 :     {
     334                 :         /* Rely on compiler "constant overflow warnings". */
     335                 :         JS_STATIC_ASSERT(((sMaxInit * sInvMaxAlpha) >> 7) < sMaxCapacity);
     336                 :         JS_STATIC_ASSERT((sMaxCapacity * sInvMaxAlpha) <= UINT32_MAX);
     337                 :         JS_STATIC_ASSERT((sMaxCapacity * sizeof(Entry)) <= UINT32_MAX);
     338                 :     }
     339                 : 
     340       847839671 :     static bool isLiveHash(HashNumber hash)
     341                 :     {
     342       847839671 :         return Entry::isLiveHash(hash);
     343                 :     }
     344                 : 
     345       414934332 :     static HashNumber prepareHash(const Lookup& l)
     346                 :     {
     347       414934332 :         HashNumber keyHash = HashPolicy::hash(l);
     348                 : 
     349                 :         /* Improve keyHash distribution. */
     350       414934332 :         keyHash *= sGoldenRatio;
     351                 : 
     352                 :         /* Avoid reserved hash codes. */
     353       414934332 :         if (!isLiveHash(keyHash))
     354       119573220 :             keyHash -= (sRemovedKey + 1);
     355       414934332 :         return keyHash & ~sCollisionBit;
     356                 :     }
     357                 : 
     358         3838054 :     static Entry *createTable(AllocPolicy &alloc, uint32_t capacity)
     359                 :     {
     360         3838054 :         Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry));
     361         3838054 :         if (!newTable)
     362               0 :             return NULL;
     363       537635786 :         for (Entry *e = newTable, *end = e + capacity; e < end; ++e)
     364       533797732 :             new(e) Entry();
     365         3838054 :         return newTable;
     366                 :     }
     367                 : 
     368         3838054 :     static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity)
     369                 :     {
     370       537635786 :         for (Entry *e = oldTable, *end = e + capacity; e < end; ++e)
     371       533797732 :             e->~Entry();
     372         3838054 :         alloc.free_(oldTable);
     373         3838054 :     }
     374                 : 
     375                 :   public:
     376         3400418 :     HashTable(AllocPolicy ap)
     377                 :       : AllocPolicy(ap),
     378                 :         hashShift(sHashBits),
     379                 :         entryCount(0),
     380                 :         gen(0),
     381                 :         removedCount(0),
     382                 :         table(NULL),
     383                 :         entered(false),
     384         3400418 :         mutationCount(0)
     385         3400418 :     {}
     386                 : 
     387         3092544 :     bool init(uint32_t length)
     388                 :     {
     389                 :         /* Make sure that init isn't called twice. */
     390         3092544 :         JS_ASSERT(table == NULL);
     391                 : 
     392                 :         /*
     393                 :          * Correct for sMaxAlphaFrac such that the table will not resize
     394                 :          * when adding 'length' entries.
     395                 :          */
     396         3092544 :         if (length > sMaxInit) {
     397               0 :             this->reportAllocOverflow();
     398               0 :             return false;
     399                 :         }
     400         3092544 :         uint32_t capacity = (length * sInvMaxAlpha) >> 7;
     401                 : 
     402         3092544 :         if (capacity < sMinSize)
     403          708946 :             capacity = sMinSize;
     404                 : 
     405                 :         /* FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). */
     406         3092544 :         uint32_t roundUp = sMinSize, roundUpLog2 = sMinSizeLog2;
     407        15316053 :         while (roundUp < capacity) {
     408         9130965 :             roundUp <<= 1;
     409         9130965 :             ++roundUpLog2;
     410                 :         }
     411                 : 
     412         3092544 :         capacity = roundUp;
     413         3092544 :         JS_ASSERT(capacity <= sMaxCapacity);
     414                 : 
     415         3092544 :         table = createTable(*this, capacity);
     416         3092544 :         if (!table)
     417               0 :             return false;
     418                 : 
     419         3092544 :         setTableSizeLog2(roundUpLog2);
     420         3092544 :         METER(memset(&stats, 0, sizeof(stats)));
     421         3092544 :         return true;
     422                 :     }
     423                 : 
     424        54607262 :     bool initialized() const
     425                 :     {
     426        54607262 :         return !!table;
     427                 :     }
     428                 : 
     429         3400418 :     ~HashTable()
     430                 :     {
     431         3400418 :         if (table)
     432         3070974 :             destroyTable(*this, table, capacity());
     433         3400418 :     }
     434                 : 
     435                 :   private:
     436       553180901 :     static HashNumber hash1(HashNumber hash0, uint32_t shift) {
     437       553180901 :         return hash0 >> shift;
     438                 :     }
     439                 : 
     440                 :     struct DoubleHash {
     441                 :         HashNumber h2;
     442                 :         HashNumber sizeMask;
     443                 :     };
     444                 : 
     445       117872256 :     DoubleHash hash2(HashNumber curKeyHash, uint32_t hashShift) const {
     446       117872256 :         unsigned sizeLog2 = sHashBits - hashShift;
     447                 :         DoubleHash dh = {
     448                 :             ((curKeyHash << sizeLog2) >> hashShift) | 1,
     449                 :             (HashNumber(1) << sizeLog2) - 1
     450       117872256 :         };
     451                 :         return dh;
     452                 :     }
     453                 : 
     454       253536988 :     static HashNumber applyDoubleHash(HashNumber h1, const DoubleHash &dh) {
     455       253536988 :         return (h1 - dh.h2) & dh.sizeMask;
     456                 :     }
     457                 : 
     458       102004229 :     bool overloaded() {
     459       102004229 :         return entryCount + removedCount >= ((sMaxAlphaFrac * capacity()) >> 8);
     460                 :     }
     461                 : 
     462          726375 :     bool underloaded() {
     463          726375 :         uint32_t tableCapacity = capacity();
     464                 :         return tableCapacity > sMinSize &&
     465          726375 :                entryCount <= ((sMinAlphaFrac * tableCapacity) >> 8);
     466                 :     }
     467                 : 
     468       107219695 :     static bool match(Entry &e, const Lookup &l) {
     469       107219695 :         return HashPolicy::match(HashPolicy::getKey(e.t), l);
     470                 :     }
     471                 : 
     472       432905339 :     Entry &lookup(const Lookup &l, HashNumber keyHash, unsigned collisionBit) const
     473                 :     {
     474       432905339 :         JS_ASSERT(isLiveHash(keyHash));
     475       432905339 :         JS_ASSERT(!(keyHash & sCollisionBit));
     476       432905339 :         JS_ASSERT(collisionBit == 0 || collisionBit == sCollisionBit);
     477       432905339 :         JS_ASSERT(table);
     478       432905339 :         METER(stats.searches++);
     479                 : 
     480                 :         /* Compute the primary hash address. */
     481       432905339 :         HashNumber h1 = hash1(keyHash, hashShift);
     482       432905339 :         Entry *entry = &table[h1];
     483                 : 
     484                 :         /* Miss: return space for a new entry. */
     485       432905339 :         if (entry->isFree()) {
     486       251825808 :             METER(stats.misses++);
     487       251825808 :             return *entry;
     488                 :         }
     489                 : 
     490                 :         /* Hit: return entry. */
     491       181079531 :         if (entry->matchHash(keyHash) && match(*entry, l)) {
     492        78895236 :             METER(stats.hits++);
     493        78895236 :             return *entry;
     494                 :         }
     495                 : 
     496                 :         /* Collision: double hash. */
     497       102184295 :         DoubleHash dh = hash2(keyHash, hashShift);
     498                 : 
     499                 :         /* Save the first removed entry pointer so we can recycle later. */
     500       102184295 :         Entry *firstRemoved = NULL;
     501                 : 
     502       128953689 :         while(true) {
     503       231137984 :             if (JS_UNLIKELY(entry->isRemoved())) {
     504          683365 :                 if (!firstRemoved)
     505          340707 :                     firstRemoved = entry;
     506                 :             } else {
     507       230454619 :                 entry->setCollision(collisionBit);
     508                 :             }
     509                 : 
     510       231137984 :             METER(stats.steps++);
     511       231137984 :             h1 = applyDoubleHash(h1, dh);
     512                 : 
     513       231137984 :             entry = &table[h1];
     514       231137984 :             if (entry->isFree()) {
     515        76954834 :                 METER(stats.misses++);
     516        76954834 :                 return firstRemoved ? *firstRemoved : *entry;
     517                 :             }
     518                 : 
     519       154183150 :             if (entry->matchHash(keyHash) && match(*entry, l)) {
     520        25229461 :                 METER(stats.hits++);
     521        25229461 :                 return *entry;
     522                 :             }
     523                 :         }
     524                 :     }
     525                 : 
     526                 :     /*
     527                 :      * This is a copy of lookup hardcoded to the assumptions:
     528                 :      *   1. the lookup is a lookupForAdd
     529                 :      *   2. the key, whose |keyHash| has been passed is not in the table,
     530                 :      *   3. no entries have been removed from the table.
     531                 :      * This specialized search avoids the need for recovering lookup values
     532                 :      * from entries, which allows more flexible Lookup/Key types.
     533                 :      */
     534       120275562 :     Entry &findFreeEntry(HashNumber keyHash)
     535                 :     {
     536       120275562 :         METER(stats.searches++);
     537       120275562 :         JS_ASSERT(!(keyHash & sCollisionBit));
     538                 : 
     539                 :         /* N.B. the |keyHash| has already been distributed. */
     540                 : 
     541                 :         /* Compute the primary hash address. */
     542       120275562 :         HashNumber h1 = hash1(keyHash, hashShift);
     543       120275562 :         Entry *entry = &table[h1];
     544                 : 
     545                 :         /* Miss: return space for a new entry. */
     546       120275562 :         if (entry->isFree()) {
     547       104587601 :             METER(stats.misses++);
     548       104587601 :             return *entry;
     549                 :         }
     550                 : 
     551                 :         /* Collision: double hash. */
     552        15687961 :         DoubleHash dh = hash2(keyHash, hashShift);
     553                 : 
     554         6711043 :         while(true) {
     555        22399004 :             JS_ASSERT(!entry->isRemoved());
     556        22399004 :             entry->setCollision();
     557                 : 
     558        22399004 :             METER(stats.steps++);
     559        22399004 :             h1 = applyDoubleHash(h1, dh);
     560                 : 
     561        22399004 :             entry = &table[h1];
     562        22399004 :             if (entry->isFree()) {
     563        15687961 :                 METER(stats.misses++);
     564        15687961 :                 return *entry;
     565                 :             }
     566                 :         }
     567                 :     }
     568                 : 
     569          745510 :     bool changeTableSize(int deltaLog2)
     570                 :     {
     571                 :         /* Look, but don't touch, until we succeed in getting new entry store. */
     572          745510 :         Entry *oldTable = table;
     573          745510 :         uint32_t oldCap = capacity();
     574          745510 :         uint32_t newLog2 = sHashBits - hashShift + deltaLog2;
     575          745510 :         uint32_t newCapacity = JS_BIT(newLog2);
     576          745510 :         if (newCapacity > sMaxCapacity) {
     577               0 :             this->reportAllocOverflow();
     578               0 :             return false;
     579                 :         }
     580                 : 
     581          745510 :         Entry *newTable = createTable(*this, newCapacity);
     582          745510 :         if (!newTable)
     583               0 :             return false;
     584                 : 
     585                 :         /* We can't fail from here on, so update table parameters. */
     586          745510 :         setTableSizeLog2(newLog2);
     587          745510 :         removedCount = 0;
     588          745510 :         gen++;
     589          745510 :         table = newTable;
     590                 : 
     591                 :         /* Copy only live entries, leaving removed ones behind. */
     592       220499078 :         for (Entry *src = oldTable, *end = src + oldCap; src < end; ++src) {
     593       219753568 :             if (src->isLive()) {
     594       119750190 :                 src->unsetCollision();
     595       119750190 :                 findFreeEntry(src->getKeyHash()) = Move(*src);
     596                 :             }
     597                 :         }
     598                 : 
     599          745510 :         destroyTable(*this, oldTable, oldCap);
     600          745510 :         return true;
     601                 :     }
     602                 : 
     603          299938 :     void add(const Lookup &l, const Entry &e)
     604                 :     {
     605          299938 :         HashNumber keyHash = prepareHash(l);
     606          299938 :         Entry &entry = lookup(l, keyHash, sCollisionBit);
     607                 : 
     608          299938 :         if (entry.isRemoved()) {
     609           83798 :             METER(stats.addOverRemoved++);
     610           83798 :             removedCount--;
     611           83798 :             keyHash |= sCollisionBit;
     612                 :         }
     613                 : 
     614          299938 :         entry.t = e.t;
     615          299938 :         entry.setLive(keyHash);
     616          299938 :         entryCount++;
     617          299938 :         mutationCount++;
     618          299938 :     }
     619                 : 
     620       102004229 :     bool checkOverloaded()
     621                 :     {
     622       102004229 :         if (overloaded()) {
     623                 :             /* Compress if a quarter or more of all entries are removed. */
     624                 :             int deltaLog2;
     625          525392 :             if (removedCount >= (capacity() >> 2)) {
     626             100 :                 METER(stats.compresses++);
     627             100 :                 deltaLog2 = 0;
     628                 :             } else {
     629          525292 :                 METER(stats.grows++);
     630          525292 :                 deltaLog2 = 1;
     631                 :             }
     632                 : 
     633          525392 :             (void) changeTableSize(deltaLog2);
     634                 : 
     635          525392 :             return true;
     636                 :         }
     637                 : 
     638       101478837 :         return false;
     639                 :     }
     640                 : 
     641        16248611 :     void remove(Entry &e)
     642                 :     {
     643        16248611 :         METER(stats.removes++);
     644        16248611 :         if (e.hasCollision()) {
     645         3999617 :             e.setRemoved();
     646         3999617 :             removedCount++;
     647                 :         } else {
     648        12248994 :             METER(stats.removeFrees++);
     649        12248994 :             e.setFree();
     650                 :         }
     651        16248611 :         entryCount--;
     652        16248611 :         mutationCount++;
     653        16248611 :     }
     654                 : 
     655          726375 :     void checkUnderloaded()
     656                 :     {
     657          726375 :         if (underloaded()) {
     658          220118 :             METER(stats.shrinks++);
     659          220118 :             (void) changeTableSize(-1);
     660                 :         }
     661          726375 :     }
     662                 : 
     663                 :   public:
     664           99228 :     void clear()
     665                 :     {
     666                 :         if (tl::IsPodType<Entry>::result) {
     667           19136 :             memset(table, 0, sizeof(*table) * capacity());
     668                 :         } else {
     669           80092 :             uint32_t tableCapacity = capacity();
     670         7082556 :             for (Entry *e = table, *end = table + tableCapacity; e < end; ++e)
     671         7002464 :                 *e = Move(Entry());
     672                 :         }
     673           99228 :         removedCount = 0;
     674           99228 :         entryCount = 0;
     675           99228 :         mutationCount++;
     676           99228 :     }
     677                 : 
     678           21570 :     void finish()
     679                 :     {
     680           21570 :         JS_ASSERT(!entered);
     681                 : 
     682           21570 :         if (!table)
     683               0 :             return;
     684                 : 
     685           21570 :         destroyTable(*this, table, capacity());
     686           21570 :         table = NULL;
     687           21570 :         gen++;
     688           21570 :         entryCount = 0;
     689           21570 :         removedCount = 0;
     690           21570 :         mutationCount++;
     691                 :     }
     692                 : 
     693         1069429 :     Range all() const {
     694         1069429 :         return Range(table, table + capacity());
     695                 :     }
     696                 : 
     697        28267042 :     bool empty() const {
     698        28267042 :         return !entryCount;
     699                 :     }
     700                 : 
     701          198030 :     uint32_t count() const {
     702          198030 :         return entryCount;
     703                 :     }
     704                 : 
     705       108262707 :     uint32_t capacity() const {
     706       108262707 :         return JS_BIT(sHashBits - hashShift);
     707                 :     }
     708                 : 
     709         6950010 :     uint32_t generation() const {
     710         6950010 :         return gen;
     711                 :     }
     712                 : 
     713               0 :     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
     714               0 :         return mallocSizeOf(table);
     715                 :     }
     716                 : 
     717                 :     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
     718                 :         return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
     719                 :     }
     720                 : 
     721       234055447 :     Ptr lookup(const Lookup &l) const {
     722       468110894 :         ReentrancyGuard g(*this);
     723       234055447 :         HashNumber keyHash = prepareHash(l);
     724       234055447 :         return Ptr(lookup(l, keyHash, 0));
     725                 :     }
     726                 : 
     727       180578947 :     AddPtr lookupForAdd(const Lookup &l) const {
     728       361157894 :         ReentrancyGuard g(*this);
     729       180578947 :         HashNumber keyHash = prepareHash(l);
     730       180578947 :         Entry &entry = lookup(l, keyHash, sCollisionBit);
     731       180578947 :         AddPtr p(entry, keyHash);
     732       180578947 :         p.mutationCount = mutationCount;
     733                 :         return p;
     734                 :     }
     735                 : 
     736       102096012 :     bool add(AddPtr &p)
     737                 :     {
     738       204192024 :         ReentrancyGuard g(*this);
     739       102096012 :         JS_ASSERT(mutationCount == p.mutationCount);
     740       102096012 :         JS_ASSERT(table);
     741       102096012 :         JS_ASSERT(!p.found());
     742       102096012 :         JS_ASSERT(!(p.keyHash & sCollisionBit));
     743                 : 
     744                 :         /*
     745                 :          * Changing an entry from removed to live does not affect whether we
     746                 :          * are overloaded and can be handled separately.
     747                 :          */
     748       102096012 :         if (p.entry->isRemoved()) {
     749           91823 :             METER(stats.addOverRemoved++);
     750           91823 :             removedCount--;
     751           91823 :             p.keyHash |= sCollisionBit;
     752                 :         } else {
     753       102004189 :             if (checkOverloaded())
     754                 :                 /* Preserve the validity of |p.entry|. */
     755          525372 :                 p.entry = &findFreeEntry(p.keyHash);
     756                 :         }
     757                 : 
     758       102096012 :         p.entry->setLive(p.keyHash);
     759       102096012 :         entryCount++;
     760       102096012 :         mutationCount++;
     761       102096012 :         return true;
     762                 :     }
     763                 : 
     764                 :     /*
     765                 :      * There is an important contract between the caller and callee for this
     766                 :      * function: if add() returns true, the caller must assign the T value
     767                 :      * which produced p before using the hashtable again.
     768                 :      */
     769        17970098 :     bool add(AddPtr &p, T** pentry)
     770                 :     {
     771        17970098 :         if (!add(p))
     772               0 :             return false;
     773        17970098 :         *pentry = &p.entry->t;
     774        17970098 :         return true;
     775                 :     }
     776                 : 
     777        84125914 :     bool add(AddPtr &p, const T &t)
     778                 :     {
     779        84125914 :         if (!add(p))
     780               0 :             return false;
     781        84125914 :         p.entry->t = t;
     782        84125914 :         return true;
     783                 :     }
     784                 : 
     785        17971007 :     bool relookupOrAdd(AddPtr& p, const Lookup &l, const T& t)
     786                 :     {
     787        17971007 :         p.mutationCount = mutationCount;
     788                 :         {
     789        35942014 :             ReentrancyGuard g(*this);
     790        17971007 :             p.entry = &lookup(l, p.keyHash, sCollisionBit);
     791                 :         }
     792        17971007 :         return p.found() || add(p, t);
     793                 :     }
     794                 : 
     795          561542 :     void remove(Ptr p)
     796                 :     {
     797         1123084 :         ReentrancyGuard g(*this);
     798          561542 :         JS_ASSERT(p.found());
     799          561542 :         remove(*p.entry);
     800          561542 :         checkUnderloaded();
     801          561542 :     }
     802                 : 
     803                 : #undef METER
     804                 : };
     805                 : 
     806                 : }  /* namespace detail */
     807                 : 
     808                 : /*****************************************************************************/
     809                 : 
     810                 : /*
     811                 :  * Hash policy
     812                 :  *
     813                 :  * A hash policy P for a hash table with key-type Key must provide:
     814                 :  *  - a type |P::Lookup| to use to lookup table entries;
     815                 :  *  - a static member function |P::hash| with signature
     816                 :  *
     817                 :  *      static js::HashNumber hash(Lookup)
     818                 :  *
     819                 :  *    to use to hash the lookup type; and
     820                 :  *  - a static member function |P::match| with signature
     821                 :  *
     822                 :  *      static bool match(Key, Lookup)
     823                 :  *
     824                 :  *    to use to test equality of key and lookup values.
     825                 :  *
     826                 :  * Normally, Lookup = Key. In general, though, different values and types of
     827                 :  * values can be used to lookup and store. If a Lookup value |l| is != to the
     828                 :  * added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.:
     829                 :  *
     830                 :  *   js::HashSet<Key, P>::AddPtr p = h.lookup(l);
     831                 :  *   if (!p) {
     832                 :  *     assert(P::match(k, l));  // must hold
     833                 :  *     h.add(p, k);
     834                 :  *   }
     835                 :  */
     836                 : 
     837                 : /* Default hashing policies. */
     838                 : template <class Key>
     839                 : struct DefaultHasher
     840                 : {
     841                 :     typedef Key Lookup;
     842         4233642 :     static HashNumber hash(const Lookup &l) {
     843                 :         /* Hash if can implicitly cast to hash number type. */
     844         4233642 :         return l;
     845                 :     }
     846         1733798 :     static bool match(const Key &k, const Lookup &l) {
     847                 :         /* Use builtin or overloaded operator==. */
     848         1733798 :         return k == l;
     849                 :     }
     850                 : };
     851                 : 
     852                 : /*
     853                 :  * Pointer hashing policy that strips the lowest zeroBits when calculating the
     854                 :  * hash to improve key distribution.
     855                 :  */
     856                 : template <typename Key, size_t zeroBits>
     857                 : struct PointerHasher
     858                 : {
     859                 :     typedef Key Lookup;
     860        59806738 :     static HashNumber hash(const Lookup &l) {
     861        59806738 :         size_t word = reinterpret_cast<size_t>(l) >> zeroBits;
     862                 :         JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
     863                 : #if JS_BYTES_PER_WORD == 4
     864        59806738 :         return HashNumber(word);
     865                 : #else
     866                 :         JS_STATIC_ASSERT(sizeof word == 8);
     867                 :         return HashNumber((word >> 32) ^ word);
     868                 : #endif
     869                 :     }
     870        16458018 :     static bool match(const Key &k, const Lookup &l) {
     871        16458018 :         return k == l;
     872                 :     }
     873                 : };
     874                 : 
     875                 : template <typename Key, size_t zeroBits>
     876                 : struct TaggedPointerHasher
     877                 : {
     878                 :     typedef Key Lookup;
     879                 : 
     880                 :     static HashNumber hash(const Lookup &l) {
     881                 :         return PointerHasher<Key, zeroBits>::hash(l);
     882                 :     }
     883                 : 
     884                 :     static const uintptr_t COMPARE_MASK = uintptr_t(-1) - 1;
     885                 : 
     886                 :     static bool match(const Key &k, const Lookup &l) {
     887                 :         return (uintptr_t(k) & COMPARE_MASK) == uintptr_t(l);
     888                 :     }
     889                 : };
     890                 : 
     891                 : /*
     892                 :  * Specialized hashing policy for pointer types. It assumes that the type is
     893                 :  * at least word-aligned. For types with smaller size use PointerHasher.
     894                 :  */
     895                 : template <class T>
     896                 : struct DefaultHasher<T *>: PointerHasher<T *, tl::FloorLog2<sizeof(void *)>::result> { };
     897                 : 
     898                 : /* Looking for a hasher for jsid?  Try the DefaultHasher<jsid> in jsatom.h. */
     899                 : 
     900                 : template <class Key, class Value>
     901                 : class HashMapEntry
     902        20774816 : {
     903                 :     template <class, class, class> friend class detail::HashTable;
     904                 :     template <class> friend class detail::HashTableEntry;
     905          810394 :     void operator=(const HashMapEntry &rhs) {
     906          810394 :         const_cast<Key &>(key) = rhs.key;
     907          810394 :         value = rhs.value;
     908          810394 :     }
     909                 : 
     910                 :   public:
     911       122223664 :     HashMapEntry() : key(), value() {}
     912                 : 
     913                 :     template<typename KeyInput, typename ValueInput>
     914           39665 :     HashMapEntry(const KeyInput &k, const ValueInput &v) : key(k), value(v) {}
     915                 : 
     916                 :     HashMapEntry(MoveRef<HashMapEntry> rhs)
     917                 :       : key(Move(rhs->key)), value(Move(rhs->value)) { }
     918        34136545 :     void operator=(MoveRef<HashMapEntry> rhs) {
     919        34136545 :         const_cast<Key &>(key) = Move(rhs->key);
     920        34136545 :         value = Move(rhs->value);
     921        34136545 :     }
     922                 : 
     923                 :     const Key key;
     924                 :     Value value;
     925                 : };
     926                 : 
     927                 : namespace tl {
     928                 : 
     929                 : template <class T>
     930                 : struct IsPodType<detail::HashTableEntry<T> > {
     931                 :     static const bool result = IsPodType<T>::result;
     932                 : };
     933                 : 
     934                 : template <class K, class V>
     935                 : struct IsPodType<HashMapEntry<K, V> >
     936                 : {
     937                 :     static const bool result = IsPodType<K>::result && IsPodType<V>::result;
     938                 : };
     939                 : 
     940                 : } /* namespace tl */
     941                 : 
     942                 : /*
     943                 :  * JS-friendly, STL-like container providing a hash-based map from keys to
     944                 :  * values. In particular, HashMap calls constructors and destructors of all
     945                 :  * objects added so non-PODs may be used safely.
     946                 :  *
     947                 :  * Key/Value requirements:
     948                 :  *  - default constructible, copyable, destructible, assignable
     949                 :  * HashPolicy requirements:
     950                 :  *  - see "Hash policy" above (default js::DefaultHasher<Key>)
     951                 :  * AllocPolicy:
     952                 :  *  - see "Allocation policies" in jsalloc.h
     953                 :  *
     954                 :  * N.B: HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members
     955                 :  *      called by HashMap must not call back into the same HashMap object.
     956                 :  * N.B: Due to the lack of exception handling, the user must call |init()|.
     957                 :  */
     958                 : template <class Key,
     959                 :           class Value,
     960                 :           class HashPolicy = DefaultHasher<Key>,
     961                 :           class AllocPolicy = TempAllocPolicy>
     962                 : class HashMap
     963          671751 : {
     964                 :   public:
     965                 :     typedef typename HashPolicy::Lookup Lookup;
     966                 : 
     967                 :     typedef HashMapEntry<Key, Value> Entry;
     968                 : 
     969                 :   private:
     970                 :     /* Implement HashMap using HashTable. Lift |Key| operations to |Entry|. */
     971                 :     struct MapHashPolicy : HashPolicy
     972                 :     {
     973                 :         typedef Key KeyType;
     974        19061329 :         static const Key &getKey(Entry &e) { return e.key; }
     975          149969 :         static void setKey(Entry &e, Key &k) { const_cast<Key &>(e.key) = k; }
     976                 :     };
     977                 :     typedef detail::HashTable<Entry, MapHashPolicy, AllocPolicy> Impl;
     978                 : 
     979                 :     friend class Impl::Enum;
     980                 : 
     981                 :     /* Not implicitly copyable (expensive). May add explicit |clone| later. */
     982                 :     HashMap(const HashMap &);
     983                 :     HashMap &operator=(const HashMap &);
     984                 : 
     985                 :     Impl impl;
     986                 : 
     987                 :   public:
     988                 :     const static unsigned sDefaultInitSize = Impl::sDefaultInitSize;
     989                 : 
     990                 :     /*
     991                 :      * HashMap construction is fallible (due to OOM); thus the user must call
     992                 :      * init after constructing a HashMap and check the return value.
     993                 :      */
     994          671751 :     HashMap(AllocPolicy a = AllocPolicy()) : impl(a)  {}
     995          449134 :     bool init(uint32_t len = sDefaultInitSize)        { return impl.init(len); }
     996         6120116 :     bool initialized() const                          { return impl.initialized(); }
     997                 : 
     998                 :     /*
     999                 :      * Return whether the given lookup value is present in the map. E.g.:
    1000                 :      *
    1001                 :      *   typedef HashMap<int,char> HM;
    1002                 :      *   HM h;
    1003                 :      *   if (HM::Ptr p = h.lookup(3)) {
    1004                 :      *     const HM::Entry &e = *p; // p acts like a pointer to Entry
    1005                 :      *     assert(p->key == 3);     // Entry contains the key
    1006                 :      *     char val = p->value;     // and value
    1007                 :      *   }
    1008                 :      *
    1009                 :      * Also see the definition of Ptr in HashTable above (with T = Entry).
    1010                 :      */
    1011                 :     typedef typename Impl::Ptr Ptr;
    1012        15394491 :     Ptr lookup(const Lookup &l) const                 { return impl.lookup(l); }
    1013                 : 
    1014                 :     /* Assuming |p.found()|, remove |*p|. */
    1015          337152 :     void remove(Ptr p)                                { impl.remove(p); }
    1016                 : 
    1017                 :     /*
    1018                 :      * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
    1019                 :      * insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using
    1020                 :      * |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.:
    1021                 :      *
    1022                 :      *   typedef HashMap<int,char> HM;
    1023                 :      *   HM h;
    1024                 :      *   HM::AddPtr p = h.lookupForAdd(3);
    1025                 :      *   if (!p) {
    1026                 :      *     if (!h.add(p, 3, 'a'))
    1027                 :      *       return false;
    1028                 :      *   }
    1029                 :      *   const HM::Entry &e = *p;   // p acts like a pointer to Entry
    1030                 :      *   assert(p->key == 3);       // Entry contains the key
    1031                 :      *   char val = p->value;       // and value
    1032                 :      *
    1033                 :      * Also see the definition of AddPtr in HashTable above (with T = Entry).
    1034                 :      *
    1035                 :      * N.B. The caller must ensure that no mutating hash table operations
    1036                 :      * occur between a pair of |lookupForAdd| and |add| calls. To avoid
    1037                 :      * looking up the key a second time, the caller may use the more efficient
    1038                 :      * relookupOrAdd method. This method reuses part of the hashing computation
    1039                 :      * to more efficiently insert the key if it has not been added. For
    1040                 :      * example, a mutation-handling version of the previous example:
    1041                 :      *
    1042                 :      *    HM::AddPtr p = h.lookupForAdd(3);
    1043                 :      *    if (!p) {
    1044                 :      *      call_that_may_mutate_h();
    1045                 :      *      if (!h.relookupOrAdd(p, 3, 'a'))
    1046                 :      *        return false;
    1047                 :      *    }
    1048                 :      *    const HM::Entry &e = *p;
    1049                 :      *    assert(p->key == 3);
    1050                 :      *    char val = p->value;
    1051                 :      */
    1052                 :     typedef typename Impl::AddPtr AddPtr;
    1053        26650361 :     AddPtr lookupForAdd(const Lookup &l) const {
    1054        26650361 :         return impl.lookupForAdd(l);
    1055                 :     }
    1056                 : 
    1057                 :     template<typename KeyInput, typename ValueInput>
    1058        14623946 :     bool add(AddPtr &p, const KeyInput &k, const ValueInput &v) {
    1059                 :         Entry *pentry;
    1060        14623946 :         if (!impl.add(p, &pentry))
    1061               0 :             return false;
    1062        14623946 :         const_cast<Key &>(pentry->key) = k;
    1063        14623946 :         pentry->value = v;
    1064        14623946 :         return true;
    1065                 :     }
    1066                 : 
    1067         3346152 :     bool add(AddPtr &p, const Key &k, MoveRef<Value> v) {
    1068                 :         Entry *pentry;
    1069         3346152 :         if (!impl.add(p, &pentry))
    1070               0 :             return false;
    1071         3346152 :         const_cast<Key &>(pentry->key) = k;
    1072         3346152 :         pentry->value = v;
    1073         3346152 :         return true;
    1074                 :     }
    1075                 : 
    1076                 :     bool add(AddPtr &p, const Key &k) {
    1077                 :         Entry *pentry;
    1078                 :         if (!impl.add(p, &pentry))
    1079                 :             return false;
    1080                 :         const_cast<Key &>(pentry->key) = k;
    1081                 :         return true;
    1082                 :     }
    1083                 : 
    1084                 :     template<typename KeyInput, typename ValueInput>
    1085           39665 :     bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) {
    1086           39665 :         return impl.relookupOrAdd(p, k, Entry(k, v));
    1087                 :     }
    1088                 : 
    1089                 :     /*
    1090                 :      * |all()| returns a Range containing |count()| elements. E.g.:
    1091                 :      *
    1092                 :      *   typedef HashMap<int,char> HM;
    1093                 :      *   HM h;
    1094                 :      *   for (HM::Range r = h.all(); !r.empty(); r.popFront())
    1095                 :      *     char c = r.front().value;
    1096                 :      *
    1097                 :      * Also see the definition of Range in HashTable above (with T = Entry).
    1098                 :      */
    1099                 :     typedef typename Impl::Range Range;
    1100          332554 :     Range all() const                                 { return impl.all(); }
    1101           73144 :     uint32_t count() const                            { return impl.count(); }
    1102                 :     size_t capacity() const                           { return impl.capacity(); }
    1103                 :     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
    1104                 :         return impl.sizeOfExcludingThis(mallocSizeOf);
    1105                 :     }
    1106               0 :     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
    1107                 :         /*
    1108                 :          * Don't just call |impl.sizeOfExcludingThis()| because there's no
    1109                 :          * guarantee that |impl| is the first field in HashMap.
    1110                 :          */
    1111               0 :         return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
    1112                 :     }
    1113                 : 
    1114                 :     /*
    1115                 :      * Typedef for the enumeration class. An Enum may be used to examine and
    1116                 :      * remove table entries:
    1117                 :      *
    1118                 :      *   typedef HashMap<int,char> HM;
    1119                 :      *   HM s;
    1120                 :      *   for (HM::Enum e(s); !e.empty(); e.popFront())
    1121                 :      *     if (e.front().value == 'l')
    1122                 :      *       e.removeFront();
    1123                 :      *
    1124                 :      * Table resize may occur in Enum's destructor. Also see the definition of
    1125                 :      * Enum in HashTable above (with T = Entry).
    1126                 :      */
    1127                 :     typedef typename Impl::Enum Enum;
    1128                 : 
    1129                 :     /*
    1130                 :      * Remove all entries. This does not shrink the table. For that consider
    1131                 :      * using the finish() method.
    1132                 :      */
    1133           39122 :     void clear()                                      { impl.clear(); }
    1134                 : 
    1135                 :     /*
    1136                 :      * Remove all the entries and release all internal buffers. The map must
    1137                 :      * be initialized again before any use.
    1138                 :      */
    1139           21570 :     void finish()                                     { impl.finish(); }
    1140                 : 
    1141                 :    /* Does the table contain any entries? */
    1142           60172 :     bool empty() const                                { return impl.empty(); }
    1143                 : 
    1144                 :     /*
    1145                 :      * If |generation()| is the same before and after a HashMap operation,
    1146                 :      * pointers into the table remain valid.
    1147                 :      */
    1148         6699954 :     unsigned generation() const                       { return impl.generation(); }
    1149                 : 
    1150                 :     /* Shorthand operations: */
    1151                 : 
    1152         1266904 :     bool has(const Lookup &l) const {
    1153         1266904 :         return impl.lookup(l) != NULL;
    1154                 :     }
    1155                 : 
    1156                 :     /* Overwrite existing value with v. Return false on oom. */
    1157                 :     template<typename KeyInput, typename ValueInput>
    1158         1781288 :     bool put(const KeyInput &k, const ValueInput &v) {
    1159         3562576 :         AddPtr p = lookupForAdd(k);
    1160         1781288 :         if (p) {
    1161             570 :             p->value = v;
    1162             570 :             return true;
    1163                 :         }
    1164         1780718 :         return add(p, k, v);
    1165                 :     }
    1166                 : 
    1167                 :     /* Like put, but assert that the given key is not already present. */
    1168          732807 :     bool putNew(const Key &k, const Value &v) {
    1169         1465614 :         AddPtr p = lookupForAdd(k);
    1170          732807 :         JS_ASSERT(!p);
    1171          732807 :         return add(p, k, v);
    1172                 :     }
    1173                 : 
    1174                 :     /* Add (k,defaultValue) if k no found. Return false-y Ptr on oom. */
    1175               0 :     Ptr lookupWithDefault(const Key &k, const Value &defaultValue) {
    1176               0 :         AddPtr p = lookupForAdd(k);
    1177               0 :         if (p)
    1178               0 :             return p;
    1179               0 :         (void)add(p, k, defaultValue);  /* p is left false-y on oom. */
    1180               0 :         return p;
    1181                 :     }
    1182                 : 
    1183                 :     /* Remove if present. */
    1184           33095 :     void remove(const Lookup &l) {
    1185           33095 :         if (Ptr p = lookup(l))
    1186           33095 :             remove(p);
    1187           33095 :     }
    1188                 : };
    1189                 : 
    1190                 : /*
    1191                 :  * JS-friendly, STL-like container providing a hash-based set of values. In
    1192                 :  * particular, HashSet calls constructors and destructors of all objects added
    1193                 :  * so non-PODs may be used safely.
    1194                 :  *
    1195                 :  * T requirements:
    1196                 :  *  - default constructible, copyable, destructible, assignable
    1197                 :  * HashPolicy requirements:
    1198                 :  *  - see "Hash policy" above (default js::DefaultHasher<Key>)
    1199                 :  * AllocPolicy:
    1200                 :  *  - see "Allocation policies" in jsalloc.h
    1201                 :  *
    1202                 :  * N.B: HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by
    1203                 :  *      HashSet must not call back into the same HashSet object.
    1204                 :  * N.B: Due to the lack of exception handling, the user must call |init()|.
    1205                 :  */
    1206                 : template <class T, class HashPolicy = DefaultHasher<T>, class AllocPolicy = TempAllocPolicy>
    1207                 : class HashSet
    1208         2728667 : {
    1209                 :     typedef typename HashPolicy::Lookup Lookup;
    1210                 : 
    1211                 :     /* Implement HashSet in terms of HashTable. */
    1212                 :     struct SetOps : HashPolicy {
    1213                 :         typedef T KeyType;
    1214        88458304 :         static const KeyType &getKey(const T &t) { return t; }
    1215          149969 :         static void setKey(T &t, KeyType &k) { t = k; }
    1216                 :     };
    1217                 :     typedef detail::HashTable<const T, SetOps, AllocPolicy> Impl;
    1218                 : 
    1219                 :     friend class Impl::Enum;
    1220                 : 
    1221                 :     /* Not implicitly copyable (expensive). May add explicit |clone| later. */
    1222                 :     HashSet(const HashSet &);
    1223                 :     HashSet &operator=(const HashSet &);
    1224                 : 
    1225                 :     Impl impl;
    1226                 : 
    1227                 :   public:
    1228                 :     const static unsigned sDefaultInitSize = Impl::sDefaultInitSize;
    1229                 : 
    1230                 :     /*
    1231                 :      * HashSet construction is fallible (due to OOM); thus the user must call
    1232                 :      * init after constructing a HashSet and check the return value.
    1233                 :      */
    1234         2728667 :     HashSet(AllocPolicy a = AllocPolicy()) : impl(a)  {}
    1235         2643410 :     bool init(uint32_t len = sDefaultInitSize)        { return impl.init(len); }
    1236        48487146 :     bool initialized() const                          { return impl.initialized(); }
    1237                 : 
    1238                 :     /*
    1239                 :      * Return whether the given lookup value is present in the map. E.g.:
    1240                 :      *
    1241                 :      *   typedef HashSet<int> HS;
    1242                 :      *   HS h;
    1243                 :      *   if (HS::Ptr p = h.lookup(3)) {
    1244                 :      *     assert(*p == 3);   // p acts like a pointer to int
    1245                 :      *   }
    1246                 :      *
    1247                 :      * Also see the definition of Ptr in HashTable above.
    1248                 :      */
    1249                 :     typedef typename Impl::Ptr Ptr;
    1250         8809360 :     Ptr lookup(const Lookup &l) const                 { return impl.lookup(l); }
    1251                 : 
    1252                 :     /* Assuming |p.found()|, remove |*p|. */
    1253          224390 :     void remove(Ptr p)                                { impl.remove(p); }
    1254                 : 
    1255                 :     /*
    1256                 :      * Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
    1257                 :      * insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using
    1258                 :      * |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.:
    1259                 :      *
    1260                 :      *   typedef HashSet<int> HS;
    1261                 :      *   HS h;
    1262                 :      *   HS::AddPtr p = h.lookupForAdd(3);
    1263                 :      *   if (!p) {
    1264                 :      *     if (!h.add(p, 3))
    1265                 :      *       return false;
    1266                 :      *   }
    1267                 :      *   assert(*p == 3);   // p acts like a pointer to int
    1268                 :      *
    1269                 :      * Also see the definition of AddPtr in HashTable above.
    1270                 :      *
    1271                 :      * N.B. The caller must ensure that no mutating hash table operations
    1272                 :      * occur between a pair of |lookupForAdd| and |add| calls. To avoid
    1273                 :      * looking up the key a second time, the caller may use the more efficient
    1274                 :      * relookupOrAdd method. This method reuses part of the hashing computation
    1275                 :      * to more efficiently insert the key if it has not been added. For
    1276                 :      * example, a mutation-handling version of the previous example:
    1277                 :      *
    1278                 :      *    HS::AddPtr p = h.lookupForAdd(3);
    1279                 :      *    if (!p) {
    1280                 :      *      call_that_may_mutate_h();
    1281                 :      *      if (!h.relookupOrAdd(p, 3, 3))
    1282                 :      *        return false;
    1283                 :      *    }
    1284                 :      *    assert(*p == 3);
    1285                 :      *
    1286                 :      * Note that relookupOrAdd(p,l,t) performs Lookup using l and adds the
    1287                 :      * entry t, where the caller ensures match(l,t).
    1288                 :      */
    1289                 :     typedef typename Impl::AddPtr AddPtr;
    1290       153928586 :     AddPtr lookupForAdd(const Lookup &l) const {
    1291       153928586 :         return impl.lookupForAdd(l);
    1292                 :     }
    1293                 : 
    1294        66154907 :     bool add(AddPtr &p, const T &t) {
    1295        66154907 :         return impl.add(p, t);
    1296                 :     }
    1297                 : 
    1298        17931342 :     bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) {
    1299        17931342 :         return impl.relookupOrAdd(p, l, t);
    1300                 :     }
    1301                 : 
    1302                 :     /*
    1303                 :      * |all()| returns a Range containing |count()| elements:
    1304                 :      *
    1305                 :      *   typedef HashSet<int> HS;
    1306                 :      *   HS h;
    1307                 :      *   for (HS::Range r = h.all(); !r.empty(); r.popFront())
    1308                 :      *     int i = r.front();
    1309                 :      *
    1310                 :      * Also see the definition of Range in HashTable above.
    1311                 :      */
    1312                 :     typedef typename Impl::Range Range;
    1313          736875 :     Range all() const                                 { return impl.all(); }
    1314          124886 :     uint32_t count() const                            { return impl.count(); }
    1315                 :     size_t capacity() const                           { return impl.capacity(); }
    1316               0 :     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
    1317               0 :         return impl.sizeOfExcludingThis(mallocSizeOf);
    1318                 :     }
    1319                 :     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
    1320                 :         /*
    1321                 :          * Don't just call |impl.sizeOfExcludingThis()| because there's no
    1322                 :          * guarantee that |impl| is the first field in HashSet.
    1323                 :          */
    1324                 :         return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
    1325                 :     }
    1326                 : 
    1327                 :     /*
    1328                 :      * Typedef for the enumeration class. An Enum may be used to examine and
    1329                 :      * remove table entries:
    1330                 :      *
    1331                 :      *   typedef HashSet<int> HS;
    1332                 :      *   HS s;
    1333                 :      *   for (HS::Enum e(s); !e.empty(); e.popFront())
    1334                 :      *     if (e.front() == 42)
    1335                 :      *       e.removeFront();
    1336                 :      *
    1337                 :      * Table resize may occur in Enum's destructor. Also see the definition of
    1338                 :      * Enum in HashTable above.
    1339                 :      */
    1340                 :     typedef typename Impl::Enum Enum;
    1341                 : 
    1342                 :     /*
    1343                 :      * Remove all entries. This does not shrink the table. For that consider
    1344                 :      * using the finish() method.
    1345                 :      */
    1346           60106 :     void clear()                                      { impl.clear(); }
    1347                 : 
    1348                 :     /*
    1349                 :      * Remove all the entries and release all internal buffers. The set must
    1350                 :      * be initialized again before any use.
    1351                 :      */
    1352               0 :     void finish()                                     { impl.finish(); }
    1353                 : 
    1354                 :     /* Does the table contain any entries? */
    1355        28206870 :     bool empty() const                                { return impl.empty(); }
    1356                 : 
    1357                 :     /*
    1358                 :      * If |generation()| is the same before and after a HashSet operation,
    1359                 :      * pointers into the table remain valid.
    1360                 :      */
    1361          250056 :     unsigned generation() const                       { return impl.generation(); }
    1362                 : 
    1363                 :     /* Shorthand operations: */
    1364                 : 
    1365       208584692 :     bool has(const Lookup &l) const {
    1366       208584692 :         return impl.lookup(l) != NULL;
    1367                 :     }
    1368                 : 
    1369                 :     /* Overwrite existing value with v. Return false on oom. */
    1370          617771 :     bool put(const T &t) {
    1371         1235542 :         AddPtr p = lookupForAdd(t);
    1372          617771 :         return p ? true : add(p, t);
    1373                 :     }
    1374                 : 
    1375                 :     /* Like put, but assert that the given key is not already present. */
    1376          699948 :     bool putNew(const T &t) {
    1377         1399896 :         AddPtr p = lookupForAdd(t);
    1378          699948 :         JS_ASSERT(!p);
    1379          699948 :         return add(p, t);
    1380                 :     }
    1381                 : 
    1382         1758231 :     bool putNew(const Lookup &l, const T &t) {
    1383         3516462 :         AddPtr p = lookupForAdd(l);
    1384         1758231 :         JS_ASSERT(!p);
    1385         1758231 :         return add(p, t);
    1386                 :     }
    1387                 : 
    1388           85417 :     void remove(const Lookup &l) {
    1389           85417 :         if (Ptr p = lookup(l))
    1390           85417 :             remove(p);
    1391           85417 :     }
    1392                 : };
    1393                 : 
    1394                 : }  /* namespace js */
    1395                 : 
    1396                 : #endif

Generated by: LCOV version 1.7