From c158e845e65cfccac35146b908b55dd668025741 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Tue, 29 Nov 2022 20:54:06 +0000 Subject: [PATCH] tint/utils: Add Hashmap equality and hashing Allows Hashmaps to be used as keys to other hashmaps. Change-Id: I557d99515451c55e599dda847e15ce8e2b4500c5 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/112282 Kokoro: Ben Clayton Commit-Queue: Ben Clayton Reviewed-by: Antonio Maiorano --- src/tint/utils/hashmap.h | 41 +++++++++++++++++++++++++ src/tint/utils/hashmap_test.cc | 56 ++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/src/tint/utils/hashmap.h b/src/tint/utils/hashmap.h index a37eab928b..6a5f3c584c 100644 --- a/src/tint/utils/hashmap.h +++ b/src/tint/utils/hashmap.h @@ -206,6 +206,31 @@ class Hashmap : public HashmapBase { return out; } + /// Equality operator + /// @param other the other Hashmap to compare this Hashmap to + /// @returns true if this Hashmap has the same key and value pairs as @p other + template + bool operator==(const Hashmap& other) const { + if (this->Count() != other.Count()) { + return false; + } + for (auto it : *this) { + auto other_val = other.Find(it.key); + if (!other_val || it.value != *other_val) { + return false; + } + } + return true; + } + + /// Inequality operator + /// @param other the other Hashmap to compare this Hashmap to + /// @returns false if this Hashmap has the same key and value pairs as @p other + template + bool operator!=(const Hashmap& other) const { + return !(*this == other); + } + private: Value* Lookup(const Key& key) { if (auto [found, index] = this->IndexOf(key); found) { @@ -222,6 +247,22 @@ class Hashmap : public HashmapBase { } }; +/// Hasher specialization for Hashmap +template +struct Hasher> { + /// @param map the Hashmap to hash + /// @returns a hash of the map + size_t operator()(const Hashmap& map) const { + auto hash = Hash(map.Count()); + for (auto it : map) { + // Use an XOR to ensure that the non-deterministic ordering of the map still produces + // the same hash value for the same entries. + hash ^= Hash(it.key) * 31 + Hash(it.value); + } + return hash; + } +}; + } // namespace tint::utils #endif // SRC_TINT_UTILS_HASHMAP_H_ diff --git a/src/tint/utils/hashmap_test.cc b/src/tint/utils/hashmap_test.cc index 3991eb2b72..5e97aef0f1 100644 --- a/src/tint/utils/hashmap_test.cc +++ b/src/tint/utils/hashmap_test.cc @@ -355,5 +355,61 @@ TEST(Hashmap, Soak) { } } +TEST(Hashmap, EqualitySameSize) { + Hashmap a; + Hashmap b; + EXPECT_EQ(a, b); + a.Add(1, "one"); + EXPECT_NE(a, b); + b.Add(2, "two"); + EXPECT_NE(a, b); + a.Add(2, "two"); + EXPECT_NE(a, b); + b.Add(1, "one"); + EXPECT_EQ(a, b); +} + +TEST(Hashmap, EqualityDifferentSize) { + Hashmap a; + Hashmap b; + EXPECT_EQ(a, b); + a.Add(1, "one"); + EXPECT_NE(a, b); + b.Add(2, "two"); + EXPECT_NE(a, b); + a.Add(2, "two"); + EXPECT_NE(a, b); + b.Add(1, "one"); + EXPECT_EQ(a, b); +} + +TEST(Hashmap, HashSameSize) { + Hashmap a; + Hashmap b; + EXPECT_EQ(Hash(a), Hash(b)); + a.Add(1, "one"); + EXPECT_NE(Hash(a), Hash(b)); + b.Add(2, "two"); + EXPECT_NE(Hash(a), Hash(b)); + a.Add(2, "two"); + EXPECT_NE(Hash(a), Hash(b)); + b.Add(1, "one"); + EXPECT_EQ(Hash(a), Hash(b)); +} + +TEST(Hashmap, HashDifferentSize) { + Hashmap a; + Hashmap b; + EXPECT_EQ(Hash(a), Hash(b)); + a.Add(1, "one"); + EXPECT_NE(Hash(a), Hash(b)); + b.Add(2, "two"); + EXPECT_NE(Hash(a), Hash(b)); + a.Add(2, "two"); + EXPECT_NE(Hash(a), Hash(b)); + b.Add(1, "one"); + EXPECT_EQ(Hash(a), Hash(b)); +} + } // namespace } // namespace tint::utils