This brings the rekey methods into line with line with methods like add that take a universal reference parameter and forward it. The actual type needed depends on the hash policy's rekey method. This allows GC pointer wrappers to live in the hashtable without having to construct instances of these to pass to rekey. Differential Revision: https://phabricator.services.mozilla.com/D296703
330 lines
9.7 KiB
C++
330 lines
9.7 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/CompactPair.h"
|
|
#include "mozilla/HashTable.h"
|
|
#include "mozilla/PairHash.h"
|
|
|
|
#include <utility>
|
|
|
|
void TestMoveConstructor() {
|
|
using namespace mozilla;
|
|
|
|
HashMap<int, int> map;
|
|
MOZ_RELEASE_ASSERT(map.putNew(3, 32));
|
|
MOZ_RELEASE_ASSERT(map.putNew(4, 42));
|
|
MOZ_RELEASE_ASSERT(map.count() == 2);
|
|
MOZ_RELEASE_ASSERT(!map.empty());
|
|
MOZ_RELEASE_ASSERT(!map.lookup(2));
|
|
MOZ_RELEASE_ASSERT(map.lookup(3)->value() == 32);
|
|
MOZ_RELEASE_ASSERT(map.lookup(4)->value() == 42);
|
|
|
|
HashMap<int, int> moved = std::move(map);
|
|
MOZ_RELEASE_ASSERT(moved.count() == 2);
|
|
MOZ_RELEASE_ASSERT(!moved.empty());
|
|
MOZ_RELEASE_ASSERT(!moved.lookup(2));
|
|
MOZ_RELEASE_ASSERT(moved.lookup(3)->value() == 32);
|
|
MOZ_RELEASE_ASSERT(moved.lookup(4)->value() == 42);
|
|
|
|
MOZ_RELEASE_ASSERT(map.empty());
|
|
MOZ_RELEASE_ASSERT(!map.count());
|
|
}
|
|
|
|
void CheckSwapMap1(const mozilla::HashMap<int, int>& 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<int, int>& 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<int, int> map1;
|
|
MOZ_RELEASE_ASSERT(map1.putNew(1, 10));
|
|
MOZ_RELEASE_ASSERT(map1.putNew(2, 20));
|
|
CheckSwapMap1(map1);
|
|
|
|
HashMap<int, int> 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 {
|
|
CLASS_ENUM_1,
|
|
CLASS_ENUM_2,
|
|
};
|
|
|
|
void TestEnumHash() {
|
|
using namespace mozilla;
|
|
|
|
HashMap<SimpleEnum, int> map;
|
|
MOZ_RELEASE_ASSERT(map.put(SIMPLE_1, 1));
|
|
MOZ_RELEASE_ASSERT(map.put(SIMPLE_2, 2));
|
|
|
|
MOZ_RELEASE_ASSERT(map.lookup(SIMPLE_1)->value() == 1);
|
|
MOZ_RELEASE_ASSERT(map.lookup(SIMPLE_2)->value() == 2);
|
|
|
|
HashMap<ClassEnum, int> map2;
|
|
MOZ_RELEASE_ASSERT(map2.put(ClassEnum::CLASS_ENUM_1, 1));
|
|
MOZ_RELEASE_ASSERT(map2.put(ClassEnum::CLASS_ENUM_2, 2));
|
|
|
|
MOZ_RELEASE_ASSERT(map2.lookup(ClassEnum::CLASS_ENUM_1)->value() == 1);
|
|
MOZ_RELEASE_ASSERT(map2.lookup(ClassEnum::CLASS_ENUM_2)->value() == 2);
|
|
}
|
|
|
|
void TestHashPair() {
|
|
using namespace mozilla;
|
|
|
|
// Test with std::pair
|
|
{
|
|
HashMap<std::pair<int, bool>, int, PairHasher<int, bool>> map;
|
|
std::pair<int, bool> key1 = std::make_pair(1, true);
|
|
MOZ_RELEASE_ASSERT(map.putNew(key1, 1));
|
|
MOZ_RELEASE_ASSERT(map.has(key1));
|
|
std::pair<int, bool> key2 = std::make_pair(1, false);
|
|
MOZ_RELEASE_ASSERT(map.putNew(key2, 1));
|
|
std::pair<int, bool> key3 = std::make_pair(2, false);
|
|
MOZ_RELEASE_ASSERT(map.putNew(key3, 2));
|
|
MOZ_RELEASE_ASSERT(map.has(key3));
|
|
|
|
MOZ_RELEASE_ASSERT(map.lookup(key1)->value() == 1);
|
|
MOZ_RELEASE_ASSERT(map.lookup(key2)->value() == 1);
|
|
MOZ_RELEASE_ASSERT(map.lookup(key3)->value() == 2);
|
|
}
|
|
// Test wtih compact pair
|
|
{
|
|
HashMap<mozilla::CompactPair<int, bool>, int, CompactPairHasher<int, bool>>
|
|
map;
|
|
mozilla::CompactPair<int, bool> key1 = mozilla::MakeCompactPair(1, true);
|
|
MOZ_RELEASE_ASSERT(map.putNew(key1, 1));
|
|
MOZ_RELEASE_ASSERT(map.has(key1));
|
|
mozilla::CompactPair<int, bool> key2 = mozilla::MakeCompactPair(1, false);
|
|
MOZ_RELEASE_ASSERT(map.putNew(key2, 1));
|
|
mozilla::CompactPair<int, bool> key3 = mozilla::MakeCompactPair(2, false);
|
|
MOZ_RELEASE_ASSERT(map.putNew(key3, 2));
|
|
MOZ_RELEASE_ASSERT(map.has(key3));
|
|
|
|
MOZ_RELEASE_ASSERT(map.lookup(key1)->value() == 1);
|
|
MOZ_RELEASE_ASSERT(map.lookup(key2)->value() == 1);
|
|
MOZ_RELEASE_ASSERT(map.lookup(key3)->value() == 2);
|
|
}
|
|
}
|
|
|
|
void TestRekey() {
|
|
using namespace mozilla;
|
|
|
|
HashMap<int, int> map;
|
|
MOZ_RELEASE_ASSERT(map.putNew(1, 10));
|
|
MOZ_RELEASE_ASSERT(map.putNew(2, 20));
|
|
MOZ_RELEASE_ASSERT(map.putNew(3, 30));
|
|
|
|
// rekeyIfMoved: same key is a no-op.
|
|
map.rekeyIfMoved(1, 1);
|
|
MOZ_RELEASE_ASSERT(map.count() == 3);
|
|
MOZ_RELEASE_ASSERT(map.lookup(1)->value() == 10);
|
|
|
|
// rekeyIfMoved: replace an existing key.
|
|
map.rekeyIfMoved(1, 11);
|
|
MOZ_RELEASE_ASSERT(map.count() == 3);
|
|
MOZ_RELEASE_ASSERT(!map.lookup(1));
|
|
MOZ_RELEASE_ASSERT(map.lookup(11)->value() == 10);
|
|
|
|
// rekeyIfMoved on a missing key is a no-op.
|
|
map.rekeyIfMoved(99, 100);
|
|
MOZ_RELEASE_ASSERT(map.count() == 3);
|
|
MOZ_RELEASE_ASSERT(!map.lookup(99));
|
|
MOZ_RELEASE_ASSERT(!map.lookup(100));
|
|
|
|
// rekeyAs: returns true and replaces key when present.
|
|
MOZ_RELEASE_ASSERT(map.rekeyAs(2, 22, 22));
|
|
MOZ_RELEASE_ASSERT(map.count() == 3);
|
|
MOZ_RELEASE_ASSERT(!map.lookup(2));
|
|
MOZ_RELEASE_ASSERT(map.lookup(22)->value() == 20);
|
|
|
|
// rekeyAs: returns false when the old key is absent.
|
|
MOZ_RELEASE_ASSERT(!map.rekeyAs(2, 23, 23));
|
|
MOZ_RELEASE_ASSERT(map.count() == 3);
|
|
MOZ_RELEASE_ASSERT(!map.lookup(23));
|
|
|
|
// Other entries are unaffected.
|
|
MOZ_RELEASE_ASSERT(map.lookup(3)->value() == 30);
|
|
}
|
|
|
|
// A structure that holds an int and supports move semantics but not copy
|
|
// semantics.
|
|
struct WrappedInt {
|
|
int mValue;
|
|
|
|
explicit WrappedInt(int v) : mValue(v) {}
|
|
|
|
WrappedInt(const WrappedInt&) = delete;
|
|
WrappedInt& operator=(const WrappedInt&) = delete;
|
|
|
|
WrappedInt(WrappedInt&& aOther) : mValue(aOther.mValue) { aOther.mValue = 0; }
|
|
WrappedInt& operator=(WrappedInt&& aOther) {
|
|
mValue = aOther.mValue;
|
|
aOther.mValue = 0;
|
|
return *this;
|
|
}
|
|
|
|
struct HashPolicy {
|
|
using Key = WrappedInt;
|
|
using Lookup = int;
|
|
|
|
static mozilla::HashNumber hash(Lookup aLookup) { return aLookup; }
|
|
|
|
static bool match(const Key& aKey, Lookup aLookup) {
|
|
return aKey.mValue == aLookup;
|
|
}
|
|
|
|
static void rekey(Key& aKey, Key&& aNewKey) { aKey = std::move(aNewKey); }
|
|
};
|
|
};
|
|
|
|
void TestRekeyWithRValue() {
|
|
using namespace mozilla;
|
|
|
|
HashMap<WrappedInt, int, WrappedInt::HashPolicy> map;
|
|
MOZ_RELEASE_ASSERT(map.putNew(1, 10));
|
|
MOZ_RELEASE_ASSERT(map.putNew(2, 20));
|
|
MOZ_RELEASE_ASSERT(map.putNew(3, 30));
|
|
|
|
// rekeyAs: replace an existing key.
|
|
map.rekeyAs(1, 11, WrappedInt(11));
|
|
MOZ_RELEASE_ASSERT(map.count() == 3);
|
|
MOZ_RELEASE_ASSERT(!map.lookup(1));
|
|
MOZ_RELEASE_ASSERT(map.lookup(11)->value() == 10);
|
|
|
|
// rekeyAs on a missing key is a no-op.
|
|
MOZ_RELEASE_ASSERT(!map.rekeyAs(99, 100, WrappedInt(100)));
|
|
MOZ_RELEASE_ASSERT(map.count() == 3);
|
|
MOZ_RELEASE_ASSERT(!map.lookup(99));
|
|
MOZ_RELEASE_ASSERT(!map.lookup(100));
|
|
|
|
// rekeyAs: returns true and replaces key when present.
|
|
MOZ_RELEASE_ASSERT(map.rekeyAs(2, 22, WrappedInt(22)));
|
|
MOZ_RELEASE_ASSERT(map.count() == 3);
|
|
MOZ_RELEASE_ASSERT(!map.lookup(2));
|
|
MOZ_RELEASE_ASSERT(map.lookup(22)->value() == 20);
|
|
|
|
// rekeyAs: returns false when the old key is absent.
|
|
MOZ_RELEASE_ASSERT(!map.rekeyAs(2, 23, WrappedInt(23)));
|
|
MOZ_RELEASE_ASSERT(map.count() == 3);
|
|
MOZ_RELEASE_ASSERT(!map.lookup(23));
|
|
|
|
// Other entries are unaffected.
|
|
MOZ_RELEASE_ASSERT(map.lookup(3)->value() == 30);
|
|
}
|
|
|
|
void TestModIteratorRekey() {
|
|
using namespace mozilla;
|
|
|
|
// Rekey one entry found during iteration.
|
|
{
|
|
HashMap<int, int> map;
|
|
MOZ_RELEASE_ASSERT(map.putNew(1, 10));
|
|
MOZ_RELEASE_ASSERT(map.putNew(2, 20));
|
|
MOZ_RELEASE_ASSERT(map.putNew(3, 30));
|
|
|
|
for (auto iter = map.modIter(); !iter.done(); iter.next()) {
|
|
if (iter.get().key() == 2) {
|
|
iter.rekey(22);
|
|
}
|
|
}
|
|
|
|
MOZ_RELEASE_ASSERT(map.count() == 3);
|
|
MOZ_RELEASE_ASSERT(!map.lookup(2));
|
|
MOZ_RELEASE_ASSERT(map.lookup(22)->value() == 20);
|
|
MOZ_RELEASE_ASSERT(map.lookup(1)->value() == 10);
|
|
MOZ_RELEASE_ASSERT(map.lookup(3)->value() == 30);
|
|
}
|
|
|
|
// Rekey multiple entries.
|
|
{
|
|
HashMap<int, int> map;
|
|
MOZ_RELEASE_ASSERT(map.putNew(1, 10));
|
|
MOZ_RELEASE_ASSERT(map.putNew(2, 20));
|
|
MOZ_RELEASE_ASSERT(map.putNew(3, 30));
|
|
|
|
for (auto iter = map.modIter(); !iter.done(); iter.next()) {
|
|
int key = iter.get().key();
|
|
if (key > 0) {
|
|
iter.rekey(-key);
|
|
}
|
|
}
|
|
|
|
MOZ_RELEASE_ASSERT(map.count() == 3);
|
|
MOZ_RELEASE_ASSERT(!map.lookup(1));
|
|
MOZ_RELEASE_ASSERT(!map.lookup(2));
|
|
MOZ_RELEASE_ASSERT(!map.lookup(3));
|
|
MOZ_RELEASE_ASSERT(map.lookup(-1)->value() == 10);
|
|
MOZ_RELEASE_ASSERT(map.lookup(-2)->value() == 20);
|
|
MOZ_RELEASE_ASSERT(map.lookup(-3)->value() == 30);
|
|
}
|
|
}
|
|
|
|
void TestCapacityAfterRemove() {
|
|
mozilla::HashMap<int, int> map;
|
|
MOZ_RELEASE_ASSERT(map.count() == 0);
|
|
MOZ_RELEASE_ASSERT(map.capacity() == 0);
|
|
|
|
MOZ_RELEASE_ASSERT(map.putNew(1, 1));
|
|
MOZ_RELEASE_ASSERT(map.count() == 1);
|
|
MOZ_RELEASE_ASSERT(map.capacity() != 0);
|
|
|
|
map.remove(1);
|
|
MOZ_RELEASE_ASSERT(map.count() == 0);
|
|
MOZ_RELEASE_ASSERT(map.capacity() != 0);
|
|
|
|
map.compact();
|
|
MOZ_RELEASE_ASSERT(map.count() == 0);
|
|
MOZ_RELEASE_ASSERT(map.capacity() == 0);
|
|
|
|
MOZ_RELEASE_ASSERT(map.putNew(1, 1));
|
|
MOZ_RELEASE_ASSERT(map.count() == 1);
|
|
MOZ_RELEASE_ASSERT(map.capacity() != 0);
|
|
|
|
{
|
|
auto iter = map.modIter();
|
|
MOZ_RELEASE_ASSERT(!iter.done());
|
|
iter.remove();
|
|
}
|
|
MOZ_RELEASE_ASSERT(map.count() == 0);
|
|
MOZ_RELEASE_ASSERT(map.capacity() != 0);
|
|
|
|
map.compact();
|
|
MOZ_RELEASE_ASSERT(map.count() == 0);
|
|
MOZ_RELEASE_ASSERT(map.capacity() == 0);
|
|
}
|
|
|
|
int main() {
|
|
TestMoveConstructor();
|
|
TestEnumHash();
|
|
TestHashPair();
|
|
TestRekey();
|
|
TestRekeyWithRValue();
|
|
TestModIteratorRekey();
|
|
TestCapacityAfterRemove();
|
|
return 0;
|
|
}
|