LCOV - app.info - objdir/dist/include/js/HashTable.h

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-04-21 | 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       656617800 : 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      -713898091 :     static bool isLiveHash(HashNumber hash)
          76                 :     {
          77      -713898091 :         return hash > sRemovedKey;
          78                 :     }
          79                 :
          80                 :   public:
          81       661564416 :     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       148410113 :     void operator=(MoveRef<HashTableEntry> rhs) { keyHash = rhs->keyHash; t = Move(rhs->t); }
          85                 :
          86                 :     NonConstT t;
          87                 :
          88      1034406894 :     bool isFree() const           { return keyHash == sFreeKey; }
          89        21373528 :     void setFree()                { keyHash = sFreeKey; t = T(); }
          90       475494887 :     bool isRemoved() const        { return keyHash == sRemovedKey; }
          91         7729176 :     void setRemoved()             { keyHash = sRemovedKey; t = T(); }
          92     -1898365501 :     bool isLive() const           { return isLiveHash(keyHash); }
          93       128564711 :     void setLive(HashNumber hn)   { JS_ASSERT(isLiveHash(hn)); keyHash = hn; }
          94                 :
          95        29988403 :     void setCollision()           { JS_ASSERT(isLive()); keyHash |= sCollisionBit; }
          96       314878620 :     void setCollision(HashNumber collisionBit) {
          97       314878620 :         JS_ASSERT(isLive()); keyHash |= collisionBit;
          98       314878620 :     }
          99       143457985 :     void unsetCollision()         { JS_ASSERT(isLive()); keyHash &= ~sCollisionBit; }
         100       172560689 :     bool hasCollision() const     { JS_ASSERT(isLive()); return keyHash & sCollisionBit; }
         101       482882035 :     bool matchHash(HashNumber hn) { return (keyHash & ~sCollisionBit) == hn; }
         102       143457985 :     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       512689700 :         Ptr(Entry &entry) : entry(&entry) {}
         138                 :
         139                 :       public:
         140                 :         /* Leaves Ptr uninitialized. */
         141        59203169 :         Ptr() {
         142                 : #ifdef DEBUG
         143        59203169 :             entry = (Entry *)0xbad;
         144                 : #endif
         145        59203169 :         }
         146                 :
         147       678584766 :         bool found() const                    { return entry->isLive(); }
         148       490800858 :         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        61148206 :         T &operator*() const                  { return entry->t; }
         153       138463131 :         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       261173682 :         AddPtr(Entry &entry, HashNumber hn, uint64_t mutationCount)
         165       261173682 :             : 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        32254829 :         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         1614970 :         Range(Entry *c, Entry *e) : cur(c), end(e) {
         186        63256116 :             while (cur < end && !cur->isLive())
         187        60026176 :                 ++cur;
         188         1614970 :         }
         189                 :
         190                 :         Entry *cur, *end;
         191                 :
         192                 :       public:
         193         3442821 :         Range() : cur(NULL), end(NULL) {}
         194                 :
         195       749671911 :         bool empty() const {
         196       749671911 :             return cur == end;
         197                 :         }
         198                 :
         199       257823306 :         T &front() const {
         200       257823306 :             JS_ASSERT(!empty());
         201       257823306 :             return cur->t;
         202                 :         }
         203                 :
         204       244834248 :         void popFront() {
         205       244834248 :             JS_ASSERT(!empty());
         206       952418425 :             while (++cur < end && !cur->isLive())
         207       462749929 :                 continue;
         208       244834248 :         }
         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          802034 :         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        27011889 :         void removeFront() {
         245        27011889 :             table.remove(*this->cur);
         246        27011889 :             removed = true;
         247        27011889 :         }
         248                 :
         249                 :         /* Potentially rehashes the table. */
         250          802034 :         ~Enum() {
         251          802034 :             if (removed)
         252          215918 :                 table.checkUnderloaded();
         253          802034 :         }
         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         6041709 :     void setTableSizeLog2(unsigned sizeLog2) {
         272         6041709 :         hashShift = sHashBits - sizeLog2;
         273         6041709 :     }
         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      1055902699 :     static bool isLiveHash(HashNumber hash)
         326                 :     {
         327      1055902699 :         return Entry::isLiveHash(hash);
         328                 :     }
         329                 :
         330       512689700 :     static HashNumber prepareHash(const Lookup& l)
         331                 :     {
         332       512689700 :         HashNumber keyHash = HashPolicy::hash(l);
         333                 :
         334                 :         /* Improve keyHash distribution. */
         335       512689700 :         keyHash *= sGoldenRatio;
         336                 :
         337                 :         /* Avoid reserved hash codes. */
         338       512689700 :         if (!isLiveHash(keyHash))
         339       125071033 :             keyHash -= (sRemovedKey + 1);
         340       512689700 :         return keyHash & ~sCollisionBit;
         341                 :     }
         342                 :
         343         6041709 :     static Entry *createTable(AllocPolicy &alloc, uint32_t capacity)
         344                 :     {
         345         6041709 :         Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry));
         346         6041709 :         if (!newTable)
         347               0 :             return NULL;
         348       662653997 :         for (Entry *e = newTable, *end = e + capacity; e < end; ++e)
         349       656612288 :             new(e) Entry();
         350         6041709 :         return newTable;
         351                 :     }
         352                 :
         353         6041567 :     static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity)
         354                 :     {
         355       662648791 :         for (Entry *e = oldTable, *end = e + capacity; e < end; ++e)
         356       656607224 :             e->~Entry();
         357         6041567 :         alloc.free_(oldTable);
         358         6041567 :     }
         359                 :
         360                 :   public:
         361         5518280 :     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         5518280 :         mutationCount(0)
         371                 : #endif
         372         5518280 :     {}
         373                 :
         374         4884929 :     bool init(uint32_t length)
         375                 :     {
         376                 :         /* Make sure that init isn't called twice. */
         377         4884929 :         JS_ASSERT(table == NULL);
         378                 :
         379                 :         /*
         380                 :          * Correct for sMaxAlphaFrac such that the table will not resize
         381                 :          * when adding 'length' entries.
         382                 :          */
         383         4884929 :         if (length > sMaxInit) {
         384               0 :             this->reportAllocOverflow();
         385               0 :             return false;
         386                 :         }
         387         4884929 :         uint32_t capacity = (length * sInvMaxAlpha) >> 7;
         388                 :
         389         4884929 :         if (capacity < sMinSize)
         390         1327662 :             capacity = sMinSize;
         391                 :
         392                 :         /* FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034). */
         393         4884929 :         uint32_t roundUp = sMinSize, roundUpLog2 = sMinSizeLog2;
         394        22437815 :         while (roundUp < capacity) {
         395        12667957 :             roundUp <<= 1;
         396        12667957 :             ++roundUpLog2;
         397                 :         }
         398                 :
         399         4884929 :         capacity = roundUp;
         400         4884929 :         JS_ASSERT(capacity <= sMaxCapacity);
         401                 :
         402         4884929 :         table = createTable(*this, capacity);
         403         4884929 :         if (!table)
         404               0 :             return false;
         405                 :
         406         4884929 :         setTableSizeLog2(roundUpLog2);
         407         4884929 :         METER(memset(&stats, 0, sizeof(stats)));
         408         4884929 :         return true;
         409                 :     }
         410                 :
         411        81845566 :     bool initialized() const
         412                 :     {
         413        81845566 :         return !!table;
         414                 :     }
         415                 :
         416         5518131 :     ~HashTable()
         417                 :     {
         418         5518131 :         if (table)
         419         4857830 :             destroyTable(*this, table, capacity());
         420         5518131 :     }
         421                 :
         422                 :   private:
         423       687476718 :     static HashNumber hash1(HashNumber hash0, uint32_t shift) {
         424       687476718 :         return hash0 >> shift;
         425                 :     }
         426                 :
         427       162414570 :     static HashNumber hash2(HashNumber hash0, uint32_t log2, uint32_t shift) {
         428       162414570 :         return ((hash0 << log2) >> shift) | 1;
         429                 :     }
         430                 :
         431       128068146 :     bool overloaded() {
         432       128068146 :         return entryCount + removedCount >= ((sMaxAlphaFrac * capacity()) >> 8);
         433                 :     }
         434                 :
         435         2306733 :     bool underloaded() {
         436         2306733 :         uint32_t tableCapacity = capacity();
         437                 :         return tableCapacity > sMinSize &&
         438         2306733 :                entryCount <= ((sMinAlphaFrac * tableCapacity) >> 8);
         439                 :     }
         440                 :
         441       172661680 :     static bool match(Entry &e, const Lookup &l) {
         442       172661680 :         return HashPolicy::match(HashPolicy::getKey(e.t), l);
         443                 :     }
         444                 :
         445       543212999 :     Entry &lookup(const Lookup &l, HashNumber keyHash, unsigned collisionBit) const
         446                 :     {
         447       543212999 :         JS_ASSERT(isLiveHash(keyHash));
         448       543212999 :         JS_ASSERT(!(keyHash & sCollisionBit));
         449       543212999 :         JS_ASSERT(collisionBit == 0 || collisionBit == sCollisionBit);
         450       543212999 :         JS_ASSERT(table);
         451       543212999 :         METER(stats.searches++);
         452                 :
         453                 :         /* Compute the primary hash address. */
         454       543212999 :         HashNumber h1 = hash1(keyHash, hashShift);
         455       543212999 :         Entry *entry = &table[h1];
         456                 :
         457                 :         /* Miss: return space for a new entry. */
         458       543212999 :         if (entry->isFree()) {
         459       287709351 :             METER(stats.misses++);
         460       287709351 :             return *entry;
         461                 :         }
         462                 :
         463                 :         /* Hit: return entry. */
         464       255503648 :         if (entry->matchHash(keyHash) && match(*entry, l)) {
         465       113950230 :             METER(stats.hits++);
         466       113950230 :             return *entry;
         467                 :         }
         468                 :
         469                 :         /* Collision: double hash. */
         470       141553418 :         unsigned sizeLog2 = sHashBits - hashShift;
         471       141553418 :         HashNumber h2 = hash2(keyHash, sizeLog2, hashShift);
         472       141553418 :         HashNumber sizeMask = (HashNumber(1) << sizeLog2) - 1;
         473                 :
         474                 :         /* Save the first removed entry pointer so we can recycle later. */
         475       141553418 :         Entry *firstRemoved = NULL;
         476                 :
         477       175388355 :         while(true) {
         478       316941773 :             if (JS_UNLIKELY(entry->isRemoved())) {
         479         2063153 :                 if (!firstRemoved)
         480         1480230 :                     firstRemoved = entry;
         481                 :             } else {
         482       314878620 :                 entry->setCollision(collisionBit);
         483                 :             }
         484                 :
         485       316941773 :             METER(stats.steps++);
         486       316941773 :             h1 -= h2;
         487       316941773 :             h1 &= sizeMask;
         488                 :
         489       316941773 :             entry = &table[h1];
         490       316941773 :             if (entry->isFree()) {
         491        89563386 :                 METER(stats.misses++);
         492        89563386 :                 return firstRemoved ? *firstRemoved : *entry;
         493                 :             }
         494                 :
         495       227378387 :             if (entry->matchHash(keyHash) && match(*entry, l)) {
         496        51990032 :                 METER(stats.hits++);
         497        51990032 :                 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       144263719 :     Entry &findFreeEntry(HashNumber keyHash)
         511                 :     {
         512       144263719 :         METER(stats.searches++);
         513       144263719 :         JS_ASSERT(!(keyHash & sCollisionBit));
         514                 :
         515                 :         /* N.B. the |keyHash| has already been distributed. */
         516                 :
         517                 :         /* Compute the primary hash address. */
         518       144263719 :         HashNumber h1 = hash1(keyHash, hashShift);
         519       144263719 :         Entry *entry = &table[h1];
         520                 :
         521                 :         /* Miss: return space for a new entry. */
         522       144263719 :         if (entry->isFree()) {
         523       123402567 :             METER(stats.misses++);
         524       123402567 :             return *entry;
         525                 :         }
         526                 :
         527                 :         /* Collision: double hash. */
         528        20861152 :         unsigned sizeLog2 = sHashBits - hashShift;
         529        20861152 :         HashNumber h2 = hash2(keyHash, sizeLog2, hashShift);
         530        20861152 :         HashNumber sizeMask = (HashNumber(1) << sizeLog2) - 1;
         531                 :
         532         9127251 :         while(true) {
         533        29988403 :             JS_ASSERT(!entry->isRemoved());
         534        29988403 :             entry->setCollision();
         535                 :
         536        29988403 :             METER(stats.steps++);
         537        29988403 :             h1 -= h2;
         538        29988403 :             h1 &= sizeMask;
         539                 :
         540        29988403 :             entry = &table[h1];
         541        29988403 :             if (entry->isFree()) {
         542        20861152 :                 METER(stats.misses++);
         543        20861152 :                 return *entry;
         544                 :             }
         545                 :         }
         546                 :     }
         547                 :
         548         1156780 :     bool changeTableSize(int deltaLog2)
         549                 :     {
         550                 :         /* Look, but don't touch, until we succeed in getting new entry store. */
         551         1156780 :         Entry *oldTable = table;
         552         1156780 :         uint32_t oldCap = capacity();
         553         1156780 :         uint32_t newLog2 = sHashBits - hashShift + deltaLog2;
         554         1156780 :         uint32_t newCapacity = JS_BIT(newLog2);
         555         1156780 :         if (newCapacity > sMaxCapacity) {
         556               0 :             this->reportAllocOverflow();
         557               0 :             return false;
         558                 :         }
         559                 :
         560         1156780 :         Entry *newTable = createTable(*this, newCapacity);
         561         1156780 :         if (!newTable)
         562               0 :             return false;
         563                 :
         564                 :         /* We can't fail from here on, so update table parameters. */
         565         1156780 :         setTableSizeLog2(newLog2);
         566         1156780 :         removedCount = 0;
         567         1156780 :         gen++;
         568         1156780 :         table = newTable;
         569                 :
         570                 :         /* Copy only live entries, leaving removed ones behind. */
         571       290677692 :         for (Entry *src = oldTable, *end = src + oldCap; src < end; ++src) {
         572       289520912 :             if (src->isLive()) {
         573       143457985 :                 src->unsetCollision();
         574       143457985 :                 findFreeEntry(src->getKeyHash()) = Move(*src);
         575                 :             }
         576                 :         }
         577                 :
         578         1156780 :         destroyTable(*this, oldTable, oldCap);
         579         1156780 :         return true;
         580                 :     }
         581                 :
         582        29102704 :     void remove(Entry &e)
         583                 :     {
         584        29102704 :         METER(stats.removes++);
         585        29102704 :         if (e.hasCollision()) {
         586         7729176 :             e.setRemoved();
         587         7729176 :             removedCount++;
         588                 :         } else {
         589        21373528 :             METER(stats.removeFrees++);
         590        21373528 :             e.setFree();
         591                 :         }
         592        29102704 :         entryCount--;
         593                 : #ifdef DEBUG
         594        29102704 :         mutationCount++;
         595                 : #endif
         596        29102704 :     }
         597                 :
         598         2306733 :     void checkUnderloaded()
         599                 :     {
         600         2306733 :         if (underloaded()) {
         601          351046 :             METER(stats.shrinks++);
         602          351046 :             (void) changeTableSize(-1);
         603                 :         }
         604         2306733 :     }
         605                 :
         606                 :   public:
         607          132281 :     void clear()
         608                 :     {
         609                 :         if (tl::IsPodType<Entry>::result) {
         610           91510 :             memset(table, 0, sizeof(*table) * capacity());
         611                 :         } else {
         612           40771 :             uint32_t tableCapacity = capacity();
         613         4992899 :             for (Entry *e = table, *end = table + tableCapacity; e < end; ++e)
         614         4952128 :                 *e = Move(Entry());
         615                 :         }
         616          132281 :         removedCount = 0;
         617          132281 :         entryCount = 0;
         618                 : #ifdef DEBUG
         619          132281 :         mutationCount++;
         620                 : #endif
         621          132281 :     }
         622                 :
         623           26957 :     void finish()
         624                 :     {
         625           26957 :         JS_ASSERT(!entered);
         626                 :
         627           26957 :         if (!table)
         628               0 :             return;
         629                 :
         630           26957 :         destroyTable(*this, table, capacity());
         631           26957 :         table = NULL;
         632           26957 :         gen++;
         633           26957 :         entryCount = 0;
         634           26957 :         removedCount = 0;
         635                 : #ifdef DEBUG
         636           26957 :         mutationCount++;
         637                 : #endif
         638                 :     }
         639                 :
         640         1614970 :     Range all() const {
         641         1614970 :         return Range(table, table + capacity());
         642                 :     }
         643                 :
         644        34645630 :     bool empty() const {
         645        34645630 :         return !entryCount;
         646                 :     }
         647                 :
         648        13974817 :     uint32_t count() const {
         649        13974817 :         return entryCount;
         650                 :     }
         651                 :
         652       138969431 :     uint32_t capacity() const {
         653       138969431 :         return JS_BIT(sHashBits - hashShift);
         654                 :     }
         655                 :
         656         7055162 :     uint32_t generation() const {
         657         7055162 :         return gen;
         658                 :     }
         659                 :
         660            1255 :     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
         661            1255 :         return mallocSizeOf(table);
         662                 :     }
         663                 :
         664                 :     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
         665                 :         return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
         666                 :     }
         667                 :
         668       251516018 :     Ptr lookup(const Lookup &l) const {
         669       503032036 :         ReentrancyGuard g(*this);
         670       251516018 :         HashNumber keyHash = prepareHash(l);
         671       251516018 :         return Ptr(lookup(l, keyHash, 0));
         672                 :     }
         673                 :
         674       261173682 :     AddPtr lookupForAdd(const Lookup &l) const {
         675       522347364 :         ReentrancyGuard g(*this);
         676       261173682 :         HashNumber keyHash = prepareHash(l);
         677       261173682 :         Entry &entry = lookup(l, keyHash, sCollisionBit);
         678                 : #ifdef DEBUG
         679       261173682 :         return AddPtr(entry, keyHash, mutationCount);
         680                 : #else
         681                 :         return AddPtr(entry, keyHash);
         682                 : #endif
         683                 :     }
         684                 :
         685       128564711 :     bool add(AddPtr &p)
         686                 :     {
         687       257129422 :         ReentrancyGuard g(*this);
         688       128564711 :         JS_ASSERT(mutationCount == p.mutationCount);
         689       128564711 :         JS_ASSERT(table);
         690       128564711 :         JS_ASSERT(!p.found());
         691       128564711 :         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       128564711 :         if (p.entry->isRemoved()) {
         698          496565 :             METER(stats.addOverRemoved++);
         699          496565 :             removedCount--;
         700          496565 :             p.keyHash |= sCollisionBit;
         701                 :         } else {
         702                 :             /* If alpha is >= .75, grow or compress the table. */
         703       128068146 :             if (overloaded()) {
         704                 :                 /* Compress if a quarter or more of all entries are removed. */
         705                 :                 int deltaLog2;
         706          805734 :                 if (removedCount >= (capacity() >> 2)) {
         707             480 :                     METER(stats.compresses++);
         708             480 :                     deltaLog2 = 0;
         709                 :                 } else {
         710          805254 :                     METER(stats.grows++);
         711          805254 :                     deltaLog2 = 1;
         712                 :                 }
         713                 :
         714          805734 :                 if (!changeTableSize(deltaLog2))
         715               0 :                     return false;
         716                 :
         717                 :                 /* Preserve the validity of |p.entry|. */
         718          805734 :                 p.entry = &findFreeEntry(p.keyHash);
         719                 :             }
         720                 :         }
         721                 :
         722       128564711 :         p.entry->setLive(p.keyHash);
         723       128564711 :         entryCount++;
         724                 : #ifdef DEBUG
         725       128564711 :         mutationCount++;
         726                 : #endif
         727       128564711 :         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        26752974 :     bool add(AddPtr &p, T** pentry)
         736                 :     {
         737        26752974 :         if (!add(p))
         738               0 :             return false;
         739        26752974 :         *pentry = &p.entry->t;
         740        26752974 :         return true;
         741                 :     }
         742                 :
         743       101811737 :     bool add(AddPtr &p, const T &t)
         744                 :     {
         745       101811737 :         if (!add(p))
         746               0 :             return false;
         747       101811737 :         p.entry->t = t;
         748       101811737 :         return true;
         749                 :     }
         750                 :
         751        30523299 :     bool relookupOrAdd(AddPtr& p, const Lookup &l, const T& t)
         752                 :     {
         753                 : #ifdef DEBUG
         754        30523299 :         p.mutationCount = mutationCount;
         755                 : #endif
         756                 :         {
         757        61046598 :             ReentrancyGuard g(*this);
         758        30523299 :             p.entry = &lookup(l, p.keyHash, sCollisionBit);
         759                 :         }
         760        30523299 :         return p.found() || add(p, t);
         761                 :     }
         762                 :
         763         2090815 :     void remove(Ptr p)
         764                 :     {
         765         4181630 :         ReentrancyGuard g(*this);
         766         2090815 :         JS_ASSERT(p.found());
         767         2090815 :         remove(*p.entry);
         768         2090815 :         checkUnderloaded();
         769         2090815 :     }
         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        84839002 :     static HashNumber hash(const Lookup &l) {
         828        84839002 :         size_t word = reinterpret_cast<size_t>(l) >> zeroBits;
         829                 :         JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
         830                 : #if JS_BYTES_PER_WORD == 4
         831        84839002 :         return HashNumber(word);
         832                 : #else
         833                 :         JS_STATIC_ASSERT(sizeof word == 8);
         834                 :         return HashNumber((word >> 32) ^ word);
         835                 : #endif
         836                 :     }
         837        22907414 :     static bool match(const Key &k, const Lookup &l) {
         838        22907414 :         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        20771107 : {
         870                 :     template <class, class, class> friend class detail::HashTable;
         871                 :     template <class> friend class detail::HashTableEntry;
         872         1075784 :     void operator=(const HashMapEntry &rhs) {
         873         1075784 :         const_cast<Key &>(key) = rhs.key;
         874         1075784 :         value = rhs.value;
         875         1075784 :     }
         876                 :
         877                 :   public:
         878       160448761 :     HashMapEntry() : key(), value() {}
         879                 :
         880                 :     template<typename KeyInput, typename ValueInput>
         881           73391 :     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        35981895 :     void operator=(MoveRef<HashMapEntry> rhs) {
         886        35981895 :         const_cast<Key &>(key) = Move(rhs->key);
         887        35981895 :         value = Move(rhs->value);
         888        35981895 :     }
         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         2303520 : {
         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        24467072 :         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         2303531 :     HashMap(AllocPolicy a = AllocPolicy()) : impl(a) {}
         959         1762884 :     bool init(uint32_t len = Impl::sDefaultInitSize)  { return impl.init(len); }
         960         7303040 :     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        18786660 :     Ptr lookup(const Lookup &l) const                 { return impl.lookup(l); }
         977                 :
         978                 :     /* Assuming |p.found()|, remove |*p|. */
         979          875180 :     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        40210277 :     AddPtr lookupForAdd(const Lookup &l) const {
        1018        40210277 :         return impl.lookupForAdd(l);
        1019                 :     }
        1020                 :
        1021                 :     template<typename KeyInput, typename ValueInput>
        1022        23409870 :     bool add(AddPtr &p, const KeyInput &k, const ValueInput &v) {
        1023                 :         Entry *pentry;
        1024        23409870 :         if (!impl.add(p, &pentry))
        1025               0 :             return false;
        1026        23409870 :         const_cast<Key &>(pentry->key) = k;
        1027        23409870 :         pentry->value = v;
        1028        23409870 :         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           73391 :     bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) {
        1050           73391 :         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          521915 :     Range all() const                                 { return impl.all(); }
        1065        12115228 :     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          112413 :     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           26957 :     void finish()                                     { impl.finish(); }
        1104                 :
        1105                 :    /* Does the table contain any entries? */
        1106           70587 :     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         6693778 :     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         2711775 :     Entry *put(const KeyInput &k, const ValueInput &v) {
        1123         2711775 :         AddPtr p = lookupForAdd(k);
        1124         2711775 :         if (p) {
        1125             577 :             p->value = v;
        1126             577 :             return &*p;
        1127                 :         }
        1128         2711198 :         return add(p, k, v) ? &*p : NULL;
        1129                 :     }
        1130                 :
        1131                 :     /* Like put, but assert that the given key is not already present. */
        1132         2269480 :     bool putNew(const Key &k, const Value &v) {
        1133         2269480 :         AddPtr p = lookupForAdd(k);
        1134         2269480 :         JS_ASSERT(!p);
        1135         2269480 :         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          527367 :     void remove(const Lookup &l) {
        1149          527367 :         if (Ptr p = lookup(l))
        1150          527367 :             remove(p);
        1151          527367 :     }
        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         3214611 : {
        1173                 :     typedef typename HashPolicy::Lookup Lookup;
        1174                 :
        1175                 :     /* Implement HashSet in terms of HashTable. */
        1176                 :     struct SetOps : HashPolicy {
        1177                 :         typedef T KeyType;
        1178       148194608 :         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         3214749 :     HashSet(AllocPolicy a = AllocPolicy()) : impl(a) {}
        1196         3122045 :     bool init(uint32_t len = Impl::sDefaultInitSize)  { return impl.init(len); }
        1197        74542526 :     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        19449346 :     Ptr lookup(const Lookup &l) const                 { return impl.lookup(l); }
        1212                 :
        1213                 :     /* Assuming |p.found()|, remove |*p|. */
        1214         1215635 :     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       220963405 :     AddPtr lookupForAdd(const Lookup &l) const {
        1252       220963405 :         return impl.lookupForAdd(l);
        1253                 :     }
        1254                 :
        1255        71288438 :     bool add(AddPtr &p, const T &t) {
        1256        71288438 :         return impl.add(p, t);
        1257                 :     }
        1258                 :
        1259        30449908 :     bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) {
        1260        30449908 :         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         1093055 :     Range all() const                                 { return impl.all(); }
        1275         1859589 :     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            1207 :     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            1207 :         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           19868 :     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        34575043 :     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          361384 :     unsigned generation() const                       { return impl.generation(); }
        1323                 :
        1324                 :     /* Shorthand operations: */
        1325                 :
        1326       213277464 :     bool has(const Lookup &l) const {
        1327       213277464 :         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         3326757 :     bool putNew(const Lookup &l, const T &t) {
        1344         3326757 :         AddPtr p = lookupForAdd(l);
        1345         3326757 :         JS_ASSERT(!p);
        1346         3326757 :         return add(p, t);
        1347                 :     }
        1348                 :
        1349         1015523 :     void remove(const Lookup &l) {
        1350         1015523 :         if (Ptr p = lookup(l))
        1351         1015523 :             remove(p);
        1352         1015523 :     }
        1353                 : };
        1354                 :
        1355                 : }  /* namespace js */
        1356                 :
        1357                 : #endif

Generated by: LCOV version 1.7