Bug 1480323 - Reorder methods in Hash{Set,Map}. r=luke draft
authorNicholas Nethercote <nnethercote@mozilla.com>
Fri, 03 Aug 2018 09:59:53 +1000
changeset 826135 ba2db23368130767843eb2fab012ef87b946452b
parent 826134 5b878f64a697a9e95ee56257aa293d31fa5838de
push id118244
push usernnethercote@mozilla.com
push dateFri, 03 Aug 2018 00:00:34 +0000
reviewersluke
bugs1480323
milestone63.0a1
Bug 1480323 - Reorder methods in Hash{Set,Map}. r=luke And add comments delimiting different operation groups. Together these changes make Hash{Set,Map} much easier to navigate. MozReview-Commit-ID: 9DrgHNhR9wg
mfbt/HashTable.h
--- a/mfbt/HashTable.h
+++ b/mfbt/HashTable.h
@@ -136,16 +136,22 @@ using Generation = Opaque<uint64_t>;
 // - Due to the lack of exception handling, the user must call |init()|.
 //
 template<class Key,
          class Value,
          class HashPolicy = DefaultHasher<Key>,
          class AllocPolicy = MallocAllocPolicy>
 class HashMap
 {
+  // -- Implementation details -----------------------------------------------
+
+  // HashMap is not copyable or assignable.
+  HashMap(const HashMap& hm) = delete;
+  HashMap& operator=(const HashMap& hm) = delete;
+
   using TableEntry = HashMapEntry<Key, Value>;
 
   struct MapHashPolicy : HashPolicy
   {
     using Base = HashPolicy;
     using KeyType = Key;
 
     static const Key& getKey(TableEntry& aEntry) { return aEntry.key(); }
@@ -154,34 +160,85 @@ class HashMap
     {
       HashPolicy::rekey(aEntry.mutableKey(), aKey);
     }
   };
 
   using Impl = detail::HashTable<TableEntry, MapHashPolicy, AllocPolicy>;
   Impl mImpl;
 
+  friend class Impl::Enum;
+
 public:
   using Lookup = typename HashPolicy::Lookup;
   using Entry = TableEntry;
 
+  // -- Initialization -------------------------------------------------------
+
   // HashMap construction is fallible (due to possible OOM). The user must
   // call init() after construction and check the return value.
   explicit HashMap(AllocPolicy aPolicy = AllocPolicy())
     : mImpl(aPolicy)
   {
   }
 
+  // HashMap is movable.
+  HashMap(HashMap&& aRhs)
+    : mImpl(std::move(aRhs.mImpl))
+  {
+  }
+  void operator=(HashMap&& aRhs)
+  {
+    MOZ_ASSERT(this != &aRhs, "self-move assignment is prohibited");
+    mImpl = std::move(aRhs.mImpl);
+  }
+
   // Initialize the map for use. Must be called after construction, before
   // any other operations (other than initialized()).
   MOZ_MUST_USE bool init(uint32_t aLen = 16) { return mImpl.init(aLen); }
 
+  // -- Status and sizing ----------------------------------------------------
+
   // Has the map been initialized?
   bool initialized() const { return mImpl.initialized(); }
 
+  // The map's current generation.
+  Generation generation() const { return mImpl.generation(); }
+
+  // Is the map empty?
+  bool empty() const { return mImpl.empty(); }
+
+  // Number of keys/values in the map.
+  uint32_t count() const { return mImpl.count(); }
+
+  // Number of key/value slots in the map. Note: resize will happen well before
+  // count() == capacity().
+  size_t capacity() const { return mImpl.capacity(); }
+
+  // The size of the map's entry storage, in bytes. If the keys/values contain
+  // pointers to other heap blocks, you must iterate over the map and measure
+  // them separately; hence the "shallow" prefix.
+  size_t shallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return mImpl.shallowSizeOfExcludingThis(aMallocSizeOf);
+  }
+  size_t shallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) +
+           mImpl.shallowSizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  // -- Lookups --------------------------------------------------------------
+
+  // Does the map contain a key/value matching |aLookup|?
+  bool has(const Lookup& aLookup) const
+  {
+    return mImpl.lookup(aLookup).found();
+  }
+
   // Return a Ptr indicating whether a key/value matching |aLookup| is
   // present in the map. E.g.:
   //
   //   using HM = HashMap<int,char>;
   //   HM h;
   //   if (HM::Ptr p = h.lookup(3)) {
   //     assert(p->key() == 3);
   //     char val = p->value();
