diff --git a/src/tint/utils/hashmap.h b/src/tint/utils/hashmap.h index e17938987f..1154086d2d 100644 --- a/src/tint/utils/hashmap.h +++ b/src/tint/utils/hashmap.h @@ -288,6 +288,9 @@ class Hashmap { /// @returns the number of entries in the map. size_t Count() const { return set_.Count(); } + /// @returns a monotonic counter which is incremented whenever the map is mutated. + size_t Generation() const { return set_.Generation(); } + /// @returns true if the map contains no entries. bool IsEmpty() const { return set_.IsEmpty(); } diff --git a/src/tint/utils/hashmap_test.cc b/src/tint/utils/hashmap_test.cc index 45e929b4c0..e52b144fd0 100644 --- a/src/tint/utils/hashmap_test.cc +++ b/src/tint/utils/hashmap_test.cc @@ -69,6 +69,27 @@ TEST(Hashmap, ReplaceRemove) { EXPECT_FALSE(map.Contains("world")); } +TEST(Hashmap, Generation) { + Hashmap map; + EXPECT_EQ(map.Generation(), 0u); + map.Add(1, "one"); + EXPECT_EQ(map.Generation(), 1u); + map.Add(1, "uno"); + EXPECT_EQ(map.Generation(), 1u); + map.Replace(1, "une"); + EXPECT_EQ(map.Generation(), 2u); + map.Add(2, "dos"); + EXPECT_EQ(map.Generation(), 3u); + map.Remove(1); + EXPECT_EQ(map.Generation(), 4u); + map.Clear(); + EXPECT_EQ(map.Generation(), 5u); + map.Find(2); + EXPECT_EQ(map.Generation(), 5u); + map.Get(2); + EXPECT_EQ(map.Generation(), 5u); +} + TEST(Hashmap, Iterator) { using Map = Hashmap; using KV = typename Map::KeyValue; diff --git a/src/tint/utils/hashset.h b/src/tint/utils/hashset.h index 3009c72eba..f7d5efe743 100644 --- a/src/tint/utils/hashset.h +++ b/src/tint/utils/hashset.h @@ -73,7 +73,8 @@ class Hashset { static constexpr size_t kMinSlots = std::max(kNumFixedSlots, 4); public: - /// Iterator for entries in the set + /// Iterator for entries in the set. + /// Iterators are invalidated if the set is modified. class Iterator { public: /// @returns the value pointed to by this iterator @@ -152,6 +153,7 @@ class Hashset { slots_.Clear(); // Destructs all entries slots_.Resize(kMinSlots); count_ = 0; + generation_++; } /// Result of Add() @@ -219,6 +221,7 @@ class Hashset { // Entry was removed. count_--; + generation_++; return true; } @@ -299,6 +302,9 @@ class Hashset { /// @returns true if the set contains no entries. bool IsEmpty() const { return count_ == 0; } + /// @returns a monotonic counter which is incremented whenever the set is mutated. + size_t Generation() const { return generation_; } + /// @returns an iterator to the start of the set. Iterator begin() const { return Iterator{slots_.begin(), slots_.end()}; } @@ -351,6 +357,7 @@ class Hashset { slot.hash = hash.value; slot.distance = distance; count_++; + generation_++; result = AddResult{AddAction::kAdded, &slot.value.value()}; return Action::kStop; } @@ -361,6 +368,7 @@ class Hashset { // Slot is equal to value. Replace or preserve? if constexpr (MODE == PutMode::kReplace) { slot.value = std::forward(value); + generation_++; result = AddResult{AddAction::kReplaced, &slot.value.value()}; } else { result = AddResult{AddAction::kKeptExisting, &slot.value.value()}; @@ -380,6 +388,7 @@ class Hashset { InsertShuffle(Wrap(index + 1), std::move(evicted)); count_++; + generation_++; result = AddResult{AddAction::kAdded, &slot.value.value()}; return Action::kStop; @@ -502,6 +511,9 @@ class Hashset { /// The number of entries in the set. size_t count_ = 0; + + /// Counter that's incremented with each modification to the set. + size_t generation_ = 0; }; } // namespace tint::utils diff --git a/src/tint/utils/hashset_test.cc b/src/tint/utils/hashset_test.cc index 4213b32490..6e8d1cc687 100644 --- a/src/tint/utils/hashset_test.cc +++ b/src/tint/utils/hashset_test.cc @@ -67,6 +67,27 @@ TEST(Hashset, AddMany) { } } +TEST(Hashset, Generation) { + Hashset set; + EXPECT_EQ(set.Generation(), 0u); + set.Add(1); + EXPECT_EQ(set.Generation(), 1u); + set.Add(1); + EXPECT_EQ(set.Generation(), 1u); + set.Replace(1); + EXPECT_EQ(set.Generation(), 2u); + set.Add(2); + EXPECT_EQ(set.Generation(), 3u); + set.Remove(1); + EXPECT_EQ(set.Generation(), 4u); + set.Clear(); + EXPECT_EQ(set.Generation(), 5u); + set.Find(2); + EXPECT_EQ(set.Generation(), 5u); + set.Get(2); + EXPECT_EQ(set.Generation(), 5u); +} + TEST(Hashset, Iterator) { Hashset set; set.Add("one");