diff --git a/mfbt/HashTable.h b/mfbt/HashTable.h index 9f3f42b40ec7..6ef91045177f 100644 --- a/mfbt/HashTable.h +++ b/mfbt/HashTable.h @@ -180,6 +180,9 @@ class HashMap { HashMap(HashMap&& aRhs) = default; HashMap& operator=(HashMap&& aRhs) = default; + // Swap the contents of this hash map with another. + void swap(HashMap& aOther) { mImpl.swap(aOther.mImpl); } + // -- Status and sizing ---------------------------------------------------- // The map's current generation. @@ -477,6 +480,9 @@ class HashSet { HashSet(HashSet&& aRhs) = default; HashSet& operator=(HashSet&& aRhs) = default; + // Swap the contents of this hash set with another. + void swap(HashSet& aOther) { mImpl.swap(aOther.mImpl); } + // -- Status and sizing ---------------------------------------------------- // The set's current generation. @@ -1558,6 +1564,29 @@ class HashTable : private AllocPolicy { return *this; } + void swap(HashTable& aOther) { + ReentrancyGuard g1(*this); + ReentrancyGuard g2(aOther); + + // Manual swap of generation because it's a bitfield + uint64_t generation = mGen; + mGen = aOther.mGen; + aOther.mGen = generation; + + // Manual swap of hashShift because it's a bitfield + uint64_t hashShift = mHashShift; + mHashShift = aOther.mHashShift; + aOther.mHashShift = hashShift; + + std::swap(mTable, aOther.mTable); + std::swap(mEntryCount, aOther.mEntryCount); + std::swap(mRemovedCount, aOther.mRemovedCount); +#ifdef DEBUG + std::swap(mMutationCount, aOther.mMutationCount); + std::swap(mEntered, aOther.mEntered); +#endif + } + private: void moveFrom(HashTable& aRhs) { mGen = aRhs.mGen; diff --git a/mfbt/tests/TestHashTable.cpp b/mfbt/tests/TestHashTable.cpp index c648184040c1..ddab1de932b5 100644 --- a/mfbt/tests/TestHashTable.cpp +++ b/mfbt/tests/TestHashTable.cpp @@ -32,6 +32,42 @@ void TestMoveConstructor() { MOZ_RELEASE_ASSERT(!map.count()); } +void CheckSwapMap1(const mozilla::HashMap& map1) { + MOZ_RELEASE_ASSERT(map1.count() == 2); + MOZ_RELEASE_ASSERT(!map1.empty()); + MOZ_RELEASE_ASSERT(!map1.lookup(3)); + MOZ_RELEASE_ASSERT(!map1.lookup(4)); + MOZ_RELEASE_ASSERT(map1.lookup(1)->value() == 10); + MOZ_RELEASE_ASSERT(map1.lookup(2)->value() == 20); +} + +void CheckSwapMap2(const mozilla::HashMap& map2) { + MOZ_RELEASE_ASSERT(map2.count() == 2); + MOZ_RELEASE_ASSERT(!map2.empty()); + MOZ_RELEASE_ASSERT(!map2.lookup(1)); + MOZ_RELEASE_ASSERT(!map2.lookup(2)); + MOZ_RELEASE_ASSERT(map2.lookup(3)->value() == 30); + MOZ_RELEASE_ASSERT(map2.lookup(4)->value() == 40); +} + +void TestSwap() { + using namespace mozilla; + + HashMap map1; + MOZ_RELEASE_ASSERT(map1.putNew(1, 10)); + MOZ_RELEASE_ASSERT(map1.putNew(2, 20)); + CheckSwapMap1(map1); + + HashMap map2; + MOZ_RELEASE_ASSERT(map2.putNew(3, 30)); + MOZ_RELEASE_ASSERT(map2.putNew(4, 40)); + CheckSwapMap2(map2); + + map1.swap(map2); + CheckSwapMap2(map1); + CheckSwapMap1(map2); +} + enum SimpleEnum { SIMPLE_1, SIMPLE_2 }; enum class ClassEnum : int {