@@ -195,19 +252,61 @@ public:
 
   // Like lookup(), but does not assert if two threads call it at the same
   // time. Only use this method when none of the threads will modify the map.
   MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& aLookup) const
   {
     return mImpl.readonlyThreadsafeLookup(aLookup);
   }
 
-  // Remove a previously found key/value (assuming aPtr.found()). The map
-  // must not have been mutated in the interim.
-  void remove(Ptr aPtr) { mImpl.remove(aPtr); }
+  // -- Insertions -----------------------------------------------------------
+
+  // Overwrite existing value with |aValue|, or add it if not present. Returns
+  // false on OOM.
+  template<typename KeyInput, typename ValueInput>
+  MOZ_MUST_USE bool put(KeyInput&& aKey, ValueInput&& aValue)
+  {
+    AddPtr p = lookupForAdd(aKey);
+    if (p) {
+      p->value() = std::forward<ValueInput>(aValue);
+      return true;
+    }
+    return add(
+      p, std::forward<KeyInput>(aKey), std::forward<ValueInput>(aValue));
+  }
+
+  // Like put(), but asserts that the given key is not already present.
+  template<typename KeyInput, typename ValueInput>
+  MOZ_MUST_USE bool putNew(KeyInput&& aKey, ValueInput&& aValue)
+  {
+    return mImpl.putNew(
+      aKey, std::forward<KeyInput>(aKey), std::forward<ValueInput>(aValue));
+  }
+
+  // Only call this to populate an empty map after reserving space with init().
+  template<typename KeyInput, typename ValueInput>
+  void putNewInfallible(KeyInput&& aKey, ValueInput&& aValue)
+  {
+    mImpl.putNewInfallible(
+      aKey, std::forward<KeyInput>(aKey), std::forward<ValueInput>(aValue));
+  }
+
+  // Add (aKey,aDefaultValue) if |aKey| is not found. Return a false-y Ptr on
+  // OOM.
+  Ptr lookupWithDefault(const Key& aKey, const Value& aDefaultValue)
+  {
+    AddPtr p = lookupForAdd(aKey);
+    if (p) {
+      return p;
+    }
+    bool ok = add(p, aKey, aDefaultValue);
+    MOZ_ASSERT_IF(!ok, !p); // p is left false-y on OOM.
+    (void)ok;
+    return p;
+  }
 
   // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
   // insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using
   // |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new key/value. E.g.:
   //
   //   using HM = HashMap<int,char>;
   //   HM h;
   //   HM::AddPtr p = h.lookupForAdd(3);
@@ -264,16 +363,55 @@ public:
                                   ValueInput&& aValue)
   {
     return mImpl.relookupOrAdd(aPtr,
                                aKey,
                                std::forward<KeyInput>(aKey),
                                std::forward<ValueInput>(aValue));
   }
 
