// Copyright 2022 The Tint Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef SRC_TINT_UTILS_HASHMAP_H_ #define SRC_TINT_UTILS_HASHMAP_H_ #include #include #include #include "src/tint/utils/hash.h" #include "src/tint/utils/hashset.h" namespace tint::utils { /// An unordered map that uses a robin-hood hashing algorithm. /// /// Hashmap internally wraps a Hashset for providing a store for key-value pairs. /// /// @see Hashset template , typename EQUAL = std::equal_to> class Hashmap { /// Entry holds a key and value pair, and is used as the element type of the underlying Hashset. /// Entries are compared and hashed using only the #key. /// @see Hasher /// @see Equality struct Entry { /// Constructor from a key and value pair Entry(K k, V v) : key(std::move(k)), value(std::move(v)) {} /// Copy-constructor. Entry(const Entry&) = default; /// Move-constructor. Entry(Entry&&) = default; /// Copy-assignment operator Entry& operator=(const Entry&) = default; /// Move-assignment operator Entry& operator=(Entry&&) = default; K key; /// The map entry key V value; /// The map entry value }; /// Hash provider for the underlying Hashset. /// Provides hash functions for an Entry or K. /// The hash functions only consider the key of an entry. struct Hasher { /// Calculates a hash from an Entry size_t operator()(const Entry& entry) const { return HASH()(entry.key); } /// Calculates a hash from a K size_t operator()(const K& key) const { return HASH()(key); } }; /// Equality provider for the underlying Hashset. /// Provides equality functions for an Entry or K to an Entry. /// The equality functions only consider the key for equality. struct Equality { /// Compares an Entry to an Entry for equality. bool operator()(const Entry& a, const Entry& b) const { return EQUAL()(a.key, b.key); } /// Compares a K to an Entry for equality. bool operator()(const K& a, const Entry& b) const { return EQUAL()(a, b.key); } }; /// The underlying set using Set = Hashset; public: /// A Key and Value const-reference pair. struct KeyValue { /// key of a map entry const K& key; /// value of a map entry const V& value; /// Equality operator /// @param other the other KeyValue /// @returns true if the key and value of this KeyValue are equal to other's. bool operator==(const KeyValue& other) const { return key == other.key && value == other.value; } }; /// STL-style alias to KeyValue. /// Used by gmock for the `ElementsAre` checks. using value_type = KeyValue; /// Iterator for the map. /// Iterators are invalidated if the map is modified. class Iterator { public: /// @returns the key of the entry pointed to by this iterator const K& Key() const { return it->key; } /// @returns the value of the entry pointed to by this iterator const V& Value() const { return it->value; } /// Increments the iterator /// @returns this iterator Iterator& operator++() { ++it; return *this; } /// Equality operator /// @param other the other iterator to compare this iterator to /// @returns true if this iterator is equal to other bool operator==(const Iterator& other) const { return it == other.it; } /// Inequality operator /// @param other the other iterator to compare this iterator to /// @returns true if this iterator is not equal to other bool operator!=(const Iterator& other) const { return it != other.it; } /// @returns a pair of key and value for the entry pointed to by this iterator KeyValue operator*() const { return {Key(), Value()}; } private: /// Friend class friend class Hashmap; /// Underlying iterator type using SetIterator = typename Set::Iterator; explicit Iterator(SetIterator i) : it(i) {} SetIterator it; }; /// Removes all entries from the map. void Clear() { set_.Clear(); } /// Adds the key-value pair to the map, if the map does not already contain an entry with a key /// equal to `key`. /// @param key the entry's key to add to the map /// @param value the entry's value to add to the map /// @returns true if the entry was added to the map, false if there was already an entry in the /// map with a key equal to `key`. template bool Add(KEY&& key, VALUE&& value) { return set_.Add(Entry{std::forward(key), std::forward(value)}); } /// Adds the key-value pair to the map, replacing any entry with a key equal to `key`. /// @param key the entry's key to add to the map /// @param value the entry's value to add to the map template void Replace(KEY&& key, VALUE&& value) { set_.Replace(Entry{std::forward(key), std::forward(value)}); } /// Searches for an entry with the given key value. /// @param key the entry's key value to search for. /// @returns the value of the entry with the given key, or no value if the entry was not found. std::optional Get(const K& key) { if (auto* entry = set_.Find(key)) { return entry->value; } return std::nullopt; } /// Searches for an entry with the given key value, adding and returning the result of /// calling `create` if the entry was not found. /// @note: Before calling `create`, the map will insert a zero-initialized value for the given /// key, which will be replaced with the value returned by `create`. If `create` adds an entry /// with `key` to this map, it will be replaced. /// @param key the entry's key value to search for. /// @param create the create function to call if the map does not contain the key. /// @returns the value of the entry. template V& GetOrCreate(const K& key, CREATE&& create) { auto res = set_.Add(Entry{key, V{}}); if (res.action == AddAction::kAdded) { // Store the set generation before calling create() auto generation = set_.Generation(); // Call create(), which might modify this map. auto value = create(); // Was this map mutated? if (set_.Generation() == generation) { // Calling create() did not touch the map. No need to lookup again. res.entry->value = std::move(value); } else { // Calling create() modified the map. Need to insert again. res = set_.Replace(Entry{key, std::move(value)}); } } return res.entry->value; } /// Searches for an entry with the given key value, adding and returning a newly created /// zero-initialized value if the entry was not found. /// @param key the entry's key value to search for. /// @returns the value of the entry. V& GetOrZero(const K& key) { auto res = set_.Add(Entry{key, V{}}); return res.entry->value; } /// Searches for an entry with the given key value. /// @param key the entry's key value to search for. /// @returns the a pointer to the value of the entry with the given key, or nullptr if the entry /// was not found. /// @warning the pointer must not be used after the map is mutated V* Find(const K& key) { if (auto* entry = set_.Find(key)) { return &entry->value; } return nullptr; } /// Searches for an entry with the given key value. /// @param key the entry's key value to search for. /// @returns the a pointer to the value of the entry with the given key, or nullptr if the entry /// was not found. /// @warning the pointer must not be used after the map is mutated const V* Find(const K& key) const { if (auto* entry = set_.Find(key)) { return &entry->value; } return nullptr; } /// Removes an entry from the set with a key equal to `key`. /// @param key the entry key value to remove. /// @returns true if an entry was removed. bool Remove(const K& key) { return set_.Remove(key); } /// Checks whether an entry exists in the map with a key equal to `key`. /// @param key the entry key value to search for. /// @returns true if the map contains an entry with the given key. bool Contains(const K& key) const { return set_.Contains(key); } /// Pre-allocates memory so that the map can hold at least `capacity` entries. /// @param capacity the new capacity of the map. void Reserve(size_t capacity) { set_.Reserve(capacity); } /// @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(); } /// @returns an iterator to the start of the map Iterator begin() const { return Iterator{set_.begin()}; } /// @returns an iterator to the end of the map Iterator end() const { return Iterator{set_.end()}; } private: Set set_; }; } // namespace tint::utils #endif // SRC_TINT_UTILS_HASHMAP_H_