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). 21struct UniqueVectorNode { 22 uint32_t hash32; 23 uint32_t index_plus1; 24 25 size_t hash_value() const { return hash32; } 26 27 bool is_valid() const { return !is_null(); } 28 29 bool is_null() const { return index_plus1 == 0; } 30 31 // Do not support deletion, making lookup faster. 32 static constexpr bool is_tombstone() { return false; } 33 34 // Return vector index. 35 size_t index() const { return index_plus1 - 1u; } 36 37 static uint32_t ToHash32(size_t hash) { return static_cast<uint32_t>(hash); } 38 39 // Create new instance from hash value and vector index. 40 static UniqueVectorNode Make(size_t hash, size_t index) { 41 return {ToHash32(hash), static_cast<uint32_t>(index + 1u)}; 42 } 43}; 44 45using UniqueVectorHashTableBase = HashTableBase<UniqueVectorNode>; 46 47// A common HashSet implementation used by all UniqueVector instantiations. 48class 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>> 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. 69 void Insert(Node* node, size_t hash, size_t index) { 70 *node = Node::Make(hash, index); 71 BaseType::UpdateAfterInsert(); 72 } 73 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. 80template <typename T, 81 typename Hash = std::hash<T>, 82 typename EqualTo = std::equal_to<T>> 83class UniqueVector { 84 public: 85 using Vector = std::vector<T>; 86 using iterator = typename Vector::iterator; 87 using const_iterator = typename Vector::const_iterator; 88 89 const Vector& vector() const { return vector_; } 90 size_t size() const { return vector_.size(); } 91 bool empty() const { return vector_.empty(); } 92 void clear() { 93 vector_.clear(); 94 set_.Clear(); 95 } 96 void reserve(size_t s) { vector_.reserve(s); } 97 98 const T& operator[](size_t index) const { return vector_[index]; } 99 100 const_iterator begin() const { return vector_.begin(); } 101 const_iterator end() const { return vector_.end(); } 102 103 // Extract the vector out of the instance, clearing it at the same time. 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). 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. 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> 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. 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. 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> 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> 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()))>> 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()))>> 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. 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. 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. 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