+  // -- Removal --------------------------------------------------------------
+
+  // Lookup and remove the key/value matching |aLookup|, if present.
+  void remove(const Lookup& aLookup)
+  {
+    if (Ptr p = lookup(aLookup)) {
+      remove(p);
+    }
+  }
+
+  // Remove a previously found key/value (assuming aPtr.found()). The map must
+  // not have been mutated in the interim.
+  void remove(Ptr aPtr) { mImpl.remove(aPtr); }
+
+  // -- Rekeying -------------------------------------------------------------
+
+  // Infallibly rekey one entry, if necessary. Requires that template
+  // parameters Key and HashPolicy::Lookup are the same type.
+  void rekeyIfMoved(const Key& aOldKey, const Key& aNewKey)
+  {
+    if (aOldKey != aNewKey) {
+      rekeyAs(aOldKey, aNewKey, aNewKey);
+    }
+  }
+
+  // Infallibly rekey one entry if present, and return whether that happened.
+  bool rekeyAs(const Lookup& aOldLookup,
+               const Lookup& aNewLookup,
+               const Key& aNewKey)
+  {
+    if (Ptr p = lookup(aOldLookup)) {
+      mImpl.rekeyAndMaybeRehash(p, aNewLookup, aNewKey);
+      return true;
+    }
+    return false;
+  }
+
+  // -- Iteration ------------------------------------------------------------
+
   // |iter()| returns an Iterator:
   //
   //   HashMap<int, char> h;
   //   for (auto iter = h.iter(); !iter.done(); iter.next()) {
   //     char c = iter.get().value();
   //   }
   //
   using Iterator = typename Impl::Iterator;
@@ -293,150 +431,27 @@ public:
   ModIterator modIter() { return mImpl.modIter(); }
 
   // These are similar to Iterator/ModIterator/iter(), but use different
   // terminology.
   using Range = typename Impl::Range;
   using Enum = typename Impl::Enum;
   Range all() const { return mImpl.all(); }
 
+  // -- Clearing -------------------------------------------------------------
+
   // Remove all keys/values without changing the capacity.
   void clear() { mImpl.clear(); }
 
   // Remove all keys/values and attempt to minimize the capacity.
   void clearAndShrink() { mImpl.clearAndShrink(); }
 
   // Remove all keys/values and release entry storage. The map must be
   // initialized via init() again before further use.
   void finish() { mImpl.finish(); }
