1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef TOOLS_GN_UNIQUE_VECTOR_H_ 6 #define TOOLS_GN_UNIQUE_VECTOR_H_ 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 #include <algorithm> 12 #include <functional> 13 #include <vector> 14 15 #include "hash_table_base.h" 16 17 // A HashTableBase node type used by all UniqueVector<> instantiations. 18 // The node stores the item's hash value and its index plus 1, in order 19 // to follow the HashTableBase requirements (i.e. zero-initializable null 20 // value). 21 struct UniqueVectorNode { 22 uint32_t hash32; 23 uint32_t index_plus1; 24 hash_valueUniqueVectorNode25 size_t hash_value() const { return hash32; } 26 is_validUniqueVectorNode27 bool is_valid() const { return !is_null(); } 28 is_nullUniqueVectorNode29 bool is_null() const { return index_plus1 == 0; } 30 31 // Do not support deletion, making lookup faster. is_tombstoneUniqueVectorNode32 static constexpr bool is_tombstone() { return false; } 33 34 // Return vector index. indexUniqueVectorNode35 size_t index() const { return index_plus1 - 1u; } 36 ToHash32UniqueVectorNode37 static uint32_t ToHash32(size_t hash) { return static_cast<uint32_t>(hash); } 38 39 // Create new instance from hash value and vector index. MakeUniqueVectorNode40 static UniqueVectorNode Make(size_t hash, size_t index) { 41 return {ToHash32(hash), static_cast<uint32_t>(index + 1u)}; 42 } 43 }; 44 45 using UniqueVectorHashTableBase = HashTableBase<UniqueVectorNode>; 46 47 // A common HashSet implementation used by all UniqueVector instantiations. 48 class UniqueVectorHashSet : public UniqueVectorHashTableBase { 49 public: 50 using BaseType = UniqueVectorHashTableBase; 51 using Node = BaseType::Node; 52 53 // Specialized Lookup() template function. 54 // |hash| is the hash value for |item|. 55 // |item| is the item search key being looked up. 56 // |vector| is containing vector for existing items. 57 // 58 // Returns a non-null mutable Node pointer. 59 template <typename T, typename EqualTo = std::equal_to<T>> Lookup(size_t hash,const T & item,const std::vector<T> & vector)60 Node* Lookup(size_t hash, const T& item, const std::vector<T>& vector) const { 61 uint32_t hash32 = Node::ToHash32(hash); 62 return BaseType::NodeLookup(hash32, [&](const Node* node) { 63 return hash32 == node->hash32 && EqualTo()(vector[node->index()], item); 64 }); 65 } 66 67 // Specialized Insert() function that converts |index| into the proper 68 // UniqueVectorKey type. Insert(Node * node,size_t hash,size_t index)69 void Insert(Node* node, size_t hash, size_t index) { 70 *node = Node::Make(hash, index); 71 BaseType::UpdateAfterInsert(); 72 } 73 Clear()74 void Clear() { NodeClear(); } 75 }; 76 77 // An ordered set optimized for GN's usage. Such sets are used to store lists 78 // of configs and libraries, and are appended to but not randomly inserted 79 // into. 80 template <typename T, 81 typename Hash = std::hash<T>, 82 typename EqualTo = std::equal_to<T>> 83 class UniqueVector { 84 public: 85 using Vector = std::vector<T>; 86 using iterator = typename Vector::iterator; 87 using const_iterator = typename Vector::const_iterator; 88 vector()89 const Vector& vector() const { return vector_; } size()90 size_t size() const { return vector_.size(); } empty()91 bool empty() const { return vector_.empty(); } clear()92 void clear() { 93 vector_.clear(); 94 set_.Clear(); 95 } reserve(size_t s)96 void reserve(size_t s) { vector_.reserve(s); } 97 98 const T& operator[](size_t index) const { return vector_[index]; } 99 begin()100 const_iterator begin() const { return vector_.begin(); } end()101 const_iterator end() const { return vector_.end(); } 102 103 // Extract the vector out of the instance, clearing it at the same time. release()104 Vector release() { 105 Vector result = std::move(vector_); 106 clear(); 107 return result; 108 } 109 110 // Returns true if the item was appended, false if it already existed (and 111 // thus the vector was not modified). push_back(const T & t)112 bool push_back(const T& t) { 113 size_t hash; 114 auto* node = Lookup(t, &hash); 115 if (node->is_valid()) { 116 return false; // Already have this one. 117 } 118 vector_.push_back(t); 119 set_.Insert(node, hash, vector_.size() - 1); 120 return true; 121 } 122 123 // Same as above, but moves the item into the vector if possible. push_back(T && t)124 bool push_back(T&& t) { 125 size_t hash = Hash()(t); 126 auto* node = Lookup(t, &hash); 127 if (node->is_valid()) { 128 return false; // Already have this one. 129 } 130 vector_.push_back(std::move(t)); 131 set_.Insert(node, hash, vector_.size() - 1); 132 return true; 133 } 134 135 // Construct an item in-place if possible. Return true if it was 136 // appended, false otherwise. 137 template <typename... ARGS> emplace_back(ARGS...args)138 bool emplace_back(ARGS... args) { 139 return push_back(T{std::forward<ARGS>(args)...}); 140 } 141 142 // Try to add an item to the vector. Return (true, index) in 143 // case of insertion, or (false, index) otherwise. In both cases 144 // |index| will be the item's index in the set and will not be 145 // kIndexNone. This can be used to implement a map using a 146 // UniqueVector<> for keys, and a parallel array for values. PushBackWithIndex(const T & t)147 std::pair<bool, size_t> PushBackWithIndex(const T& t) { 148 size_t hash; 149 auto* node = Lookup(t, &hash); 150 if (node->is_valid()) { 151 return {false, node->index()}; 152 } 153 size_t result = vector_.size(); 154 vector_.push_back(t); 155 set_.Insert(node, hash, result); 156 return {true, result}; 157 } 158 159 // Same as above, but moves the item into the set on success. PushBackWithIndex(T && t)160 std::pair<bool, size_t> PushBackWithIndex(T&& t) { 161 size_t hash; 162 auto* node = Lookup(t, &hash); 163 if (node->is_valid()) { 164 return {false, node->index()}; 165 } 166 size_t result = vector_.size(); 167 vector_.push_back(std::move(t)); 168 set_.Insert(node, hash, result); 169 return {true, result}; 170 } 171 172 // Construct an item in-place if possible. If a corresponding 173 // item is already in the vector, return (false, index), otherwise 174 // perform the insertion and return (true, index). In both cases 175 // |index| will be the item's index in the vector and will not be 176 // kIndexNone. 177 template <typename... ARGS> EmplaceBackWithIndex(ARGS...args)178 std::pair<bool, size_t> EmplaceBackWithIndex(ARGS... args) { 179 return PushBackWithIndex(T{std::forward<ARGS>(args)...}); 180 } 181 182 // Appends a range of items from an iterator. 183 template <typename iter> Append(const iter & begin,const iter & end)184 void Append(const iter& begin, const iter& end) { 185 for (iter i = begin; i != end; ++i) 186 push_back(*i); 187 } 188 189 // Append from any iterable container with begin() and end() 190 // methods, whose iterators derefence to values convertible to T. 191 template <typename C, 192 typename = std::void_t< 193 decltype(static_cast<const T>(*std::declval<C>().begin())), 194 decltype(static_cast<const T>(*std::declval<C>().end()))>> Append(const C & other)195 void Append(const C& other) { 196 Append(other.begin(), other.end()); 197 } 198 199 // Append from any moveable iterable container with begin() and 200 // end() methods. This variant moves items from the container 201 // into the UniqueVector instance. 202 template <typename C, 203 typename = std::void_t< 204 decltype(static_cast<T>(*std::declval<C>().begin())), 205 decltype(static_cast<T>(*std::declval<C>().end()))>> Append(C && other)206 void Append(C&& other) { 207 for (auto it = other.begin(); it != other.end(); ++it) 208 push_back(std::move(*it)); 209 } 210 211 // Returns true if the item is already in the vector. Contains(const T & t)212 bool Contains(const T& t) const { 213 size_t hash; 214 return Lookup(t, &hash)->is_valid(); 215 } 216 217 // Returns the index of the item matching the given value in the list, or 218 // kIndexNone if it's not found. IndexOf(const T & t)219 size_t IndexOf(const T& t) const { 220 size_t hash; 221 return Lookup(t, &hash)->index(); 222 } 223 224 static constexpr size_t kIndexNone = 0xffffffffu; 225 226 private: 227 // Lookup hash set node for item |t|, also sets |*hash| to the hash value. Lookup(const T & t,size_t * hash)228 UniqueVectorNode* Lookup(const T& t, size_t* hash) const { 229 *hash = Hash()(t); 230 return set_.Lookup<T, EqualTo>(*hash, t, vector_); 231 } 232 233 Vector vector_; 234 UniqueVectorHashSet set_; 235 }; 236 237 #endif // TOOLS_GN_UNIQUE_VECTOR_H_ 238