// 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 #include #include #include #include #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(hash); } // Create new instance from hash value and vector index. static UniqueVectorNode Make(size_t hash, size_t index) { return {ToHash32(hash), static_cast(index + 1u)}; } }; using UniqueVectorHashTableBase = HashTableBase; // 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 > Node* Lookup(size_t hash, const T& item, const std::vector& 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(); } 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 EqualTo = std::equal_to> class UniqueVector { public: using Vector = std::vector; 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 bool emplace_back(ARGS... args) { return push_back(T{std::forward(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 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 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 std::pair EmplaceBackWithIndex(ARGS... args) { return PushBackWithIndex(T{std::forward(args)...}); } // Appends a range of items from an iterator. template 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 (*std::declval().begin())), decltype(static_cast(*std::declval().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 (*std::declval().begin())), decltype(static_cast(*std::declval().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(*hash, t, vector_); } Vector vector_; UniqueVectorHashSet set_; }; #endif // TOOLS_GN_UNIQUE_VECTOR_H_