-
-  // Is the map empty?
-  bool empty() const { return mImpl.empty(); }
-
-  // Number of keys/values in the map.
-  uint32_t count() const { return mImpl.count(); }
-
-  // Number of key/value slots in the map. Note: resize will happen well before
-  // count() == capacity().
-  size_t capacity() const { return mImpl.capacity(); }
-
-  // The size of the map's entry storage, in bytes. If the keys/values contain
-  // pointers to other heap blocks, you must iterate over the map and measure
-  // them separately; hence the "shallow" prefix.
-  size_t shallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
-  {
-    return mImpl.shallowSizeOfExcludingThis(aMallocSizeOf);
-  }
-  size_t shallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
-  {
-    return aMallocSizeOf(this) +
-           mImpl.shallowSizeOfExcludingThis(aMallocSizeOf);
-  }
-
-  // The map's current generation.
-  Generation generation() const { return mImpl.generation(); }
-
-  /************************************************** Shorthand operations */
-
-  // Does the map contain a key/value matching |aLookup|?
-  bool has(const Lookup& aLookup) const
-  {
-    return mImpl.lookup(aLookup).found();
-  }
-
-  // Overwrite existing value with |aValue|, or add it if not present. Returns
-  // false on OOM.
-  template<typename KeyInput, typename ValueInput>
-  MOZ_MUST_USE bool put(KeyInput&& aKey, ValueInput&& aValue)
-  {
-    AddPtr p = lookupForAdd(aKey);
-    if (p) {
-      p->value() = std::forward<ValueInput>(aValue);
-      return true;
-    }
-    return add(
-      p, std::forward<KeyInput>(aKey), std::forward<ValueInput>(aValue));
-  }
-
-  // Like put(), but asserts that the given key is not already present.
-  template<typename KeyInput, typename ValueInput>
-  MOZ_MUST_USE bool putNew(KeyInput&& aKey, ValueInput&& aValue)
-  {
-    return mImpl.putNew(
-      aKey, std::forward<KeyInput>(aKey), std::forward<ValueInput>(aValue));
-  }
-
-  // Only call this to populate an empty map after reserving space with init().
-  template<typename KeyInput, typename ValueInput>
-  void putNewInfallible(KeyInput&& aKey, ValueInput&& aValue)
-  {
-    mImpl.putNewInfallible(
-      aKey, std::forward<KeyInput>(aKey), std::forward<ValueInput>(aValue));
-  }
-
-  // Add (aKey,aDefaultValue) if |aKey| is not found. Return a false-y Ptr on
-  // OOM.
-  Ptr lookupWithDefault(const Key& aKey, const Value& aDefaultValue)
-  {
-    AddPtr p = lookupForAdd(aKey);
-    if (p) {
-      return p;
-    }
-    bool ok = add(p, aKey, aDefaultValue);
-    MOZ_ASSERT_IF(!ok, !p); // p is left false-y on OOM.
-    (void)ok;
-    return p;
-  }
-
-  // Lookup and remove the key/value matching |aLookup|, if present.
-  void remove(const Lookup& aLookup)
-  {
-    if (Ptr p = lookup(aLookup)) {
-      remove(p);
-    }
-  }
-
-  // Infallibly rekey one entry, if necessary. Requires that template
-  // parameters Key and HashPolicy::Lookup are the same type.
-  void rekeyIfMoved(const Key& aOldKey, const Key& aNewKey)
-  {
-    if (aOldKey != aNewKey) {
-      rekeyAs(aOldKey, aNewKey, aNewKey);
-    }
-  }
-
-  // Infallibly rekey one entry if present, and return whether that happened.
-  bool rekeyAs(const Lookup& aOldLookup,
-               const Lookup& aNewLookup,
-               const Key& aNewKey)
-  {
-    if (Ptr p = lookup(aOldLookup)) {
-      mImpl.rekeyAndMaybeRehash(p, aNewLookup, aNewKey);
-      return true;
-    }
-    return false;
-  }
-
-  // HashMap is movable.
-  HashMap(HashMap&& aRhs)
-    : mImpl(std::move(aRhs.mImpl))
-  {
-  }
-  void operator=(HashMap&& aRhs)
-  {
-    MOZ_ASSERT(this != &aRhs, "self-move assignment is prohibited");
-    mImpl = std::move(aRhs.mImpl);
-  }
-
-private:
-  // HashMap is not copyable or assignable.
-  HashMap(const HashMap& hm) = delete;
-  HashMap& operator=(const HashMap& hm) = delete;
-
-  friend class Impl::Enum;
 };
 
 //---------------------------------------------------------------------------
 // HashSet
 //---------------------------------------------------------------------------
 
 // HashSet is a fast hash-based set of values.
 //
