/* 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;
}