| // Copyright 2014 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef TOOLS_GN_UNIQUE_VECTOR_H_ | |
| #define TOOLS_GN_UNIQUE_VECTOR_H_ | |
| #include <stddef.h> | |
| #include <stdint.h> | |
| #include <algorithm> | |
| #include <functional> | |
| #include <vector> | |
| #include "hash_table_base.h" | |
| // A HashTableBase node type used by all UniqueVector<> instantiations. | |
| // The node stores the item's hash value and its index plus 1, in order | |
| // to follow the HashTableBase requirements (i.e. zero-initializable null | |
| // value). | |
| struct UniqueVectorNode { | |
| uint32_t hash32; | |
| uint32_t index_plus1; | |
| size_t hash_value() const { return hash32; } | |
| bool is_valid() const { return !is_null(); } | |
| bool is_null() const { return index_plus1 == 0; } | |
| // Do not support deletion, making lookup faster. | |
| static constexpr bool is_tombstone() { return false; } | |
| // Return vector index. | |
| size_t index() const { return index_plus1 - 1u; } | |
| static uint32_t ToHash32(size_t hash) { return static_cast<uint32_t>(hash); } | |
| // Create new instance from hash value and vector index. | |
| static UniqueVectorNode Make(size_t hash, size_t index) { | |
| return {ToHash32(hash), static_cast<uint32_t>(index + 1u)}; | |
| } | |
| }; | |
| using UniqueVectorHashTableBase = HashTableBase<UniqueVectorNode>; | |
| // A common HashSet implementation used by all UniqueVector instantiations. | |
| class UniqueVectorHashSet : public UniqueVectorHashTableBase { | |
| public: | |
| using BaseType = UniqueVectorHashTableBase; | |
| using Node = BaseType::Node; | |
| // Specialized Lookup() template function. | |
| // |hash| is the hash value for |item|. | |
| // |item| is the item search key being looked up. | |
| // |vector| is containing vector for existing items. | |
| // | |
| // Returns a non-null mutable Node pointer. | |
| template <typename T, typename EqualTo = std::equal_to<T>> | |
| Node* Lookup(size_t hash, const T& item, const std::vector<T>& vector) const { | |
| uint32_t hash32 = Node::ToHash32(hash); | |
| return BaseType::NodeLookup(hash32, [&](const Node* node) { | |
| return hash32 == node->hash32 && EqualTo()(vector[node->index()], item); | |
| }); | |
| } | |
| // Specialized Insert() function that converts |index| into the proper | |
| // UniqueVectorKey type. | |
| void Insert(Node* node, size_t hash, size_t index) { | |
| *node = Node::Make(hash, index); | |
| BaseType::UpdateAfterInsert(false); | |
| } | |
| void Clear() { NodeClear(); } | |
| }; | |
| // An ordered set optimized for GN's usage. Such sets are used to store lists | |
| // of configs and libraries, and are appended to but not randomly inserted | |
| // into. | |
| template <typename T, | |
| typename Hash = std::hash<T>, | |
| typename EqualTo = std::equal_to<T>> | |
| class UniqueVector { | |
| public: | |
| using Vector = std::vector<T>; | |
| using iterator = typename Vector::iterator; | |
| using const_iterator = typename Vector::const_iterator; | |
| const Vector& vector() const { return vector_; } | |
| size_t size() const { return vector_.size(); } | |
| bool empty() const { return vector_.empty(); } | |
| void clear() { | |
| vector_.clear(); | |
| set_.Clear(); | |
| } | |
| void reserve(size_t s) { vector_.reserve(s); } | |
| const T& operator[](size_t index) const { return vector_[index]; } | |
| const_iterator begin() const { return vector_.begin(); } | |
| const_iterator end() const { return vector_.end(); } | |
| // Extract the vector out of the instance, clearing it at the same time. | |
| Vector release() { | |
| Vector result = std::move(vector_); | |
| clear(); | |
| return result; | |
| } | |
| // Returns true if the item was appended, false if it already existed (and | |
| // thus the vector was not modified). | |
| bool push_back(const T& t) { | |
| size_t hash; | |
| auto* node = Lookup(t, &hash); | |
| if (node->is_valid()) { | |
| return false; // Already have this one. | |
| } | |
| vector_.push_back(t); | |
| set_.Insert(node, hash, vector_.size() - 1); | |
| return true; | |
| } | |
| // Same as above, but moves the item into the vector if possible. | |
| bool push_back(T&& t) { | |
| size_t hash = Hash()(t); | |
| auto* node = Lookup(t, &hash); | |
| if (node->is_valid()) { | |
| return false; // Already have this one. | |
| } | |
| vector_.push_back(std::move(t)); | |
| set_.Insert(node, hash, vector_.size() - 1); | |
| return true; | |
| } | |
| // Construct an item in-place if possible. Return true if it was | |
| // appended, false otherwise. | |
| template <typename... ARGS> | |
| bool emplace_back(ARGS... args) { | |
| return push_back(T{std::forward<ARGS>(args)...}); | |
| } | |
| // Try to add an item to the vector. Return (true, index) in | |
| // case of insertion, or (false, index) otherwise. In both cases | |
| // |index| will be the item's index in the set and will not be | |
| // kIndexNone. This can be used to implement a map using a | |
| // UniqueVector<> for keys, and a parallel array for values. | |
| std::pair<bool, size_t> PushBackWithIndex(const T& t) { | |
| size_t hash; | |
| auto* node = Lookup(t, &hash); | |
| if (node->is_valid()) { | |
| return {false, node->index()}; | |
| } | |
| size_t result = vector_.size(); | |
| vector_.push_back(t); | |
| set_.Insert(node, hash, result); | |
| return {true, result}; | |
| } | |
| // Same as above, but moves the item into the set on success. | |
| std::pair<bool, size_t> PushBackWithIndex(T&& t) { | |
| size_t hash; | |
| auto* node = Lookup(t, &hash); | |
| if (node->is_valid()) { | |
| return {false, node->index()}; | |
| } | |
| size_t result = vector_.size(); | |
| vector_.push_back(std::move(t)); | |
| set_.Insert(node, hash, result); | |
| return {true, result}; | |
| } | |
| // Construct an item in-place if possible. If a corresponding | |
| // item is already in the vector, return (false, index), otherwise | |
| // perform the insertion and return (true, index). In both cases | |
| // |index| will be the item's index in the vector and will not be | |
| // kIndexNone. | |
| template <typename... ARGS> | |
| std::pair<bool, size_t> EmplaceBackWithIndex(ARGS... args) { | |
| return PushBackWithIndex(T{std::forward<ARGS>(args)...}); | |
| } | |
| // Appends a range of items from an iterator. | |
| template <typename iter> | |
| void Append(const iter& begin, const iter& end) { | |
| for (iter i = begin; i != end; ++i) | |
| push_back(*i); | |
| } | |
| // Append from any iterable container with begin() and end() | |
| // methods, whose iterators derefence to values convertible to T. | |
| template <typename C, | |
| typename = std::void_t< | |
| decltype(static_cast<const T>(*std::declval<C>().begin())), | |
| decltype(static_cast<const T>(*std::declval<C>().end()))>> | |
| void Append(const C& other) { | |
| Append(other.begin(), other.end()); | |
| } | |
| // Append from any moveable iterable container with begin() and | |
| // end() methods. This variant moves items from the container | |
| // into the UniqueVector instance. | |
| template <typename C, | |
| typename = std::void_t< | |
| decltype(static_cast<T>(*std::declval<C>().begin())), | |
| decltype(static_cast<T>(*std::declval<C>().end()))>> | |
| void Append(C&& other) { | |
| for (auto it = other.begin(); it != other.end(); ++it) | |
| push_back(std::move(*it)); | |
| } | |
| // Returns true if the item is already in the vector. | |
| bool Contains(const T& t) const { | |
| size_t hash; | |
| return Lookup(t, &hash)->is_valid(); | |
| } | |
| // Returns the index of the item matching the given value in the list, or | |
| // kIndexNone if it's not found. | |
| size_t IndexOf(const T& t) const { | |
| size_t hash; | |
| return Lookup(t, &hash)->index(); | |
| } | |
| static constexpr size_t kIndexNone = 0xffffffffu; | |
| private: | |
| // Lookup hash set node for item |t|, also sets |*hash| to the hash value. | |
| UniqueVectorNode* Lookup(const T& t, size_t* hash) const { | |
| *hash = Hash()(t); | |
| return set_.Lookup<T, EqualTo>(*hash, t, vector_); | |
| } | |
| Vector vector_; | |
| UniqueVectorHashSet set_; | |
| }; | |
| #endif // TOOLS_GN_UNIQUE_VECTOR_H_ |