@@ -450,47 +465,104 @@ private:
 //   HashSet must not call back into the same HashSet object.
 // - Due to the lack of exception handling, the user must call |init()|.
 //
 template<class T,
          class HashPolicy = DefaultHasher<T>,
          class AllocPolicy = MallocAllocPolicy>
 class HashSet
 {
+  // -- Implementation details -----------------------------------------------
+
+  // HashSet is not copyable or assignable.
+  HashSet(const HashSet& hs) = delete;
+  HashSet& operator=(const HashSet& hs) = delete;
+
   struct SetHashPolicy : HashPolicy
   {
     using Base = HashPolicy;
     using KeyType = T;
 
     static const KeyType& getKey(const T& aT) { return aT; }
 
     static void setKey(T& aT, KeyType& aKey) { HashPolicy::rekey(aT, aKey); }
   };
 
   using Impl = detail::HashTable<const T, SetHashPolicy, AllocPolicy>;
   Impl mImpl;
 
+  friend class Impl::Enum;
+
 public:
   using Lookup = typename HashPolicy::Lookup;
   using Entry = T;
 
+  // -- Initialization -------------------------------------------------------
+
   // HashSet construction is fallible (due to possible OOM). The user must call
   // init() after construction and check the return value.
   explicit HashSet(AllocPolicy a = AllocPolicy())
     : mImpl(a)
   {
   }
 
+  // HashSet is movable.
+  HashSet(HashSet&& aRhs)
+    : mImpl(std::move(aRhs.mImpl))
+  {
+  }
+  void operator=(HashSet&& aRhs)
+  {
+    MOZ_ASSERT(this != &aRhs, "self-move assignment is prohibited");
+    mImpl = std::move(aRhs.mImpl);
+  }
+
   // Initialize the set for use. Must be called after construction, before
   // any other operations (other than initialized()).
   MOZ_MUST_USE bool init(uint32_t aLen = 16) { return mImpl.init(aLen); }
 
+  // -- Status and sizing ----------------------------------------------------
+
   // Has the set been initialized?
   bool initialized() const { return mImpl.initialized(); }
 
+  // The set's current generation.
+  Generation generation() const { return mImpl.generation(); }
+
+  // Is the set empty?
+  bool empty() const { return mImpl.empty(); }
+
+  // Number of elements in the set.
+  uint32_t count() const { return mImpl.count(); }
+
+  // Number of element slots in the set. Note: resize will happen well before
+  // count() == capacity().
+  size_t capacity() const { return mImpl.capacity(); }
+
+  // The size of the set's entry storage, in bytes. If the elements contain
+  // pointers to other heap blocks, you must iterate over the set and measure
+  // them separately; hence the "shallow" prefix.
+  size_t shallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return mImpl.shallowSizeOfExcludingThis(aMallocSizeOf);
+  }
+  size_t shallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) +
+           mImpl.shallowSizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  // -- Lookups --------------------------------------------------------------
+
+  // Does the set contain an element matching |aLookup|?
+  bool has(const Lookup& aLookup) const
+  {
+    return mImpl.lookup(aLookup).found();
+  }
+
   // Return a Ptr indicating whether an element matching |aLookup| is present
   // in the set. E.g.:
   //
   //   using HS = HashSet<int>;
   //   HS h;
   //   if (HS::Ptr p = h.lookup(3)) {
   //     assert(*p == 3);   // p acts like a pointer to int
   //   }
@@ -503,19 +575,46 @@ public:
 
   // Like lookup(), but does not assert if two threads call it at the same
   // time. Only use this method when none of the threads will modify the set.
   MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& aLookup) const
   {
     return mImpl.readonlyThreadsafeLookup(aLookup);
   }
 
-  // Remove a previously found element (assuming aPtr.found()). The set must
-  // not have been mutated in the interim.
-  void remove(Ptr aPtr) { mImpl.remove(aPtr); }
+  // -- Insertions -----------------------------------------------------------
+
+  // Add |aU| if it is not present already. Returns false on OOM.
+  template<typename U>
+  MOZ_MUST_USE bool put(U&& aU)
+  {
+    AddPtr p = lookupForAdd(aU);
+    return p ? true : add(p, std::forward<U>(aU));
+  }
+
+  // Like put(), but asserts that the given key is not already present.
+  template<typename U>
+  MOZ_MUST_USE bool putNew(U&& aU)
+  {
+    return mImpl.putNew(aU, std::forward<U>(aU));
+  }
+
+  // Like the other putNew(), but for when |Lookup| is different to |T|.
+  template<typename U>
+  MOZ_MUST_USE bool putNew(const Lookup& aLookup, U&& aU)
+  {
+    return mImpl.putNew(aLookup, std::forward<U>(aU));
+  }
+
+  // Only call this to populate an empty set after reserving space with init().
+  template<typename U>
+  void putNewInfallible(const Lookup& aLookup, U&& aU)
+  {
+    mImpl.putNewInfallible(aLookup, std::forward<U>(aU));
+  }
 
   // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
   // insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using
   // |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.:
   //
   //   using HS = HashSet<int>;
   //   HS h;
   //   HS::AddPtr p = h.lookupForAdd(3);
