| // Copyright 2022 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #ifndef SRC_TINT_UTILS_CONTAINERS_UNIQUE_ALLOCATOR_H_ |
| #define SRC_TINT_UTILS_CONTAINERS_UNIQUE_ALLOCATOR_H_ |
| |
| #include <functional> |
| #include <utility> |
| |
| #include "src/tint/utils/containers/hashmap_base.h" |
| #include "src/tint/utils/memory/block_allocator.h" |
| |
| namespace tint { |
| |
| /// UniqueAllocator is used to allocate unique instances of the template type `T`. |
| template <typename T, typename HASH = Hasher<T>, typename EQUAL = std::equal_to<T>> |
| class UniqueAllocator { |
| public: |
| /// Iterator is the type returned by begin() and end() |
| using Iterator = typename BlockAllocator<T>::ConstIterator; |
| |
| /// @param args the arguments used to construct the object. |
| /// @return a pointer to an instance of `T` with the provided arguments. |
| /// If an existing instance of `T` has been constructed, then the same |
| /// pointer is returned. |
| template <typename TYPE = T, typename... ARGS> |
| TYPE* Get(ARGS&&... args) { |
| // Create a temporary T instance on the stack so that we can hash it, and use it for |
| // equality lookup for the Set. If the item is not found in the set, then we create the |
| // persisted instance with the allocator. |
| TYPE prototype{args...}; |
| Key& key = items.Add(&prototype); |
| if (key.Value() == &prototype) { |
| key = allocator.template Create<TYPE>(std::forward<ARGS>(args)...); |
| } |
| return static_cast<TYPE*>(key.Value()); |
| } |
| |
| /// @param args the arguments used to create the temporary used for the search. |
| /// @return a pointer to an instance of `T` with the provided arguments, or nullptr if the item |
| /// was not found. |
| template <typename TYPE = T, typename... ARGS> |
| TYPE* Find(ARGS&&... args) const { |
| // Create a temporary T instance on the stack so that we can hash it, and use it for |
| // equality lookup for the Set. |
| TYPE prototype{std::forward<ARGS>(args)...}; |
| return static_cast<TYPE*>(items.Get(&prototype)); |
| } |
| |
| /// Wrap sets this allocator to the objects created with the content of `inner`. |
| /// The allocator after Wrap is intended to temporarily extend the objects |
| /// of an existing immutable UniqueAllocator. |
| /// As the copied objects are owned by `inner`, `inner` must not be destructed |
| /// or assigned while using this allocator. |
| /// @param o the immutable UniqueAlllocator to extend |
| void Wrap(const UniqueAllocator<T, HASH, EQUAL>& o) { items = o.items; } |
| |
| /// @returns an iterator to the beginning of the types |
| Iterator begin() const { return allocator.Objects().begin(); } |
| /// @returns an iterator to the end of the types |
| Iterator end() const { return allocator.Objects().end(); } |
| |
| private: |
| /// Comparator is the hashing function used by the Hashset |
| struct Hasher { |
| /// Hashing function |
| /// @param e the entry |
| /// @returns the hash of the entry |
| HashCode operator()(T* e) const { return HASH{}(*e); } |
| }; |
| |
| /// Equality is the equality function used by the Hashset |
| struct Equality { |
| /// Equality function |
| /// @param a the first entry to compare |
| /// @param b the second entry to compare |
| /// @returns true if the two entries are equal |
| bool operator()(T* a, T* b) const { return EQUAL{}(*a, *b); } |
| }; |
| |
| /// The maximum capacity of Set before it performs heap allocations. |
| static constexpr size_t kFixedSize = 32; |
| |
| /// The key type of Set. |
| using Key = HashmapKey<T*, Hasher, Equality>; |
| |
| /// A custom Hashset implementation that allows keys to be modified. |
| class Set : public HashmapBase<Key, kFixedSize> { |
| public: |
| Key& Add(T* key) { |
| auto idx = this->EditAt(key); |
| if (!idx.entry) { |
| idx.Insert(std::forward<T*>(key)); |
| } |
| return *idx.entry; |
| } |
| |
| T* Get(T* key) const { |
| if (auto* entry = this->GetEntry(key)) { |
| return *entry; |
| } |
| return nullptr; |
| } |
| }; |
| |
| /// The block allocator used to allocate the unique objects |
| BlockAllocator<T> allocator; |
| /// The set of unique item entries |
| Set items; |
| }; |
| |
| } // namespace tint |
| |
| #endif // SRC_TINT_UTILS_CONTAINERS_UNIQUE_ALLOCATOR_H_ |