LCOV - code coverage report
Current view: top level - js/public - HashTable.h (source / functions) Hit Total Coverage
Test: output.info Lines: 561 666 84.2 %
Date: 2017-07-14 16:53:18 Functions: 3005 10575 28.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef js_HashTable_h
       8             : #define js_HashTable_h
       9             : 
      10             : #include "mozilla/Alignment.h"
      11             : #include "mozilla/Assertions.h"
      12             : #include "mozilla/Attributes.h"
      13             : #include "mozilla/Casting.h"
      14             : #include "mozilla/HashFunctions.h"
      15             : #include "mozilla/MemoryReporting.h"
      16             : #include "mozilla/Move.h"
      17             : #include "mozilla/Opaque.h"
      18             : #include "mozilla/PodOperations.h"
      19             : #include "mozilla/ReentrancyGuard.h"
      20             : #include "mozilla/TemplateLib.h"
      21             : #include "mozilla/TypeTraits.h"
      22             : #include "mozilla/UniquePtr.h"
      23             : 
      24             : #include "js/Utility.h"
      25             : 
      26             : namespace js {
      27             : 
      28             : class TempAllocPolicy;
      29             : template <class> struct DefaultHasher;
      30             : template <class, class> class HashMapEntry;
      31             : namespace detail {
      32             :     template <class T> class HashTableEntry;
      33             :     template <class T, class HashPolicy, class AllocPolicy> class HashTable;
      34             : } // namespace detail
      35             : 
      36             : /*****************************************************************************/
      37             : 
      38             : // The "generation" of a hash table is an opaque value indicating the state of
      39             : // modification of the hash table through its lifetime.  If the generation of
      40             : // a hash table compares equal at times T1 and T2, then lookups in the hash
      41             : // table, pointers to (or into) hash table entries, etc. at time T1 are valid
      42             : // at time T2.  If the generation compares unequal, these computations are all
      43             : // invalid and must be performed again to be used.
      44             : //
      45             : // Generations are meaningfully comparable only with respect to a single hash
      46             : // table.  It's always nonsensical to compare the generation of distinct hash
      47             : // tables H1 and H2.
      48             : using Generation = mozilla::Opaque<uint64_t>;
      49             : 
      50             : // A JS-friendly, STL-like container providing a hash-based map from keys to
      51             : // values. In particular, HashMap calls constructors and destructors of all
      52             : // objects added so non-PODs may be used safely.
      53             : //
      54             : // Key/Value requirements:
      55             : //  - movable, destructible, assignable
      56             : // HashPolicy requirements:
      57             : //  - see Hash Policy section below
      58             : // AllocPolicy:
      59             : //  - see jsalloc.h
      60             : //
      61             : // Note:
      62             : // - HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members
      63             : //   called by HashMap must not call back into the same HashMap object.
      64             : // - Due to the lack of exception handling, the user must call |init()|.
      65             : template <class Key,
      66             :           class Value,
      67             :           class HashPolicy = DefaultHasher<Key>,
      68             :           class AllocPolicy = TempAllocPolicy>
      69       27071 : class HashMap
      70             : {
      71             :     typedef HashMapEntry<Key, Value> TableEntry;
      72             : 
      73             :     struct MapHashPolicy : HashPolicy
      74             :     {
      75             :         using Base = HashPolicy;
      76             :         typedef Key KeyType;
      77     2097144 :         static const Key& getKey(TableEntry& e) { return e.key(); }
      78        2496 :         static void setKey(TableEntry& e, Key& k) { HashPolicy::rekey(e.mutableKey(), k); }
      79             :     };
      80             : 
      81             :     typedef detail::HashTable<TableEntry, MapHashPolicy, AllocPolicy> Impl;
      82             :     Impl impl;
      83             : 
      84             :   public:
      85             :     typedef typename HashPolicy::Lookup Lookup;
      86             :     typedef TableEntry Entry;
      87             : 
      88             :     // HashMap construction is fallible (due to OOM); thus the user must call
      89             :     // init after constructing a HashMap and check the return value.
      90       28287 :     explicit HashMap(AllocPolicy a = AllocPolicy()) : impl(a)  {}
      91        5999 :     MOZ_MUST_USE bool init(uint32_t len = 16) { return impl.init(len); }
      92       17460 :     bool initialized() const                  { return impl.initialized(); }
      93             : 
      94             :     // Return whether the given lookup value is present in the map. E.g.:
      95             :     //
      96             :     //   typedef HashMap<int,char> HM;
      97             :     //   HM h;
      98             :     //   if (HM::Ptr p = h.lookup(3)) {
      99             :     //     const HM::Entry& e = *p; // p acts like a pointer to Entry
     100             :     //     assert(p->key == 3);     // Entry contains the key
     101             :     //     char val = p->value;     // and value
     102             :     //   }
     103             :     //
     104             :     // Also see the definition of Ptr in HashTable above (with T = Entry).
     105             :     typedef typename Impl::Ptr Ptr;
     106      825320 :     MOZ_ALWAYS_INLINE Ptr lookup(const Lookup& l) const { return impl.lookup(l); }
     107             : 
     108             :     // Like lookup, but does not assert if two threads call lookup at the same
     109             :     // time. Only use this method when none of the threads will modify the map.
     110        4650 :     MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& l) const {
     111        4650 :         return impl.readonlyThreadsafeLookup(l);
     112             :     }
     113             : 
     114             :     // Assuming |p.found()|, remove |*p|.
     115        4200 :     void remove(Ptr p)                                { impl.remove(p); }
     116             : 
     117             :     // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
     118             :     // insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using
     119             :     // |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.:
     120             :     //
     121             :     //   typedef HashMap<int,char> HM;
     122             :     //   HM h;
     123             :     //   HM::AddPtr p = h.lookupForAdd(3);
     124             :     //   if (!p) {
     125             :     //     if (!h.add(p, 3, 'a'))
     126             :     //       return false;
     127             :     //   }
     128             :     //   const HM::Entry& e = *p;   // p acts like a pointer to Entry
     129             :     //   assert(p->key == 3);       // Entry contains the key
     130             :     //   char val = p->value;       // and value
     131             :     //
     132             :     // Also see the definition of AddPtr in HashTable above (with T = Entry).
     133             :     //
     134             :     // N.B. The caller must ensure that no mutating hash table operations
     135             :     // occur between a pair of |lookupForAdd| and |add| calls. To avoid
     136             :     // looking up the key a second time, the caller may use the more efficient
     137             :     // relookupOrAdd method. This method reuses part of the hashing computation
     138             :     // to more efficiently insert the key if it has not been added. For
     139             :     // example, a mutation-handling version of the previous example:
     140             :     //
     141             :     //    HM::AddPtr p = h.lookupForAdd(3);
     142             :     //    if (!p) {
     143             :     //      call_that_may_mutate_h();
     144             :     //      if (!h.relookupOrAdd(p, 3, 'a'))
     145             :     //        return false;
     146             :     //    }
     147             :     //    const HM::Entry& e = *p;
     148             :     //    assert(p->key == 3);
     149             :     //    char val = p->value;
     150             :     typedef typename Impl::AddPtr AddPtr;
     151      949551 :     MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& l) const {
     152      949551 :         return impl.lookupForAdd(l);
     153             :     }
     154             : 
     155             :     template<typename KeyInput, typename ValueInput>
     156       52457 :     MOZ_MUST_USE bool add(AddPtr& p, KeyInput&& k, ValueInput&& v) {
     157             :         return impl.add(p,
     158             :                         mozilla::Forward<KeyInput>(k),
     159       52457 :                         mozilla::Forward<ValueInput>(v));
     160             :     }
     161             : 
     162             :     template<typename KeyInput>
     163             :     MOZ_MUST_USE bool add(AddPtr& p, KeyInput&& k) {
     164             :         return impl.add(p, mozilla::Forward<KeyInput>(k), Value());
     165             :     }
     166             : 
     167             :     template<typename KeyInput, typename ValueInput>
     168        1095 :     MOZ_MUST_USE bool relookupOrAdd(AddPtr& p, KeyInput&& k, ValueInput&& v) {
     169             :         return impl.relookupOrAdd(p, k,
     170             :                                   mozilla::Forward<KeyInput>(k),
     171        1095 :                                   mozilla::Forward<ValueInput>(v));
     172             :     }
     173             : 
     174             :     // |all()| returns a Range containing |count()| elements. E.g.:
     175             :     //
     176             :     //   typedef HashMap<int,char> HM;
     177             :     //   HM h;
     178             :     //   for (HM::Range r = h.all(); !r.empty(); r.popFront())
     179             :     //     char c = r.front().value();
     180             :     //
     181             :     // Also see the definition of Range in HashTable above (with T = Entry).
     182             :     typedef typename Impl::Range Range;
     183       53696 :     Range all() const                                 { return impl.all(); }
     184             : 
     185             :     // Typedef for the enumeration class. An Enum may be used to examine and
     186             :     // remove table entries:
     187             :     //
     188             :     //   typedef HashMap<int,char> HM;
     189             :     //   HM s;
     190             :     //   for (HM::Enum e(s); !e.empty(); e.popFront())
     191             :     //     if (e.front().value() == 'l')
     192             :     //       e.removeFront();
     193             :     //
     194             :     // Table resize may occur in Enum's destructor. Also see the definition of
     195             :     // Enum in HashTable above (with T = Entry).
     196             :     typedef typename Impl::Enum Enum;
     197             : 
     198             :     // Remove all entries. This does not shrink the table. For that consider
     199             :     // using the finish() method.
     200         590 :     void clear()                                      { impl.clear(); }
     201             : 
     202             :     // Remove all entries. Unlike clear() this method tries to shrink the table.
     203             :     // Unlike finish() it does not require the map to be initialized again.
     204             :     void clearAndShrink()                             { impl.clearAndShrink(); }
     205             : 
     206             :     // Remove all the entries and release all internal buffers. The map must
     207             :     // be initialized again before any use.
     208         117 :     void finish()                                     { impl.finish(); }
     209             : 
     210             :     // Does the table contain any entries?
     211       13711 :     bool empty() const                                { return impl.empty(); }
     212             : 
     213             :     // Number of live elements in the map.
     214       30779 :     uint32_t count() const                            { return impl.count(); }
     215             : 
     216             :     // Total number of allocation in the dynamic table. Note: resize will
     217             :     // happen well before count() == capacity().
     218             :     size_t capacity() const                           { return impl.capacity(); }
     219             : 
     220             :     // Don't just call |impl.sizeOfExcludingThis()| because there's no
     221             :     // guarantee that |impl| is the first field in HashMap.
     222           0 :     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     223           0 :         return impl.sizeOfExcludingThis(mallocSizeOf);
     224             :     }
     225           0 :     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     226           0 :         return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
     227             :     }
     228             : 
     229           0 :     Generation generation() const {
     230           0 :         return impl.generation();
     231             :     }
     232             : 
     233             :     /************************************************** Shorthand operations */
     234             : 
     235      440976 :     bool has(const Lookup& l) const {
     236      440976 :         return impl.lookup(l).found();
     237             :     }
     238             : 
     239             :     // Overwrite existing value with v. Return false on oom.
     240             :     template<typename KeyInput, typename ValueInput>
     241        3159 :     MOZ_MUST_USE bool put(KeyInput&& k, ValueInput&& v) {
     242        3159 :         AddPtr p = lookupForAdd(k);
     243        3159 :         if (p) {
     244           5 :             p->value() = mozilla::Forward<ValueInput>(v);
     245           5 :             return true;
     246             :         }
     247        3154 :         return add(p, mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
     248             :     }
     249             : 
     250             :     // Like put, but assert that the given key is not already present.
     251             :     template<typename KeyInput, typename ValueInput>
     252       15896 :     MOZ_MUST_USE bool putNew(KeyInput&& k, ValueInput&& v) {
     253       15896 :         return impl.putNew(k, mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
     254             :     }
     255             : 
     256             :     // Only call this to populate an empty map after reserving space with init().
     257             :     template<typename KeyInput, typename ValueInput>
     258        2217 :     void putNewInfallible(KeyInput&& k, ValueInput&& v) {
     259        2217 :         impl.putNewInfallible(k, mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
     260        2217 :     }
     261             : 
     262             :     // Add (k,defaultValue) if |k| is not found. Return a false-y Ptr on oom.
     263           0 :     Ptr lookupWithDefault(const Key& k, const Value& defaultValue) {
     264           0 :         AddPtr p = lookupForAdd(k);
     265           0 :         if (p)
     266           0 :             return p;
     267           0 :         bool ok = add(p, k, defaultValue);
     268           0 :         MOZ_ASSERT_IF(!ok, !p); // p is left false-y on oom.
     269             :         (void)ok;
     270           0 :         return p;
     271             :     }
     272             : 
     273             :     // Remove if present.
     274        4476 :     void remove(const Lookup& l) {
     275        4476 :         if (Ptr p = lookup(l))
     276        4177 :             remove(p);
     277        4476 :     }
     278             : 
     279             :     // Infallibly rekey one entry, if necessary.
     280             :     // Requires template parameters Key and HashPolicy::Lookup to be the same type.
     281       24782 :     void rekeyIfMoved(const Key& old_key, const Key& new_key) {
     282       24782 :         if (old_key != new_key)
     283       24782 :             rekeyAs(old_key, new_key, new_key);
     284       24782 :     }
     285             : 
     286             :     // Infallibly rekey one entry if present, and return whether that happened.
     287       24782 :     bool rekeyAs(const Lookup& old_lookup, const Lookup& new_lookup, const Key& new_key) {
     288       24782 :         if (Ptr p = lookup(old_lookup)) {
     289        2496 :             impl.rekeyAndMaybeRehash(p, new_lookup, new_key);
     290        2496 :             return true;
     291             :         }
     292       22286 :         return false;
     293             :     }
     294             : 
     295             :     // HashMap is movable
     296        3117 :     HashMap(HashMap&& rhs) : impl(mozilla::Move(rhs.impl)) {}
     297           0 :     void operator=(HashMap&& rhs) {
     298           0 :         MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
     299           0 :         impl = mozilla::Move(rhs.impl);
     300           0 :     }
     301             : 
     302             :   private:
     303             :     // HashMap is not copyable or assignable
     304             :     HashMap(const HashMap& hm) = delete;
     305             :     HashMap& operator=(const HashMap& hm) = delete;
     306             : 
     307             :     friend class Impl::Enum;
     308             : };
     309             : 
     310             : /*****************************************************************************/
     311             : 
     312             : // A JS-friendly, STL-like container providing a hash-based set of values. In
     313             : // particular, HashSet calls constructors and destructors of all objects added
     314             : // so non-PODs may be used safely.
     315             : //
     316             : // T requirements:
     317             : //  - movable, destructible, assignable
     318             : // HashPolicy requirements:
     319             : //  - see Hash Policy section below
     320             : // AllocPolicy:
     321             : //  - see jsalloc.h
     322             : //
     323             : // Note:
     324             : // - HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by
     325             : //   HashSet must not call back into the same HashSet object.
     326             : // - Due to the lack of exception handling, the user must call |init()|.
     327             : template <class T,
     328             :           class HashPolicy = DefaultHasher<T>,
     329             :           class AllocPolicy = TempAllocPolicy>
     330         234 : class HashSet
     331             : {
     332             :     struct SetOps : HashPolicy
     333             :     {
     334             :         using Base = HashPolicy;
     335             :         typedef T KeyType;
     336     1160245 :         static const KeyType& getKey(const T& t) { return t; }
     337         471 :         static void setKey(T& t, KeyType& k) { HashPolicy::rekey(t, k); }
     338             :     };
     339             : 
     340             :     typedef detail::HashTable<const T, SetOps, AllocPolicy> Impl;
     341             :     Impl impl;
     342             : 
     343             :   public:
     344             :     typedef typename HashPolicy::Lookup Lookup;
     345             :     typedef T Entry;
     346             : 
     347             :     // HashSet construction is fallible (due to OOM); thus the user must call
     348             :     // init after constructing a HashSet and check the return value.
     349        3764 :     explicit HashSet(AllocPolicy a = AllocPolicy()) : impl(a)  {}
     350        3728 :     MOZ_MUST_USE bool init(uint32_t len = 16) { return impl.init(len); }
     351      180080 :     bool initialized() const                  { return impl.initialized(); }
     352             : 
     353             :     // Return whether the given lookup value is present in the map. E.g.:
     354             :     //
     355             :     //   typedef HashSet<int> HS;
     356             :     //   HS h;
     357             :     //   if (HS::Ptr p = h.lookup(3)) {
     358             :     //     assert(*p == 3);   // p acts like a pointer to int
     359             :     //   }
     360             :     //
     361             :     // Also see the definition of Ptr in HashTable above.
     362             :     typedef typename Impl::Ptr Ptr;
     363      512146 :     MOZ_ALWAYS_INLINE Ptr lookup(const Lookup& l) const { return impl.lookup(l); }
     364             : 
     365             :     // Like lookup, but does not assert if two threads call lookup at the same
     366             :     // time. Only use this method when none of the threads will modify the map.
     367      124762 :     MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& l) const {
     368      124762 :         return impl.readonlyThreadsafeLookup(l);
     369             :     }
     370             : 
     371             :     // Assuming |p.found()|, remove |*p|.
     372         874 :     void remove(Ptr p)                                { impl.remove(p); }
     373             : 
     374             :     // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
     375             :     // insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using
     376             :     // |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.:
     377             :     //
     378             :     //   typedef HashSet<int> HS;
     379             :     //   HS h;
     380             :     //   HS::AddPtr p = h.lookupForAdd(3);
     381             :     //   if (!p) {
     382             :     //     if (!h.add(p, 3))
     383             :     //       return false;
     384             :     //   }
     385             :     //   assert(*p == 3);   // p acts like a pointer to int
     386             :     //
     387             :     // Also see the definition of AddPtr in HashTable above.
     388             :     //
     389             :     // N.B. The caller must ensure that no mutating hash table operations
     390             :     // occur between a pair of |lookupForAdd| and |add| calls. To avoid
     391             :     // looking up the key a second time, the caller may use the more efficient
     392             :     // relookupOrAdd method. This method reuses part of the hashing computation
     393             :     // to more efficiently insert the key if it has not been added. For
     394             :     // example, a mutation-handling version of the previous example:
     395             :     //
     396             :     //    HS::AddPtr p = h.lookupForAdd(3);
     397             :     //    if (!p) {
     398             :     //      call_that_may_mutate_h();
     399             :     //      if (!h.relookupOrAdd(p, 3, 3))
     400             :     //        return false;
     401             :     //    }
     402             :     //    assert(*p == 3);
     403             :     //
     404             :     // Note that relookupOrAdd(p,l,t) performs Lookup using |l| and adds the
     405             :     // entry |t|, where the caller ensures match(l,t).
     406             :     typedef typename Impl::AddPtr AddPtr;
     407      838249 :     MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& l) const {
     408      838249 :         return impl.lookupForAdd(l);
     409             :     }
     410             : 
     411             :     template <typename U>
     412      194584 :     MOZ_MUST_USE bool add(AddPtr& p, U&& u) {
     413      194584 :         return impl.add(p, mozilla::Forward<U>(u));
     414             :     }
     415             : 
     416             :     template <typename U>
     417       15318 :     MOZ_MUST_USE bool relookupOrAdd(AddPtr& p, const Lookup& l, U&& u) {
     418       15318 :         return impl.relookupOrAdd(p, l, mozilla::Forward<U>(u));
     419             :     }
     420             : 
     421             :     // |all()| returns a Range containing |count()| elements:
     422             :     //
     423             :     //   typedef HashSet<int> HS;
     424             :     //   HS h;
     425             :     //   for (HS::Range r = h.all(); !r.empty(); r.popFront())
     426             :     //     int i = r.front();
     427             :     //
     428             :     // Also see the definition of Range in HashTable above.
     429             :     typedef typename Impl::Range Range;
     430          99 :     Range all() const                                 { return impl.all(); }
     431             : 
     432             :     // Typedef for the enumeration class. An Enum may be used to examine and
     433             :     // remove table entries:
     434             :     //
     435             :     //   typedef HashSet<int> HS;
     436             :     //   HS s;
     437             :     //   for (HS::Enum e(s); !e.empty(); e.popFront())
     438             :     //     if (e.front() == 42)
     439             :     //       e.removeFront();
     440             :     //
     441             :     // Table resize may occur in Enum's destructor. Also see the definition of
     442             :     // Enum in HashTable above.
     443             :     typedef typename Impl::Enum Enum;
     444             : 
     445             :     // Remove all entries. This does not shrink the table. For that consider
     446             :     // using the finish() method.
     447         209 :     void clear()                                      { impl.clear(); }
     448             : 
     449             :     // Remove all entries. Unlike clear() this method tries to shrink the table.
     450             :     // Unlike finish() it does not require the set to be initialized again.
     451          16 :     void clearAndShrink()                             { impl.clearAndShrink(); }
     452             : 
     453             :     // Remove all the entries and release all internal buffers. The set must
     454             :     // be initialized again before any use.
     455           4 :     void finish()                                     { impl.finish(); }
     456             : 
     457             :     // Does the table contain any entries?
     458         192 :     bool empty() const                                { return impl.empty(); }
     459             : 
     460             :     // Number of live elements in the map.
     461       21296 :     uint32_t count() const                            { return impl.count(); }
     462             : 
     463             :     // Total number of allocation in the dynamic table. Note: resize will
     464             :     // happen well before count() == capacity().
     465             :     size_t capacity() const                           { return impl.capacity(); }
     466             : 
     467             :     // Don't just call |impl.sizeOfExcludingThis()| because there's no
     468             :     // guarantee that |impl| is the first field in HashSet.
     469           0 :     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     470           0 :         return impl.sizeOfExcludingThis(mallocSizeOf);
     471             :     }
     472           0 :     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     473           0 :         return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
     474             :     }
     475             : 
     476             :     Generation generation() const {
     477             :         return impl.generation();
     478             :     }
     479             : 
     480             :     /************************************************** Shorthand operations */
     481             : 
     482        4869 :     bool has(const Lookup& l) const {
     483        4869 :         return impl.lookup(l).found();
     484             :     }
     485             : 
     486             :     // Add |u| if it is not present already. Return false on oom.
     487             :     template <typename U>
     488       11802 :     MOZ_MUST_USE bool put(U&& u) {
     489       11802 :         AddPtr p = lookupForAdd(u);
     490       11802 :         return p ? true : add(p, mozilla::Forward<U>(u));
     491             :     }
     492             : 
     493             :     // Like put, but assert that the given key is not already present.
     494             :     template <typename U>
     495          10 :     MOZ_MUST_USE bool putNew(U&& u) {
     496          10 :         return impl.putNew(u, mozilla::Forward<U>(u));
     497             :     }
     498             : 
     499             :     template <typename U>
     500        8341 :     MOZ_MUST_USE bool putNew(const Lookup& l, U&& u) {
     501        8341 :         return impl.putNew(l, mozilla::Forward<U>(u));
     502             :     }
     503             : 
     504             :     // Only call this to populate an empty set after reserving space with init().
     505             :     template <typename U>
     506        4190 :     void putNewInfallible(const Lookup& l, U&& u) {
     507        4190 :         impl.putNewInfallible(l, mozilla::Forward<U>(u));
     508        4190 :     }
     509             : 
     510        1152 :     void remove(const Lookup& l) {
     511        1152 :         if (Ptr p = lookup(l))
     512         827 :             remove(p);
     513        1152 :     }
     514             : 
     515             :     // Infallibly rekey one entry, if present.
     516             :     // Requires template parameters T and HashPolicy::Lookup to be the same type.
     517             :     void rekeyIfMoved(const Lookup& old_value, const T& new_value) {
     518             :         if (old_value != new_value)
     519             :             rekeyAs(old_value, new_value, new_value);
     520             :     }
     521             : 
     522             :     // Infallibly rekey one entry if present, and return whether that happened.
     523         471 :     bool rekeyAs(const Lookup& old_lookup, const Lookup& new_lookup, const T& new_value) {
     524         471 :         if (Ptr p = lookup(old_lookup)) {
     525         471 :             impl.rekeyAndMaybeRehash(p, new_lookup, new_value);
     526         471 :             return true;
     527             :         }
     528           0 :         return false;
     529             :     }
     530             : 
     531             :     // Infallibly replace the current key at |p| with an equivalent key.
     532             :     // Specifically, both HashPolicy::hash and HashPolicy::match must return
     533             :     // identical results for the new and old key when applied against all
     534             :     // possible matching values.
     535         146 :     void replaceKey(Ptr p, const T& new_value) {
     536         146 :         MOZ_ASSERT(p.found());
     537         146 :         MOZ_ASSERT(*p != new_value);
     538         146 :         MOZ_ASSERT(HashPolicy::hash(*p) == HashPolicy::hash(new_value));
     539         146 :         MOZ_ASSERT(HashPolicy::match(*p, new_value));
     540         146 :         const_cast<T&>(*p) = new_value;
     541         146 :     }
     542             : 
     543             :     // HashSet is movable
     544          78 :     HashSet(HashSet&& rhs) : impl(mozilla::Move(rhs.impl)) {}
     545           8 :     void operator=(HashSet&& rhs) {
     546           8 :         MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
     547           8 :         impl = mozilla::Move(rhs.impl);
     548           8 :     }
     549             : 
     550             :   private:
     551             :     // HashSet is not copyable or assignable
     552             :     HashSet(const HashSet& hs) = delete;
     553             :     HashSet& operator=(const HashSet& hs) = delete;
     554             : 
     555             :     friend class Impl::Enum;
     556             : };
     557             : 
     558             : /*****************************************************************************/
     559             : 
     560             : // Hash Policy
     561             : //
     562             : // A hash policy P for a hash table with key-type Key must provide:
     563             : //  - a type |P::Lookup| to use to lookup table entries;
     564             : //  - a static member function |P::hash| with signature
     565             : //
     566             : //      static js::HashNumber hash(Lookup)
     567             : //
     568             : //    to use to hash the lookup type; and
     569             : //  - a static member function |P::match| with signature
     570             : //
     571             : //      static bool match(Key, Lookup)
     572             : //
     573             : //    to use to test equality of key and lookup values.
     574             : //
     575             : // Normally, Lookup = Key. In general, though, different values and types of
     576             : // values can be used to lookup and store. If a Lookup value |l| is != to the
     577             : // added Key value |k|, the user must ensure that |P::match(k,l)|. E.g.:
     578             : //
     579             : //   js::HashSet<Key, P>::AddPtr p = h.lookup(l);
     580             : //   if (!p) {
     581             : //     assert(P::match(k, l));  // must hold
     582             : //     h.add(p, k);
     583             : //   }
     584             : 
     585             : // Pointer hashing policy that strips the lowest zeroBits when calculating the
     586             : // hash to improve key distribution.
     587             : template <typename Key, size_t zeroBits>
     588             : struct PointerHasher
     589             : {
     590             :     typedef Key Lookup;
     591     1641695 :     static HashNumber hash(const Lookup& l) {
     592     1641695 :         size_t word = reinterpret_cast<size_t>(l) >> zeroBits;
     593             :         static_assert(sizeof(HashNumber) == 4,
     594             :                       "subsequent code assumes a four-byte hash");
     595             : #if JS_BITS_PER_WORD == 32
     596             :         return HashNumber(word);
     597             : #else
     598             :         static_assert(sizeof(word) == 8,
     599             :                       "unexpected word size, new hashing strategy required to "
     600             :                       "properly incorporate all bits");
     601     1641695 :         return HashNumber((word >> 32) ^ word);
     602             : #endif
     603             :     }
     604     2533579 :     static bool match(const Key& k, const Lookup& l) {
     605     2533579 :         return k == l;
     606             :     }
     607         866 :     static void rekey(Key& k, const Key& newKey) {
     608         866 :         k = newKey;
     609         866 :     }
     610             : };
     611             : 
     612             : // Default hash policy: just use the 'lookup' value. This of course only
     613             : // works if the lookup value is integral. HashTable applies ScrambleHashCode to
     614             : // the result of the 'hash' which means that it is 'ok' if the lookup value is
     615             : // not well distributed over the HashNumber domain.
     616             : template <class Key>
     617       19849 : struct DefaultHasher
     618             : {
     619             :     typedef Key Lookup;
     620      624994 :     static HashNumber hash(const Lookup& l) {
     621             :         // Hash if can implicitly cast to hash number type.
     622      624994 :         return l;
     623             :     }
     624      617775 :     static bool match(const Key& k, const Lookup& l) {
     625             :         // Use builtin or overloaded operator==.
     626      617775 :         return k == l;
     627             :     }
     628        2101 :     static void rekey(Key& k, const Key& newKey) {
     629        2101 :         k = newKey;
     630        2101 :     }
     631             : };
     632             : 
     633             : // Specialize hashing policy for pointer types. It assumes that the type is
     634             : // at least word-aligned. For types with smaller size use PointerHasher.
     635             : template <class T>
     636             : struct DefaultHasher<T*> : PointerHasher<T*, mozilla::tl::FloorLog2<sizeof(void*)>::value>
     637             : {};
     638             : 
     639             : // Specialize hashing policy for mozilla::UniquePtr to proxy the UniquePtr's
     640             : // raw pointer to PointerHasher.
     641             : template <class T, class D>
     642             : struct DefaultHasher<mozilla::UniquePtr<T, D>>
     643             : {
     644             :     using Lookup = mozilla::UniquePtr<T, D>;
     645             :     using PtrHasher = PointerHasher<T*, mozilla::tl::FloorLog2<sizeof(void*)>::value>;
     646             : 
     647             :     static HashNumber hash(const Lookup& l) {
     648             :         return PtrHasher::hash(l.get());
     649             :     }
     650             :     static bool match(const mozilla::UniquePtr<T, D>& k, const Lookup& l) {
     651             :         return PtrHasher::match(k.get(), l.get());
     652             :     }
     653             :     static void rekey(mozilla::UniquePtr<T, D>& k, mozilla::UniquePtr<T, D>&& newKey) {
     654             :         k = mozilla::Move(newKey);
     655             :     }
     656             : };
     657             : 
     658             : // For doubles, we can xor the two uint32s.
     659             : template <>
     660             : struct DefaultHasher<double>
     661             : {
     662             :     typedef double Lookup;
     663           0 :     static HashNumber hash(double d) {
     664             :         static_assert(sizeof(HashNumber) == 4,
     665             :                       "subsequent code assumes a four-byte hash");
     666           0 :         uint64_t u = mozilla::BitwiseCast<uint64_t>(d);
     667           0 :         return HashNumber(u ^ (u >> 32));
     668             :     }
     669           0 :     static bool match(double lhs, double rhs) {
     670           0 :         return mozilla::BitwiseCast<uint64_t>(lhs) == mozilla::BitwiseCast<uint64_t>(rhs);
     671             :     }
     672             : };
     673             : 
     674             : template <>
     675             : struct DefaultHasher<float>
     676             : {
     677             :     typedef float Lookup;
     678           0 :     static HashNumber hash(float f) {
     679             :         static_assert(sizeof(HashNumber) == 4,
     680             :                       "subsequent code assumes a four-byte hash");
     681           0 :         return HashNumber(mozilla::BitwiseCast<uint32_t>(f));
     682             :     }
     683           0 :     static bool match(float lhs, float rhs) {
     684           0 :         return mozilla::BitwiseCast<uint32_t>(lhs) == mozilla::BitwiseCast<uint32_t>(rhs);
     685             :     }
     686             : };
     687             : 
     688             : // A hash policy that compares C strings.
     689             : struct CStringHasher
     690             : {
     691             :     typedef const char* Lookup;
     692           0 :     static js::HashNumber hash(Lookup l) {
     693           0 :         return mozilla::HashString(l);
     694             :     }
     695           0 :     static bool match(const char* key, Lookup lookup) {
     696           0 :         return strcmp(key, lookup) == 0;
     697             :     }
     698             : };
     699             : 
     700             : // Fallible hashing interface.
     701             : //
     702             : // Most of the time generating a hash code is infallible so this class provides
     703             : // default methods that always succeed.  Specialize this class for your own hash
     704             : // policy to provide fallible hashing.
     705             : //
     706             : // This is used by MovableCellHasher to handle the fact that generating a unique
     707             : // ID for cell pointer may fail due to OOM.
     708             : template <typename HashPolicy>
     709             : struct FallibleHashMethods
     710             : {
     711             :     // Return true if a hashcode is already available for its argument.  Once
     712             :     // this returns true for a specific argument it must continue to do so.
     713     1941302 :     template <typename Lookup> static bool hasHash(Lookup&& l) { return true; }
     714             : 
     715             :     // Fallible method to ensure a hashcode exists for its argument and create
     716             :     // one if not.  Returns false on error, e.g. out of memory.
     717     1683814 :     template <typename Lookup> static bool ensureHash(Lookup&& l) { return true; }
     718             : };
     719             : 
     720             : template <typename HashPolicy, typename Lookup>
     721             : static bool
     722     1943313 : HasHash(Lookup&& l) {
     723     1943313 :     return FallibleHashMethods<typename HashPolicy::Base>::hasHash(mozilla::Forward<Lookup>(l));
     724             : }
     725             : 
     726             : template <typename HashPolicy, typename Lookup>
     727             : static bool
     728     1812051 : EnsureHash(Lookup&& l) {
     729     1812051 :     return FallibleHashMethods<typename HashPolicy::Base>::ensureHash(mozilla::Forward<Lookup>(l));
     730             : }
     731             : 
     732             : /*****************************************************************************/
     733             : 
     734             : // Both HashMap and HashSet are implemented by a single HashTable that is even
     735             : // more heavily parameterized than the other two. This leaves HashTable gnarly
     736             : // and extremely coupled to HashMap and HashSet; thus code should not use
     737             : // HashTable directly.
     738             : 
     739             : template <class Key, class Value>
     740       47151 : class HashMapEntry
     741             : {
     742             :     Key key_;
     743             :     Value value_;
     744             : 
     745             :     template <class, class, class> friend class detail::HashTable;
     746             :     template <class> friend class detail::HashTableEntry;
     747             :     template <class, class, class, class> friend class HashMap;
     748             : 
     749             :   public:
     750             :     template<typename KeyInput, typename ValueInput>
     751       71665 :     HashMapEntry(KeyInput&& k, ValueInput&& v)
     752       61823 :       : key_(mozilla::Forward<KeyInput>(k)),
     753       71665 :         value_(mozilla::Forward<ValueInput>(v))
     754       71665 :     {}
     755             : 
     756       52695 :     HashMapEntry(HashMapEntry&& rhs)
     757       52695 :       : key_(mozilla::Move(rhs.key_)),
     758       52695 :         value_(mozilla::Move(rhs.value_))
     759       52695 :     {}
     760             : 
     761           0 :     void operator=(HashMapEntry&& rhs) {
     762           0 :         key_ = mozilla::Move(rhs.key_);
     763           0 :         value_ = mozilla::Move(rhs.value_);
     764           0 :     }
     765             : 
     766             :     typedef Key KeyType;
     767             :     typedef Value ValueType;
     768             : 
     769     2127868 :     const Key& key() const { return key_; }
     770        9605 :     Key& mutableKey() { return key_; }
     771         835 :     const Value& value() const { return value_; }
     772     1809089 :     Value& value() { return value_; }
     773             : 
     774             :   private:
     775             :     HashMapEntry(const HashMapEntry&) = delete;
     776             :     void operator=(const HashMapEntry&) = delete;
     777             : };
     778             : 
     779             : } // namespace js
     780             : 
     781             : namespace mozilla {
     782             : 
     783             : template <typename T>
     784             : struct IsPod<js::detail::HashTableEntry<T> > : IsPod<T> {};
     785             : 
     786             : template <typename K, typename V>
     787             : struct IsPod<js::HashMapEntry<K, V> >
     788             :   : IntegralConstant<bool, IsPod<K>::value && IsPod<V>::value>
     789             : {};
     790             : 
     791             : } // namespace mozilla
     792             : 
     793             : namespace js {
     794             : 
     795             : namespace detail {
     796             : 
     797             : template <class T, class HashPolicy, class AllocPolicy>
     798             : class HashTable;
     799             : 
     800             : template <class T>
     801             : class HashTableEntry
     802             : {
     803             :     template <class, class, class> friend class HashTable;
     804             :     typedef typename mozilla::RemoveConst<T>::Type NonConstT;
     805             : 
     806             :     HashNumber keyHash;
     807             :     mozilla::AlignedStorage2<NonConstT> mem;
     808             : 
     809             :     static const HashNumber sFreeKey = 0;
     810             :     static const HashNumber sRemovedKey = 1;
     811             :     static const HashNumber sCollisionBit = 1;
     812             : 
     813    29202101 :     static bool isLiveHash(HashNumber hash)
     814             :     {
     815    29202101 :         return hash > sRemovedKey;
     816             :     }
     817             : 
     818             :     HashTableEntry(const HashTableEntry&) = delete;
     819             :     void operator=(const HashTableEntry&) = delete;
     820             :     ~HashTableEntry() = delete;
     821             : 
     822             :   public:
     823             :     // NB: HashTableEntry is treated as a POD: no constructor or destructor calls.
     824             : 
     825      104936 :     void destroyIfLive() {
     826      104936 :         if (isLive())
     827       24375 :             mem.addr()->~T();
     828      104936 :     }
     829             : 
     830      313192 :     void destroy() {
     831      313192 :         MOZ_ASSERT(isLive());
     832      313192 :         mem.addr()->~T();
     833      313192 :     }
     834             : 
     835           0 :     void swap(HashTableEntry* other) {
     836           0 :         if (this == other)
     837           0 :             return;
     838           0 :         MOZ_ASSERT(isLive());
     839           0 :         if (other->isLive()) {
     840           0 :             mozilla::Swap(*mem.addr(), *other->mem.addr());
     841             :         } else {
     842           0 :             *other->mem.addr() = mozilla::Move(*mem.addr());
     843           0 :             destroy();
     844             :         }
     845           0 :         mozilla::Swap(keyHash, other->keyHash);
     846             :     }
     847             : 
     848     6658999 :     T& get() { MOZ_ASSERT(isLive()); return *mem.addr(); }
     849           7 :     NonConstT& getMutable() { MOZ_ASSERT(isLive()); return *mem.addr(); }
     850             : 
     851     6258576 :     bool isFree() const    { return keyHash == sFreeKey; }
     852        6482 :     void clearLive()       { MOZ_ASSERT(isLive()); keyHash = sFreeKey; mem.addr()->~T(); }
     853      126352 :     void clear()           { if (isLive()) mem.addr()->~T(); keyHash = sFreeKey; }
     854     2913593 :     bool isRemoved() const { return keyHash == sRemovedKey; }
     855        1624 :     void removeLive()      { MOZ_ASSERT(isLive()); keyHash = sRemovedKey; mem.addr()->~T(); }
     856    21683317 :     bool isLive() const    { return isLiveHash(keyHash); }
     857     1354002 :     void setCollision()               { MOZ_ASSERT(isLive()); keyHash |= sCollisionBit; }
     858           0 :     void unsetCollision()             { keyHash &= ~sCollisionBit; }
     859        8106 :     bool hasCollision() const         { return keyHash & sCollisionBit; }
     860     5747248 :     bool matchHash(HashNumber hn)     { return (keyHash & ~sCollisionBit) == hn; }
     861      313192 :     HashNumber getKeyHash() const     { return keyHash & ~sCollisionBit; }
     862             : 
     863             :     template <typename... Args>
     864      610268 :     void setLive(HashNumber hn, Args&&... args)
     865             :     {
     866      610268 :         MOZ_ASSERT(!isLive());
     867      610268 :         keyHash = hn;
     868      610268 :         new(mem.addr()) T(mozilla::Forward<Args>(args)...);
     869      610268 :         MOZ_ASSERT(isLive());
     870      610268 :     }
     871             : };
     872             : 
     873             : template <class T, class HashPolicy, class AllocPolicy>
     874             : class HashTable : private AllocPolicy
     875             : {
     876             :     friend class mozilla::ReentrancyGuard;
     877             : 
     878             :     typedef typename mozilla::RemoveConst<T>::Type NonConstT;
     879             :     typedef typename HashPolicy::KeyType Key;
     880             :     typedef typename HashPolicy::Lookup Lookup;
     881             : 
     882             :   public:
     883             :     typedef HashTableEntry<T> Entry;
     884             : 
     885             :     // A nullable pointer to a hash table element. A Ptr |p| can be tested
     886             :     // either explicitly |if (p.found()) p->...| or using boolean conversion
     887             :     // |if (p) p->...|. Ptr objects must not be used after any mutating hash
     888             :     // table operations unless |generation()| is tested.
     889             :     class Ptr
     890             :     {
     891             :         friend class HashTable;
     892             : 
     893             :         Entry* entry_;
     894             : #ifdef JS_DEBUG
     895             :         const HashTable* table_;
     896             :         Generation generation;
     897             : #endif
     898             : 
     899             :       protected:
     900     3728918 :         Ptr(Entry& entry, const HashTable& tableArg)
     901             :           : entry_(&entry)
     902             : #ifdef JS_DEBUG
     903             :           , table_(&tableArg)
     904     3728918 :           , generation(tableArg.generation())
     905             : #endif
     906     3728924 :         {}
     907             : 
     908             :       public:
     909      409940 :         Ptr()
     910             :           : entry_(nullptr)
     911             : #ifdef JS_DEBUG
     912             :           , table_(nullptr)
     913      409940 :           , generation(0)
     914             : #endif
     915      409940 :         {}
     916             : 
     917     8246567 :         bool isValid() const {
     918     8246567 :             return !!entry_;
     919             :         }
     920             : 
     921     7703333 :         bool found() const {
     922     7703333 :             if (!isValid())
     923       30749 :                 return false;
     924             : #ifdef JS_DEBUG
     925     7672613 :             MOZ_ASSERT(generation == table_->generation());
     926             : #endif
     927     7672603 :             return entry_->isLive();
     928             :         }
     929             : 
     930     3577115 :         explicit operator bool() const {
     931     3577115 :             return found();
     932             :         }
     933             : 
     934             :         bool operator==(const Ptr& rhs) const {
     935             :             MOZ_ASSERT(found() && rhs.found());
     936             :             return entry_ == rhs.entry_;
     937             :         }
     938             : 
     939             :         bool operator!=(const Ptr& rhs) const {
     940             : #ifdef JS_DEBUG
     941             :             MOZ_ASSERT(generation == table_->generation());
     942             : #endif
     943             :             return !(*this == rhs);
     944             :         }
     945             : 
     946      215086 :         T& operator*() const {
     947             : #ifdef JS_DEBUG
     948      215086 :             MOZ_ASSERT(found());
     949      215086 :             MOZ_ASSERT(generation == table_->generation());
     950             : #endif
     951      215084 :             return entry_->get();
     952             :         }
     953             : 
     954     2652949 :         T* operator->() const {
     955             : #ifdef JS_DEBUG
     956     2652949 :             MOZ_ASSERT(found());
     957     2652926 :             MOZ_ASSERT(generation == table_->generation());
     958             : #endif
     959     2652870 :             return &entry_->get();
     960             :         }
     961             :     };
     962             : 
     963             :     // A Ptr that can be used to add a key after a failed lookup.
     964             :     class AddPtr : public Ptr
     965             :     {
     966             :         friend class HashTable;
     967             :         HashNumber keyHash;
     968             : #ifdef JS_DEBUG
     969             :         uint64_t mutationCount;
     970             : #endif
     971             : 
     972     1787798 :         AddPtr(Entry& entry, const HashTable& tableArg, HashNumber hn)
     973             :           : Ptr(entry, tableArg)
     974             :           , keyHash(hn)
     975             : #ifdef JS_DEBUG
     976     1787798 :           , mutationCount(tableArg.mutationCount)
     977             : #endif
     978     1787799 :         {}
     979             : 
     980             :       public:
     981      242579 :         AddPtr() : keyHash(0) {}
     982             :     };
     983             : 
     984             :     // A collection of hash table entries. The collection is enumerated by
     985             :     // calling |front()| followed by |popFront()| as long as |!empty()|. As
     986             :     // with Ptr/AddPtr, Range objects must not be used after any mutating hash
     987             :     // table operation unless the |generation()| is tested.
     988             :     class Range
     989             :     {
     990             :       protected:
     991             :         friend class HashTable;
     992             : 
     993       53795 :         Range(const HashTable& tableArg, Entry* c, Entry* e)
     994             :           : cur(c)
     995             :           , end(e)
     996             : #ifdef JS_DEBUG
     997             :           , table_(&tableArg)
     998       53795 :           , mutationCount(tableArg.mutationCount)
     999             :           , generation(tableArg.generation())
    1000      107590 :           , validEntry(true)
    1001             : #endif
    1002             :         {
    1003     3997215 :             while (cur < end && !cur->isLive())
    1004     1971710 :                 ++cur;
    1005       53795 :         }
    1006             : 
    1007             :         Entry* cur;
    1008             :         Entry* end;
    1009             : #ifdef JS_DEBUG
    1010             :         const HashTable* table_;
    1011             :         uint64_t mutationCount;
    1012             :         Generation generation;
    1013             :         bool validEntry;
    1014             : #endif
    1015             : 
    1016             :       public:
    1017       41145 :         Range()
    1018             :           : cur(nullptr)
    1019             :           , end(nullptr)
    1020             : #ifdef JS_DEBUG
    1021             :           , table_(nullptr)
    1022             :           , mutationCount(0)
    1023             :           , generation(0)
    1024       41145 :           , validEntry(false)
    1025             : #endif
    1026       41145 :         {}
    1027             : 
    1028      767042 :         bool empty() const {
    1029             : #ifdef JS_DEBUG
    1030      767042 :             MOZ_ASSERT(generation == table_->generation());
    1031      767042 :             MOZ_ASSERT(mutationCount == table_->mutationCount);
    1032             : #endif
    1033      767042 :             return cur == end;
    1034             :         }
    1035             : 
    1036      220710 :         T& front() const {
    1037      220710 :             MOZ_ASSERT(!empty());
    1038             : #ifdef JS_DEBUG
    1039      220710 :             MOZ_ASSERT(validEntry);
    1040      220710 :             MOZ_ASSERT(generation == table_->generation());
    1041      220710 :             MOZ_ASSERT(mutationCount == table_->mutationCount);
    1042             : #endif
    1043      220710 :             return cur->get();
    1044             :         }
    1045             : 
    1046      190779 :         void popFront() {
    1047      190779 :             MOZ_ASSERT(!empty());
    1048             : #ifdef JS_DEBUG
    1049      190779 :             MOZ_ASSERT(generation == table_->generation());
    1050      190779 :             MOZ_ASSERT(mutationCount == table_->mutationCount);
    1051             : #endif
    1052     2355753 :             while (++cur < end && !cur->isLive())
    1053     1082487 :                 continue;
    1054             : #ifdef JS_DEBUG
    1055      190779 :             validEntry = true;
    1056             : #endif
    1057      190779 :         }
    1058             :     };
    1059             : 
    1060             :     // A Range whose lifetime delimits a mutating enumeration of a hash table.
    1061             :     // Since rehashing when elements were removed during enumeration would be
    1062             :     // bad, it is postponed until the Enum is destructed.  Since the Enum's
    1063             :     // destructor touches the hash table, the user must ensure that the hash
    1064             :     // table is still alive when the destructor runs.
    1065             :     class Enum : public Range
    1066             :     {
    1067             :         friend class HashTable;
    1068             : 
    1069             :         HashTable& table_;
    1070             :         bool rekeyed;
    1071             :         bool removed;
    1072             : 
    1073             :         // Enum is movable but not copyable.
    1074             :         Enum(const Enum&) = delete;
    1075             :         void operator=(const Enum&) = delete;
    1076             : 
    1077             :       public:
    1078             :         template<class Map>
    1079        4049 :         explicit Enum(Map& map)
    1080        4049 :           : Range(map.all()), table_(map.impl), rekeyed(false), removed(false) {}
    1081             : 
    1082             :         MOZ_IMPLICIT Enum(Enum&& other)
    1083             :           : Range(other), table_(other.table_), rekeyed(other.rekeyed), removed(other.removed)
    1084             :         {
    1085             :             other.rekeyed = false;
    1086             :             other.removed = false;
    1087             :         }
    1088             : 
    1089             :         // Removes the |front()| element from the table, leaving |front()|
    1090             :         // invalid until the next call to |popFront()|. For example:
    1091             :         //
    1092             :         //   HashSet<int> s;
    1093             :         //   for (HashSet<int>::Enum e(s); !e.empty(); e.popFront())
    1094             :         //     if (e.front() == 42)
    1095             :         //       e.removeFront();
    1096          65 :         void removeFront() {
    1097          65 :             table_.remove(*this->cur);
    1098          65 :             removed = true;
    1099             : #ifdef JS_DEBUG
    1100          65 :             this->validEntry = false;
    1101          65 :             this->mutationCount = table_.mutationCount;
    1102             : #endif
    1103          65 :         }
    1104             : 
    1105           7 :         NonConstT& mutableFront() {
    1106           7 :             MOZ_ASSERT(!this->empty());
    1107             : #ifdef JS_DEBUG
    1108           7 :             MOZ_ASSERT(this->validEntry);
    1109           7 :             MOZ_ASSERT(this->generation == this->Range::table_->generation());
    1110           7 :             MOZ_ASSERT(this->mutationCount == this->Range::table_->mutationCount);
    1111             : #endif
    1112           7 :             return this->cur->getMutable();
    1113             :         }
    1114             : 
    1115             :         // Removes the |front()| element and re-inserts it into the table with
    1116             :         // a new key at the new Lookup position.  |front()| is invalid after
    1117             :         // this operation until the next call to |popFront()|.
    1118           0 :         void rekeyFront(const Lookup& l, const Key& k) {
    1119           0 :             MOZ_ASSERT(&k != &HashPolicy::getKey(this->cur->get()));
    1120           0 :             Ptr p(*this->cur, table_);
    1121           0 :             table_.rekeyWithoutRehash(p, l, k);
    1122           0 :             rekeyed = true;
    1123             : #ifdef JS_DEBUG
    1124           0 :             this->validEntry = false;
    1125           0 :             this->mutationCount = table_.mutationCount;
    1126             : #endif
    1127           0 :         }
    1128             : 
    1129           0 :         void rekeyFront(const Key& k) {
    1130           0 :             rekeyFront(k, k);
    1131           0 :         }
    1132             : 
    1133             :         // Potentially rehashes the table.
    1134        4049 :         ~Enum() {
    1135        4049 :             if (rekeyed) {
    1136           0 :                 table_.gen++;
    1137           0 :                 table_.checkOverRemoved();
    1138             :             }
    1139             : 
    1140        4049 :             if (removed)
    1141          39 :                 table_.compactIfUnderloaded();
    1142        4049 :         }
    1143             :     };
    1144             : 
    1145             :     // HashTable is movable
    1146        3195 :     HashTable(HashTable&& rhs)
    1147          74 :       : AllocPolicy(rhs)
    1148             :     {
    1149        3195 :         mozilla::PodAssign(this, &rhs);
    1150        3195 :         rhs.table = nullptr;
    1151        3195 :     }
    1152           8 :     void operator=(HashTable&& rhs) {
    1153           8 :         MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
    1154           8 :         if (table)
    1155           0 :             destroyTable(*this, table, capacity());
    1156           8 :         mozilla::PodAssign(this, &rhs);
    1157           8 :         rhs.table = nullptr;
    1158           8 :     }
    1159             : 
    1160             :   private:
    1161             :     // HashTable is not copyable or assignable
    1162             :     HashTable(const HashTable&) = delete;
    1163             :     void operator=(const HashTable&) = delete;
    1164             : 
    1165             :   private:
    1166             :     static const size_t CAP_BITS = 30;
    1167             : 
    1168             :   public:
    1169             :     uint64_t    gen:56;                 // entry storage generation number
    1170             :     uint64_t    hashShift:8;            // multiplicative hash shift
    1171             :     Entry*      table;                  // entry storage
    1172             :     uint32_t    entryCount;             // number of entries in table
    1173             :     uint32_t    removedCount;           // removed entry sentinels in table
    1174             : 
    1175             : #ifdef JS_DEBUG
    1176             :     uint64_t     mutationCount;
    1177             :     mutable bool mEntered;
    1178             :     // Note that some updates to these stats are not thread-safe. See the
    1179             :     // comment on the three-argument overloading of HashTable::lookup().
    1180             :     mutable struct Stats
    1181             :     {
    1182             :         uint32_t        searches;       // total number of table searches
    1183             :         uint32_t        steps;          // hash chain links traversed
    1184             :         uint32_t        hits;           // searches that found key
    1185             :         uint32_t        misses;         // searches that didn't find key
    1186             :         uint32_t        addOverRemoved; // adds that recycled a removed entry
    1187             :         uint32_t        removes;        // calls to remove
    1188             :         uint32_t        removeFrees;    // calls to remove that freed the entry
    1189             :         uint32_t        grows;          // table expansions
    1190             :         uint32_t        shrinks;        // table contractions
    1191             :         uint32_t        compresses;     // table compressions
    1192             :         uint32_t        rehashes;       // tombstone decontaminations
    1193             :     } stats;
    1194             : #   define METER(x) x
    1195             : #else
    1196             : #   define METER(x)
    1197             : #endif
    1198             : 
    1199             :     // The default initial capacity is 32 (enough to hold 16 elements), but it
    1200             :     // can be as low as 4.
    1201             :     static const unsigned sMinCapacityLog2 = 2;
    1202             :     static const unsigned sMinCapacity  = 1 << sMinCapacityLog2;
    1203             :     static const unsigned sMaxInit      = JS_BIT(CAP_BITS - 1);
    1204             :     static const unsigned sMaxCapacity  = JS_BIT(CAP_BITS);
    1205             :     static const unsigned sHashBits     = mozilla::tl::BitSize<HashNumber>::value;
    1206             : 
    1207             :     // Hash-table alpha is conceptually a fraction, but to avoid floating-point
    1208             :     // math we implement it as a ratio of integers.
    1209             :     static const uint8_t sAlphaDenominator = 4;
    1210             :     static const uint8_t sMinAlphaNumerator = 1; // min alpha: 1/4
    1211             :     static const uint8_t sMaxAlphaNumerator = 3; // max alpha: 3/4
    1212             : 
    1213             :     static const HashNumber sFreeKey = Entry::sFreeKey;
    1214             :     static const HashNumber sRemovedKey = Entry::sRemovedKey;
    1215             :     static const HashNumber sCollisionBit = Entry::sCollisionBit;
    1216             : 
    1217       14211 :     void setTableSizeLog2(unsigned sizeLog2)
    1218             :     {
    1219       14211 :         hashShift = sHashBits - sizeLog2;
    1220       14211 :     }
    1221             : 
    1222     7524002 :     static bool isLiveHash(HashNumber hash)
    1223             :     {
    1224     7524002 :         return Entry::isLiveHash(hash);
    1225             :     }
    1226             : 
    1227     3779032 :     static HashNumber prepareHash(const Lookup& l)
    1228             :     {
    1229     3779032 :         HashNumber keyHash = ScrambleHashCode(HashPolicy::hash(l));
    1230             : 
    1231             :         // Avoid reserved hash codes.
    1232     3779064 :         if (!isLiveHash(keyHash))
    1233       39664 :             keyHash -= (sRemovedKey + 1);
    1234     3779059 :         return keyHash & ~sCollisionBit;
    1235             :     }
    1236             : 
    1237             :     enum FailureBehavior { DontReportFailure = false, ReportFailure = true };
    1238             : 
    1239       14211 :     static Entry* createTable(AllocPolicy& alloc, uint32_t capacity,
    1240             :                               FailureBehavior reportFailure = ReportFailure)
    1241             :     {
    1242             :         static_assert(sFreeKey == 0,
    1243             :                       "newly-calloc'd tables have to be considered empty");
    1244       14211 :         if (reportFailure)
    1245       13196 :             return alloc.template pod_calloc<Entry>(capacity);
    1246             : 
    1247        1015 :         return alloc.template maybe_pod_calloc<Entry>(capacity);
    1248             :     }
    1249             : 
    1250             :     static Entry* maybeCreateTable(AllocPolicy& alloc, uint32_t capacity)
    1251             :     {
    1252             :         static_assert(sFreeKey == 0,
    1253             :                       "newly-calloc'd tables have to be considered empty");
    1254             :         return alloc.template maybe_pod_calloc<Entry>(capacity);
    1255             :     }
    1256             : 
    1257        2222 :     static void destroyTable(AllocPolicy& alloc, Entry* oldTable, uint32_t capacity)
    1258             :     {
    1259        2222 :         Entry* end = oldTable + capacity;
    1260      107158 :         for (Entry* e = oldTable; e < end; ++e)
    1261      104936 :             e->destroyIfLive();
    1262        2222 :         alloc.free_(oldTable);
    1263        2222 :     }
    1264             : 
    1265             :   public:
    1266       32051 :     explicit HashTable(AllocPolicy ap)
    1267             :       : AllocPolicy(ap)
    1268             :       , gen(0)
    1269             :       , hashShift(sHashBits)
    1270             :       , table(nullptr)
    1271             :       , entryCount(0)
    1272             :       , removedCount(0)
    1273             : #ifdef JS_DEBUG
    1274             :       , mutationCount(0)
    1275       32051 :       , mEntered(false)
    1276             : #endif
    1277       32051 :     {}
    1278             : 
    1279        9727 :     MOZ_MUST_USE bool init(uint32_t length)
    1280             :     {
    1281        9727 :         MOZ_ASSERT(!initialized());
    1282             : 
    1283             :         // Reject all lengths whose initial computed capacity would exceed
    1284             :         // sMaxCapacity.  Round that maximum length down to the nearest power
    1285             :         // of two for speedier code.
    1286        9727 :         if (MOZ_UNLIKELY(length > sMaxInit)) {
    1287           0 :             this->reportAllocOverflow();
    1288           0 :             return false;
    1289             :         }
    1290             : 
    1291             :         static_assert((sMaxInit * sAlphaDenominator) / sAlphaDenominator == sMaxInit,
    1292             :                       "multiplication in numerator below could overflow");
    1293             :         static_assert(sMaxInit * sAlphaDenominator <= UINT32_MAX - sMaxAlphaNumerator,
    1294             :                       "numerator calculation below could potentially overflow");
    1295             : 
    1296             :         // Compute the smallest capacity allowing |length| elements to be
    1297             :         // inserted without rehashing: ceil(length / max-alpha).  (Ceiling
    1298             :         // integral division: <http://stackoverflow.com/a/2745086>.)
    1299             :         uint32_t newCapacity =
    1300        9727 :             (length * sAlphaDenominator + sMaxAlphaNumerator - 1) / sMaxAlphaNumerator;
    1301        9727 :         if (newCapacity < sMinCapacity)
    1302        2529 :             newCapacity = sMinCapacity;
    1303             : 
    1304             :         // FIXME: use JS_CEILING_LOG2 when PGO stops crashing (bug 543034).
    1305        9727 :         uint32_t roundUp = sMinCapacity, roundUpLog2 = sMinCapacityLog2;
    1306       46093 :         while (roundUp < newCapacity) {
    1307       18183 :             roundUp <<= 1;
    1308       18183 :             ++roundUpLog2;
    1309             :         }
    1310             : 
    1311        9727 :         newCapacity = roundUp;
    1312        9727 :         MOZ_ASSERT(newCapacity >= length);
    1313        9727 :         MOZ_ASSERT(newCapacity <= sMaxCapacity);
    1314             : 
    1315        9727 :         table = createTable(*this, newCapacity);
    1316        9727 :         if (!table)
    1317           0 :             return false;
    1318             : 
    1319        9727 :         setTableSizeLog2(roundUpLog2);
    1320        9727 :         METER(memset(&stats, 0, sizeof(stats)));
    1321        9727 :         return true;
    1322             :     }
    1323             : 
    1324      207267 :     bool initialized() const
    1325             :     {
    1326      207267 :         return !!table;
    1327             :     }
    1328             : 
    1329       27305 :     ~HashTable()
    1330             :     {
    1331       27305 :         if (table)
    1332        2124 :             destroyTable(*this, table, capacity());
    1333       27305 :     }
    1334             : 
    1335             :   private:
    1336     4094592 :     HashNumber hash1(HashNumber hash0) const
    1337             :     {
    1338     4094592 :         return hash0 >> hashShift;
    1339             :     }
    1340             : 
    1341             :     struct DoubleHash
    1342             :     {
    1343             :         HashNumber h2;
    1344             :         HashNumber sizeMask;
    1345             :     };
    1346             : 
    1347     1190895 :     DoubleHash hash2(HashNumber curKeyHash) const
    1348             :     {
    1349     1190895 :         unsigned sizeLog2 = sHashBits - hashShift;
    1350             :         DoubleHash dh = {
    1351     1190895 :             ((curKeyHash << sizeLog2) >> hashShift) | 1,
    1352     1190895 :             (HashNumber(1) << sizeLog2) - 1
    1353     2381790 :         };
    1354     1190895 :         return dh;
    1355             :     }
    1356             : 
    1357     2616515 :     static HashNumber applyDoubleHash(HashNumber h1, const DoubleHash& dh)
    1358             :     {
    1359     2616515 :         return (h1 - dh.h2) & dh.sizeMask;
    1360             :     }
    1361             : 
    1362      290565 :     bool overloaded()
    1363             :     {
    1364             :         static_assert(sMaxCapacity <= UINT32_MAX / sMaxAlphaNumerator,
    1365             :                       "multiplication below could overflow");
    1366      290565 :         return entryCount + removedCount >=
    1367      290565 :                capacity() * sMaxAlphaNumerator / sAlphaDenominator;
    1368             :     }
    1369             : 
    1370             :     // Would the table be underloaded if it had the given capacity and entryCount?
    1371        5290 :     static bool wouldBeUnderloaded(uint32_t capacity, uint32_t entryCount)
    1372             :     {
    1373             :         static_assert(sMaxCapacity <= UINT32_MAX / sMinAlphaNumerator,
    1374             :                       "multiplication below could overflow");
    1375        8889 :         return capacity > sMinCapacity &&
    1376        8889 :                entryCount <= capacity * sMinAlphaNumerator / sAlphaDenominator;
    1377             :     }
    1378             : 
    1379        5074 :     bool underloaded()
    1380             :     {
    1381        5074 :         return wouldBeUnderloaded(capacity(), entryCount);
    1382             :     }
    1383             : 
    1384     3257393 :     static MOZ_ALWAYS_INLINE bool match(Entry& e, const Lookup& l)
    1385             :     {
    1386     3257393 :         return HashPolicy::match(HashPolicy::getKey(e.get()), l);
    1387             :     }
    1388             : 
    1389             :     // Warning: in order for readonlyThreadsafeLookup() to be safe this
    1390             :     // function must not modify the table in any way when |collisionBit| is 0.
    1391             :     // (The use of the METER() macro to increment stats violates this
    1392             :     // restriction but we will live with that for now because it's enabled so
    1393             :     // rarely.)
    1394             :     MOZ_ALWAYS_INLINE Entry&
    1395     3745435 :     lookup(const Lookup& l, HashNumber keyHash, unsigned collisionBit) const
    1396             :     {
    1397     3745435 :         MOZ_ASSERT(isLiveHash(keyHash));
    1398     3745288 :         MOZ_ASSERT(!(keyHash & sCollisionBit));
    1399     3745288 :         MOZ_ASSERT(collisionBit == 0 || collisionBit == sCollisionBit);
    1400     3745288 :         MOZ_ASSERT(table);
    1401     3745288 :         METER(stats.searches++);
    1402             : 
    1403             :         // Compute the primary hash address.
    1404     3745288 :         HashNumber h1 = hash1(keyHash);
    1405     3745369 :         Entry* entry = &table[h1];
    1406             : 
    1407             :         // Miss: return space for a new entry.
    1408     3745369 :         if (entry->isFree()) {
    1409      245522 :             METER(stats.misses++);
    1410      245522 :             return *entry;
    1411             :         }
    1412             : 
    1413             :         // Hit: return entry.
    1414     3499732 :         if (entry->matchHash(keyHash) && match(*entry, l)) {
    1415     2380459 :             METER(stats.hits++);
    1416     2380459 :             return *entry;
    1417             :         }
    1418             : 
    1419             :         // Collision: double hash.
    1420     1119364 :         DoubleHash dh = hash2(keyHash);
    1421             : 
    1422             :         // Save the first removed entry pointer so we can recycle later.
    1423     1119387 :         Entry* firstRemoved = nullptr;
    1424             : 
    1425             :         while (true) {
    1426     3907553 :             if (MOZ_UNLIKELY(entry->isRemoved())) {
    1427        1860 :                 if (!firstRemoved)
    1428        1668 :                     firstRemoved = entry;
    1429             :             } else {
    1430     2511565 :                 if (collisionBit == sCollisionBit)
    1431     1250900 :                     entry->setCollision();
    1432             :             }
    1433             : 
    1434     2513426 :             METER(stats.steps++);
    1435     2513426 :             h1 = applyDoubleHash(h1, dh);
    1436             : 
    1437     2513412 :             entry = &table[h1];
    1438     2513412 :             if (entry->isFree()) {
    1439      265726 :                 METER(stats.misses++);
    1440      265726 :                 return firstRemoved ? *firstRemoved : *entry;
    1441             :             }
    1442             : 
    1443     2247715 :             if (entry->matchHash(keyHash) && match(*entry, l)) {
    1444      853655 :                 METER(stats.hits++);
    1445      853655 :                 return *entry;
    1446             :             }
    1447             :         }
    1448             :     }
    1449             : 
    1450             :     // This is a copy of lookup hardcoded to the assumptions:
    1451             :     //   1. the lookup is a lookupForAdd
    1452             :     //   2. the key, whose |keyHash| has been passed is not in the table,
    1453             :     //   3. no entries have been removed from the table.
    1454             :     // This specialized search avoids the need for recovering lookup values
    1455             :     // from entries, which allows more flexible Lookup/Key types.
    1456      349293 :     Entry& findFreeEntry(HashNumber keyHash)
    1457             :     {
    1458      349293 :         MOZ_ASSERT(!(keyHash & sCollisionBit));
    1459      349293 :         MOZ_ASSERT(table);
    1460      349293 :         METER(stats.searches++);
    1461             : 
    1462             :         // We assume 'keyHash' has already been distributed.
    1463             : 
    1464             :         // Compute the primary hash address.
    1465      349293 :         HashNumber h1 = hash1(keyHash);
    1466      349293 :         Entry* entry = &table[h1];
    1467             : 
    1468             :         // Miss: return space for a new entry.
    1469      349293 :         if (!entry->isLive()) {
    1470      277790 :             METER(stats.misses++);
    1471      277790 :             return *entry;
    1472             :         }
    1473             : 
    1474             :         // Collision: double hash.
    1475       71503 :         DoubleHash dh = hash2(keyHash);
    1476             : 
    1477       31604 :         while (true) {
    1478      103107 :             MOZ_ASSERT(!entry->isRemoved());
    1479      103107 :             entry->setCollision();
    1480             : 
    1481      103107 :             METER(stats.steps++);
    1482      103107 :             h1 = applyDoubleHash(h1, dh);
    1483             : 
    1484      103107 :             entry = &table[h1];
    1485      103107 :             if (!entry->isLive()) {
    1486       71503 :                 METER(stats.misses++);
    1487       71503 :                 return *entry;
    1488             :             }
    1489             :         }
    1490             :     }
    1491             : 
    1492             :     enum RebuildStatus { NotOverloaded, Rehashed, RehashFailed };
    1493             : 
    1494        4483 :     RebuildStatus changeTableSize(int deltaLog2, FailureBehavior reportFailure = ReportFailure)
    1495             :     {
    1496             :         // Look, but don't touch, until we succeed in getting new entry store.
    1497        4483 :         Entry* oldTable = table;
    1498        4483 :         uint32_t oldCap = capacity();
    1499        4483 :         uint32_t newLog2 = sHashBits - hashShift + deltaLog2;
    1500        4483 :         uint32_t newCapacity = JS_BIT(newLog2);
    1501        4483 :         if (MOZ_UNLIKELY(newCapacity > sMaxCapacity)) {
    1502           0 :             if (reportFailure)
    1503           0 :                 this->reportAllocOverflow();
    1504           0 :             return RehashFailed;
    1505             :         }
    1506             : 
    1507        4483 :         Entry* newTable = createTable(*this, newCapacity, reportFailure);
    1508        4483 :         if (!newTable)
    1509           0 :             return RehashFailed;
    1510             : 
    1511             :         // We can't fail from here on, so update table parameters.
    1512        4483 :         setTableSizeLog2(newLog2);
    1513        4483 :         removedCount = 0;
    1514        4483 :         gen++;
    1515        4483 :         table = newTable;
    1516             : 
    1517             :         // Copy only live entries, leaving removed ones behind.
    1518        4483 :         Entry* end = oldTable + oldCap;
    1519      533815 :         for (Entry* src = oldTable; src < end; ++src) {
    1520      529332 :             if (src->isLive()) {
    1521      313192 :                 HashNumber hn = src->getKeyHash();
    1522      313192 :                 findFreeEntry(hn).setLive(
    1523      313192 :                     hn, mozilla::Move(const_cast<typename Entry::NonConstT&>(src->get())));
    1524      313192 :                 src->destroy();
    1525             :             }
    1526             :         }
    1527             : 
    1528             :         // All entries have been destroyed, no need to destroyTable.
    1529        4483 :         this->free_(oldTable);
    1530        4483 :         return Rehashed;
    1531             :     }
    1532             : 
    1533        3592 :     bool shouldCompressTable()
    1534             :     {
    1535             :         // Compress if a quarter or more of all entries are removed.
    1536        3592 :         return removedCount >= (capacity() >> 2);
    1537             :     }
    1538             : 
    1539      287598 :     RebuildStatus checkOverloaded(FailureBehavior reportFailure = ReportFailure)
    1540             :     {
    1541      287598 :         if (!overloaded())
    1542      284006 :             return NotOverloaded;
    1543             : 
    1544             :         int deltaLog2;
    1545        3592 :         if (shouldCompressTable()) {
    1546          54 :             METER(stats.compresses++);
    1547          54 :             deltaLog2 = 0;
    1548             :         } else {
    1549        3538 :             METER(stats.grows++);
    1550        3538 :             deltaLog2 = 1;
    1551             :         }
    1552             : 
    1553        3592 :         return changeTableSize(deltaLog2, reportFailure);
    1554             :     }
    1555             : 
    1556             :     // Infallibly rehash the table if we are overloaded with removals.
    1557        2967 :     void checkOverRemoved()
    1558             :     {
    1559        2967 :         if (overloaded()) {
    1560         124 :             if (checkOverloaded(DontReportFailure) == RehashFailed)
    1561           0 :                 rehashTableInPlace();
    1562             :         }
    1563        2967 :     }
    1564             : 
    1565        8106 :     void remove(Entry& e)
    1566             :     {
    1567        8106 :         MOZ_ASSERT(table);
    1568        8106 :         METER(stats.removes++);
    1569             : 
    1570        8106 :         if (e.hasCollision()) {
    1571        1624 :             e.removeLive();
    1572        1624 :             removedCount++;
    1573             :         } else {
    1574        6482 :             METER(stats.removeFrees++);
    1575        6482 :             e.clearLive();
    1576             :         }
    1577        8106 :         entryCount--;
    1578             : #ifdef JS_DEBUG
    1579        8106 :         mutationCount++;
    1580             : #endif
    1581        8106 :     }
    1582             : 
    1583        5074 :     void checkUnderloaded()
    1584             :     {
    1585        5074 :         if (underloaded()) {
    1586         867 :             METER(stats.shrinks++);
    1587         867 :             (void) changeTableSize(-1, DontReportFailure);
    1588             :         }
    1589        5074 :     }
    1590             : 
    1591             :     // Resize the table down to the largest capacity which doesn't underload the
    1592             :     // table.  Since we call checkUnderloaded() on every remove, you only need
    1593             :     // to call this after a bulk removal of items done without calling remove().
    1594          55 :     void compactIfUnderloaded()
    1595             :     {
    1596          55 :         int32_t resizeLog2 = 0;
    1597          55 :         uint32_t newCapacity = capacity();
    1598         377 :         while (wouldBeUnderloaded(newCapacity, entryCount)) {
    1599         161 :             newCapacity = newCapacity >> 1;
    1600         161 :             resizeLog2--;
    1601             :         }
    1602             : 
    1603          55 :         if (resizeLog2 != 0)
    1604          24 :             (void) changeTableSize(resizeLog2, DontReportFailure);
    1605          55 :     }
    1606             : 
    1607             :     // This is identical to changeTableSize(currentSize), but without requiring
    1608             :     // a second table.  We do this by recycling the collision bits to tell us if
    1609             :     // the element is already inserted or still waiting to be inserted.  Since
    1610             :     // already-inserted elements win any conflicts, we get the same table as we
    1611             :     // would have gotten through random insertion order.
    1612           0 :     void rehashTableInPlace()
    1613             :     {
    1614           0 :         METER(stats.rehashes++);
    1615           0 :         removedCount = 0;
    1616           0 :         gen++;
    1617           0 :         for (size_t i = 0; i < capacity(); ++i)
    1618           0 :             table[i].unsetCollision();
    1619             : 
    1620           0 :         for (size_t i = 0; i < capacity();) {
    1621           0 :             Entry* src = &table[i];
    1622             : 
    1623           0 :             if (!src->isLive() || src->hasCollision()) {
    1624           0 :                 ++i;
    1625           0 :                 continue;
    1626             :             }
    1627             : 
    1628           0 :             HashNumber keyHash = src->getKeyHash();
    1629           0 :             HashNumber h1 = hash1(keyHash);
    1630           0 :             DoubleHash dh = hash2(keyHash);
    1631           0 :             Entry* tgt = &table[h1];
    1632             :             while (true) {
    1633           0 :                 if (!tgt->hasCollision()) {
    1634           0 :                     src->swap(tgt);
    1635           0 :                     tgt->setCollision();
    1636           0 :                     break;
    1637             :                 }
    1638             : 
    1639           0 :                 h1 = applyDoubleHash(h1, dh);
    1640           0 :                 tgt = &table[h1];
    1641             :             }
    1642             :         }
    1643             : 
    1644             :         // TODO: this algorithm leaves collision bits on *all* elements, even if
    1645             :         // they are on no collision path. We have the option of setting the
    1646             :         // collision bits correctly on a subsequent pass or skipping the rehash
    1647             :         // unless we are totally filled with tombstones: benchmark to find out
    1648             :         // which approach is best.
    1649           0 :     }
    1650             : 
    1651             :     // Note: |l| may be a reference to a piece of |u|, so this function
    1652             :     // must take care not to use |l| after moving |u|.
    1653             :     //
    1654             :     // Prefer to use putNewInfallible; this function does not check
    1655             :     // invariants.
    1656             :     template <typename... Args>
    1657       33621 :     void putNewInfallibleInternal(const Lookup& l, Args&&... args)
    1658             :     {
    1659       33621 :         MOZ_ASSERT(table);
    1660             : 
    1661       33621 :         HashNumber keyHash = prepareHash(l);
    1662       33621 :         Entry* entry = &findFreeEntry(keyHash);
    1663       33621 :         MOZ_ASSERT(entry);
    1664             : 
    1665       33621 :         if (entry->isRemoved()) {
    1666         257 :             METER(stats.addOverRemoved++);
    1667         257 :             removedCount--;
    1668         257 :             keyHash |= sCollisionBit;
    1669             :         }
    1670             : 
    1671       33621 :         entry->setLive(keyHash, mozilla::Forward<Args>(args)...);
    1672       33621 :         entryCount++;
    1673             : #ifdef JS_DEBUG
    1674       33621 :         mutationCount++;
    1675             : #endif
    1676       33621 :     }
    1677             : 
    1678             :   public:
    1679         815 :     void clear()
    1680             :     {
    1681             :         if (mozilla::IsPod<Entry>::value) {
    1682         574 :             memset(table, 0, sizeof(*table) * capacity());
    1683             :         } else {
    1684         241 :             uint32_t tableCapacity = capacity();
    1685         241 :             Entry* end = table + tableCapacity;
    1686      126593 :             for (Entry* e = table; e < end; ++e)
    1687      126352 :                 e->clear();
    1688             :         }
    1689         815 :         removedCount = 0;
    1690         815 :         entryCount = 0;
    1691             : #ifdef JS_DEBUG
    1692         815 :         mutationCount++;
    1693             : #endif
    1694         815 :     }
    1695             : 
    1696          16 :     void clearAndShrink()
    1697             :     {
    1698          16 :         clear();
    1699          16 :         compactIfUnderloaded();
    1700          16 :     }
    1701             : 
    1702         121 :     void finish()
    1703             :     {
    1704             : #ifdef JS_DEBUG
    1705         121 :         MOZ_ASSERT(!mEntered);
    1706             : #endif
    1707         121 :         if (!table)
    1708          23 :             return;
    1709             : 
    1710          98 :         destroyTable(*this, table, capacity());
    1711          98 :         table = nullptr;
    1712          98 :         gen++;
    1713          98 :         entryCount = 0;
    1714          98 :         removedCount = 0;
    1715             : #ifdef JS_DEBUG
    1716          98 :         mutationCount++;
    1717             : #endif
    1718             :     }
    1719             : 
    1720       53795 :     Range all() const
    1721             :     {
    1722       53795 :         MOZ_ASSERT(table);
    1723       53795 :         return Range(*this, table, table + capacity());
    1724             :     }
    1725             : 
    1726       13903 :     bool empty() const
    1727             :     {
    1728       13903 :         MOZ_ASSERT(table);
    1729       13903 :         return !entryCount;
    1730             :     }
    1731             : 
    1732       52075 :     uint32_t count() const
    1733             :     {
    1734       52075 :         MOZ_ASSERT(table);
    1735       52075 :         return entryCount;
    1736             :     }
    1737             : 
    1738      360615 :     uint32_t capacity() const
    1739             :     {
    1740      360615 :         MOZ_ASSERT(table);
    1741      360615 :         return JS_BIT(sHashBits - hashShift);
    1742             :     }
    1743             : 
    1744    16050841 :     Generation generation() const
    1745             :     {
    1746    16050841 :         MOZ_ASSERT(table);
    1747    16050841 :         return Generation(gen);
    1748             :     }
    1749             : 
    1750           0 :     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
    1751             :     {
    1752           0 :         return mallocSizeOf(table);
    1753             :     }
    1754             : 
    1755             :     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
    1756             :     {
    1757             :         return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
    1758             :     }
    1759             : 
    1760     1813968 :     MOZ_ALWAYS_INLINE Ptr lookup(const Lookup& l) const
    1761             :     {
    1762     3627923 :         mozilla::ReentrancyGuard g(*this);
    1763     1813967 :         if (!HasHash<HashPolicy>(l))
    1764        2427 :             return Ptr();
    1765     1811539 :         HashNumber keyHash = prepareHash(l);
    1766     1811541 :         return Ptr(lookup(l, keyHash, 0), *this);
    1767             :     }
    1768             : 
    1769      129412 :     MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& l) const
    1770             :     {
    1771      129412 :         if (!HasHash<HashPolicy>(l))
    1772           0 :             return Ptr();
    1773      129412 :         HashNumber keyHash = prepareHash(l);
    1774      129412 :         return Ptr(lookup(l, keyHash, 0), *this);
    1775             :     }
    1776             : 
    1777     1787804 :     MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& l) const
    1778             :     {
    1779     3575643 :         mozilla::ReentrancyGuard g(*this);
    1780     1787807 :         if (!EnsureHash<HashPolicy>(l))
    1781           0 :             return AddPtr();
    1782     1787809 :         HashNumber keyHash = prepareHash(l);
    1783     1787807 :         Entry& entry = lookup(l, keyHash, sCollisionBit);
    1784     1787801 :         AddPtr p(entry, *this, keyHash);
    1785     1787839 :         return p;
    1786             :     }
    1787             : 
    1788             :     template <typename... Args>
    1789      263454 :     MOZ_MUST_USE bool add(AddPtr& p, Args&&... args)
    1790             :     {
    1791      526909 :         mozilla::ReentrancyGuard g(*this);
    1792      263454 :         MOZ_ASSERT(table);
    1793      263454 :         MOZ_ASSERT_IF(p.isValid(), p.table_ == this);
    1794      263454 :         MOZ_ASSERT(!p.found());
    1795      263455 :         MOZ_ASSERT(!(p.keyHash & sCollisionBit));
    1796             : 
    1797             :         // Check for error from ensureHash() here.
    1798      263455 :         if (!p.isValid())
    1799           0 :             return false;
    1800             : 
    1801      263455 :         MOZ_ASSERT(p.generation == generation());
    1802      263455 :         MOZ_ASSERT(p.mutationCount == mutationCount);
    1803             : 
    1804             :         // Changing an entry from removed to live does not affect whether we
    1805             :         // are overloaded and can be handled separately.
    1806      263455 :         if (p.entry_->isRemoved()) {
    1807         232 :             if (!this->checkSimulatedOOM())
    1808           0 :                 return false;
    1809         232 :             METER(stats.addOverRemoved++);
    1810         232 :             removedCount--;
    1811         232 :             p.keyHash |= sCollisionBit;
    1812             :         } else {
    1813             :             // Preserve the validity of |p.entry_|.
    1814      263223 :             RebuildStatus status = checkOverloaded();
    1815      263223 :             if (status == RehashFailed)
    1816           0 :                 return false;
    1817      263223 :             if (status == NotOverloaded && !this->checkSimulatedOOM())
    1818           0 :                 return false;
    1819      263223 :             if (status == Rehashed)
    1820        2480 :                 p.entry_ = &findFreeEntry(p.keyHash);
    1821             :         }
    1822             : 
    1823      263455 :         p.entry_->setLive(p.keyHash, mozilla::Forward<Args>(args)...);
    1824      263455 :         entryCount++;
    1825             : #ifdef JS_DEBUG
    1826      263455 :         mutationCount++;
    1827      263455 :         p.generation = generation();
    1828      263455 :         p.mutationCount = mutationCount;
    1829             : #endif
    1830      263455 :         return true;
    1831             :     }
    1832             : 
    1833             :     // Note: |l| may be a reference to a piece of |u|, so this function
    1834             :     // must take care not to use |l| after moving |u|.
    1835             :     template <typename... Args>
    1836       30654 :     void putNewInfallible(const Lookup& l, Args&&... args)
    1837             :     {
    1838       30654 :         MOZ_ASSERT(!lookup(l).found());
    1839       61308 :         mozilla::ReentrancyGuard g(*this);
    1840       30654 :         putNewInfallibleInternal(l, mozilla::Forward<Args>(args)...);
    1841       30654 :     }
    1842             : 
    1843             :     // Note: |l| may be alias arguments in |args|, so this function must take
    1844             :     // care not to use |l| after moving |args|.
    1845             :     template <typename... Args>
    1846       24247 :     MOZ_MUST_USE bool putNew(const Lookup& l, Args&&... args)
    1847             :     {
    1848       24247 :         if (!this->checkSimulatedOOM())
    1849           0 :             return false;
    1850             : 
    1851       24247 :         if (!EnsureHash<HashPolicy>(l))
    1852           0 :             return false;
    1853             : 
    1854       24247 :         if (checkOverloaded() == RehashFailed)
    1855           0 :             return false;
    1856             : 
    1857       24247 :         putNewInfallible(l, mozilla::Forward<Args>(args)...);
    1858       24247 :         return true;
    1859             :     }
    1860             : 
    1861             :     // Note: |l| may be a reference to a piece of |u|, so this function
    1862             :     // must take care not to use |l| after moving |u|.
    1863             :     template <typename... Args>
    1864       16413 :     MOZ_MUST_USE bool relookupOrAdd(AddPtr& p, const Lookup& l, Args&&... args)
    1865             :     {
    1866             :         // Check for error from ensureHash() here.
    1867       16413 :         if (!p.isValid())
    1868           0 :             return false;
    1869             : 
    1870             : #ifdef JS_DEBUG
    1871       16413 :         p.generation = generation();
    1872       16413 :         p.mutationCount = mutationCount;
    1873             : #endif
    1874             :         {
    1875       32826 :             mozilla::ReentrancyGuard g(*this);
    1876       16413 :             MOZ_ASSERT(prepareHash(l) == p.keyHash); // l has not been destroyed
    1877       16413 :             p.entry_ = &lookup(l, p.keyHash, sCollisionBit);
    1878             :         }
    1879       16413 :         return p.found() || add(p, mozilla::Forward<Args>(args)...);
    1880             :     }
    1881             : 
    1882        5074 :     void remove(Ptr p)
    1883             :     {
    1884        5074 :         MOZ_ASSERT(table);
    1885       10148 :         mozilla::ReentrancyGuard g(*this);
    1886        5074 :         MOZ_ASSERT(p.found());
    1887        5074 :         MOZ_ASSERT(p.generation == generation());
    1888        5074 :         remove(*p.entry_);
    1889        5074 :         checkUnderloaded();
    1890        5074 :     }
    1891             : 
    1892        2967 :     void rekeyWithoutRehash(Ptr p, const Lookup& l, const Key& k)
    1893             :     {
    1894        2967 :         MOZ_ASSERT(table);
    1895        5934 :         mozilla::ReentrancyGuard g(*this);
    1896        2967 :         MOZ_ASSERT(p.found());
    1897        2967 :         MOZ_ASSERT(p.generation == generation());
    1898        5068 :         typename HashTableEntry<T>::NonConstT t(mozilla::Move(*p));
    1899        2967 :         HashPolicy::setKey(t, const_cast<Key&>(k));
    1900        2967 :         remove(*p.entry_);
    1901        2967 :         putNewInfallibleInternal(l, mozilla::Move(t));
    1902        2967 :     }
    1903             : 
    1904        2967 :     void rekeyAndMaybeRehash(Ptr p, const Lookup& l, const Key& k)
    1905             :     {
    1906        2967 :         rekeyWithoutRehash(p, l, k);
    1907        2967 :         checkOverRemoved();
    1908        2967 :     }
    1909             : 
    1910             : #undef METER
    1911             : };
    1912             : 
    1913             : } // namespace detail
    1914             : } // namespace js
    1915             : 
    1916             : #endif  /* js_HashTable_h */

Generated by: LCOV version 1.13