@@ -559,126 +658,32 @@ public:
 
   // See the comment above lookupForAdd() for details.
   template<typename U>
   MOZ_MUST_USE bool relookupOrAdd(AddPtr& aPtr, const Lookup& aLookup, U&& aU)
   {
     return mImpl.relookupOrAdd(aPtr, aLookup, std::forward<U>(aU));
   }
 
-  // |iter()| returns an Iterator:
-  //
-  //   HashSet<int> h;
-  //   for (auto iter = h.iter(); !iter.done(); iter.next()) {
-  //     int i = iter.get();
-  //   }
-  //
-  typedef typename Impl::Iterator Iterator;
-  Iterator iter() const { return mImpl.iter(); }
-
-  // |modIter()| returns a ModIterator:
-  //
-  //   HashSet<int> h;
-  //   for (auto iter = h.modIter(); !iter.done(); iter.next()) {
-  //     if (iter.get() == 42) {
-  //       iter.remove();
-  //     }
-  //   }
-  //
-  // Table resize may occur in ModIterator's destructor.
-  typedef typename Impl::ModIterator ModIterator;
-  ModIterator modIter() { return mImpl.modIter(); }
-
-  // These are similar to Iterator/ModIterator/iter(), but use different
-  // terminology.
-  using Range = typename Impl::Range;
-  using Enum = typename Impl::Enum;
-  Range all() const { return mImpl.all(); }
-
-  // Remove all elements without changing the capacity.
-  void clear() { mImpl.clear(); }
-
-  // Remove all elements and attempt to minimize the capacity.
-  void clearAndShrink() { mImpl.clearAndShrink(); }
-
-  // Remove all keys/values and release entry storage. The set must be
-  // initialized via init() again before further use.
-  void finish() { mImpl.finish(); }
-
-  // Is the set empty?
-  bool empty() const { return mImpl.empty(); }
-
-  // Number of elements in the set.
-  uint32_t count() const { return mImpl.count(); }
-
-  // Number of element slots in the set. Note: resize will happen well before
-  // count() == capacity().
-  size_t capacity() const { return mImpl.capacity(); }
-
-  // The size of the HashSet's entry storage, in bytes. If the elements contain
-  // pointers to other heap blocks, you must iterate over the set and measure
-  // them separately; hence the "shallow" prefix.
-  size_t shallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
-  {
-    return mImpl.shallowSizeOfExcludingThis(aMallocSizeOf);
-  }
-  size_t shallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
-  {
-    return aMallocSizeOf(this) +
-           mImpl.shallowSizeOfExcludingThis(aMallocSizeOf);
-  }
-
-  // The set's current generation.
-  Generation generation() const { return mImpl.generation(); }
-
-  /************************************************** Shorthand operations */
-
-  // Does the set contain an element matching |aLookup|?
-  bool has(const Lookup& aLookup) const
-  {
-    return mImpl.lookup(aLookup).found();
-  }
-
-  // Add |aU| if it is not present already. Returns false on OOM.
-  template<typename U>
-  MOZ_MUST_USE bool put(U&& aU)
-  {
-    AddPtr p = lookupForAdd(aU);
-    return p ? true : add(p, std::forward<U>(aU));
-  }
-
-  // Like put(), but asserts that the given key is not already present.
-  template<typename U>
-  MOZ_MUST_USE bool putNew(U&& aU)
-  {
-    return mImpl.putNew(aU, std::forward<U>(aU));
-  }
-
-  // Like the other putNew(), but for when |Lookup| is different to |T|.
-  template<typename U>
-  MOZ_MUST_USE bool putNew(const Lookup& aLookup, U&& aU)
-  {
-    return mImpl.putNew(aLookup, std::forward<U>(aU));
-  }
-
-  // Only call this to populate an empty set after reserving space with init().
-  template<typename U>
-  void putNewInfallible(const Lookup& aLookup, U&& aU)
-  {
-    mImpl.putNewInfallible(aLookup, std::forward<U>(aU));
-  }
+  // -- Removal --------------------------------------------------------------
 
   // Lookup and remove the element matching |aLookup|, if present.
   void remove(const Lookup& aLookup)
   {
     if (Ptr p = lookup(aLookup)) {
       remove(p);
     }
   }
 
