• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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