+  // Remove a previously found element (assuming aPtr.found()). The set must
+  // not have been mutated in the interim.
+  void remove(Ptr aPtr) { mImpl.remove(aPtr); }
+
+  // -- Rekeying -------------------------------------------------------------
+
   // Infallibly rekey one entry, if present. Requires that template parameters
   // T and HashPolicy::Lookup are the same type.
   void rekeyIfMoved(const Lookup& aOldValue, const T& aNewValue)
   {
     if (aOldValue != aNewValue) {
       rekeyAs(aOldValue, aNewValue, aNewValue);
     }
   }
@@ -703,33 +708,58 @@ public:
   {
     MOZ_ASSERT(aPtr.found());
     MOZ_ASSERT(*aPtr != aNewValue);
     MOZ_ASSERT(HashPolicy::hash(*aPtr) == HashPolicy::hash(aNewValue));
     MOZ_ASSERT(HashPolicy::match(*aPtr, aNewValue));
     const_cast<T&>(*aPtr) = aNewValue;
   }
 
-  // HashSet is movable.
-  HashSet(HashSet&& aRhs)
-    : mImpl(std::move(aRhs.mImpl))
-  {
-  }
-  void operator=(HashSet&& aRhs)
-  {
-    MOZ_ASSERT(this != &aRhs, "self-move assignment is prohibited");
-    mImpl = std::move(aRhs.mImpl);
-  }
+  // -- Iteration ------------------------------------------------------------
+
+  // |iter()| returns an Iterator:
+  //
+  //   HashSet<int> h;
+  //   for (auto iter = h.iter(); !iter.done(); iter.next()) {
+  //     int i = iter.get();
+  //   }
+  //
+  using Iterator = typename Impl::Iterator;
+  Iterator iter() const { return mImpl.iter(); }
 
-private:
-  // HashSet is not copyable or assignable.
-  HashSet(const HashSet& hs) = delete;
-  HashSet& operator=(const HashSet& hs) = delete;
+  // |modIter()| returns a ModIterator:
+  //
+  //   HashSet<int> h;
+  //   for (auto iter = h.modIter(); !iter.done(); iter.next()) {
+  //     if (iter.get() == 42) {
+  //       iter.remove();
+  //     }
+  //   }
+  //
+  // Table resize may occur in ModIterator's destructor.
+  using ModIterator = typename Impl::ModIterator;
+  ModIterator modIter() { return mImpl.modIter(); }
 
-  friend class Impl::Enum;
+  // These are similar to Iterator/ModIterator/iter(), but use different
+  // terminology.
+  using Range = typename Impl::Range;
+  using Enum = typename Impl::Enum;
+  Range all() const { return mImpl.all(); }
+
+  // -- Clearing -------------------------------------------------------------
+
+  // Remove all elements without changing the capacity.
+  void clear() { mImpl.clear(); }
+
+  // Remove all elements and attempt to minimize the capacity.
+  void clearAndShrink() { mImpl.clearAndShrink(); }
+
+  // Remove all keys/values and release entry storage. The set must be
+  // initialized via init() again before further use.
+  void finish() { mImpl.finish(); }
 };
 
 //---------------------------------------------------------------------------
 // Hash Policy
 //---------------------------------------------------------------------------
 
 // A hash policy |HP| for a hash table with key-type |Key| must provide:
 //