• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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 SRC_GN_POINTER_SET_H_
6 #define SRC_GN_POINTER_SET_H_
7 
8 #include <functional>
9 #include <vector>
10 
11 #include "gn/hash_table_base.h"
12 
13 // PointerSet<T> is a fast implemention of a set of non-owning and non-null
14 // typed pointer values (of type T*).
15 //
16 // Note that this intentionally does not support a find() method
17 // for performance reasons, instead callers must use contains(), add()
18 // or erase() directly to perform lookups or conditional insertion/removal.
19 //
20 // Only constant iterators are provided. Moreover, the slightly faster
21 // iteration form can be used for performance as in:
22 //
23 //   for (auto it = my_set.begin(); it.valid(); ++it) {
24 //     T* ptr = *it;
25 //     ...
26 //   }
27 //
28 // Instead of:
29 //
30 //   for (T* ptr : my_set) {
31 //     ...
32 //   }
33 //
34 // The PointerSetNode type implements the methods required by HashTableBase<>
35 // to store and hash a pointer directly in the buckets array. The special
36 // address 1 is used as the tombstone value to support removal.
37 //
38 // Null nodes are marked with an empty pointer, which means that nullptr
39 // itself cannot be stored in the set.
40 struct PointerSetNode {
41   const void* ptr_;
is_nullPointerSetNode42   bool is_null() const { return !ptr_; }
is_tombstonePointerSetNode43   bool is_tombstone() const { return ptr_ == MakeTombstone(); }
is_validPointerSetNode44   bool is_valid() const { return !is_null() && !is_tombstone(); }
hash_valuePointerSetNode45   size_t hash_value() const { return MakeHash(ptr_); }
46 
47   // Return the tomebstone value. This could be a static constexpr value
48   // if C++ didn't complain about reinterpret_cast<> here.
MakeTombstonePointerSetNode49   static const void* MakeTombstone() {
50     return reinterpret_cast<const void*>(1u);
51   }
52 
53   // Return the hash corresponding to a given pointer.
MakeHashPointerSetNode54   static size_t MakeHash(const void* ptr) {
55     return std::hash<const void*>()(ptr);
56   }
57 };
58 
59 template <typename T>
60 class PointerSet : public HashTableBase<PointerSetNode> {
61  public:
62   using NodeType = PointerSetNode;
63   using BaseType = HashTableBase<NodeType>;
64 
65   PointerSet() = default;
66 
67   // Allow copying pointer sets.
PointerSet(const PointerSet & other)68   PointerSet(const PointerSet& other) : BaseType() { insert(other); }
69   PointerSet& operator=(const PointerSet& other) {
70     if (this != &other) {
71       this->~PointerSet();
72       new (this) PointerSet(other);
73     }
74     return *this;
75   }
76 
77   // Redefine move operators since the copy constructor above
78   // masks the base ones by default.
PointerSet(PointerSet && other)79   PointerSet(PointerSet&& other) noexcept : BaseType(std::move(other)) {}
80   PointerSet& operator=(PointerSet&& other) noexcept {
81     if (this != &other) {
82       this->~PointerSet();
83       new (this) PointerSet(std::move(other));
84     }
85     return *this;
86   }
87 
88   // Range constructor.
89   template <typename InputIter>
PointerSet(InputIter first,InputIter last)90   PointerSet(InputIter first, InputIter last) {
91     for (; first != last; ++first)
92       add(*first);
93   }
94 
95   // Clear the set.
clear()96   void clear() { NodeClear(); }
97 
98   // Add one pointer to the set. Return true if the pointer was
99   // added, or false if its was already in the set.
add(T * ptr)100   bool add(T* ptr) {
101     NodeType* node = Lookup(ptr);
102     if (node->is_valid())
103       return false;
104 
105     node->ptr_ = ptr;
106     UpdateAfterInsert();
107     return true;
108   }
109 
110   // Return true if a given pointer is already in the set.
contains(T * ptr)111   bool contains(T* ptr) const { return Lookup(ptr)->is_valid(); }
112 
113   // Try to remove a pointer from the set. Return true in case of
114   // removal, or false if the pointer was not in the set.
erase(T * ptr)115   bool erase(T* ptr) {
116     NodeType* node = Lookup(ptr);
117     if (!node->is_valid())
118       return false;
119     node->ptr_ = node->MakeTombstone();
120     UpdateAfterRemoval();
121     return true;
122   }
123 
124   // Same as add() but does not return a boolean.
125   // This minimizes code changes when PointerSet<> replaces
126   // other standard set types in the code.
insert(T * ptr)127   void insert(T* ptr) { add(ptr); }
128 
129   // Range insertion.
130   template <typename InputIter>
insert(InputIter first,InputIter last)131   void insert(InputIter first, InputIter last) {
132     for (; first != last; ++first)
133       add(*first);
134   }
135 
136   // Insert of aitems of |other| into the current set. This
137   // is slightly more efficient than using range insertion
138   // with insert(other.begin(), other.end()).
insert(const PointerSet & other)139   void insert(const PointerSet& other) {
140     for (const_iterator iter = other.begin(); iter.valid(); ++iter)
141       add(*iter);
142   }
143 
144   // Return a new set that is the intersection of the current one
145   // and |other|.
intersection_with(const PointerSet & other)146   PointerSet intersection_with(const PointerSet& other) const {
147     PointerSet result;
148     for (const_iterator iter = other.begin(); iter.valid(); ++iter) {
149       if (contains(*iter))
150         result.add(*iter);
151     }
152     return result;
153   }
154 
155   // Only provide const interators for pointer sets.
156   struct const_iterator : public NodeIterator {
157     T* const* operator->() const {
158       return &const_cast<T*>(static_cast<const T*>(node_->ptr_));
159     }
160     T* operator*() const {
161       return const_cast<T*>(static_cast<const T*>(node_->ptr_));
162     }
163 
164     // The following allows:
165     // std::vector<T*> vector(set.begin(), set.end());
166     using iterator_category = std::forward_iterator_tag;
167     using difference_type = std::ptrdiff_t;
168     using value_type = T*;
169     using pointer = T**;
170     using reference = T*&;
171   };
172 
begin()173   const_iterator begin() const { return {NodeBegin()}; }
end()174   const_iterator end() const { return {NodeEnd()}; }
175 
176   // Only used for unit-tests so performance is not critical.
177   bool operator==(const PointerSet& other) const {
178     if (size() != other.size())
179       return false;
180 
181     for (const_iterator iter = begin(); iter.valid(); ++iter)
182       if (!other.contains(*iter))
183         return false;
184 
185     for (const_iterator iter = other.begin(); iter.valid(); ++iter)
186       if (!contains(*iter))
187         return false;
188 
189     return true;
190   }
191 
192   // Convert this to a vector, more convenient and slightly faster than using
193   // std::vector<T*>(set.begin(), set.end()).
ToVector()194   std::vector<T*> ToVector() const {
195     std::vector<T*> result(this->size());
196     auto it_result = result.begin();
197     for (auto it = this->begin(); it.valid(); ++it)
198       *it_result++ = *it;
199     return result;
200   }
201 
202  private:
203   // Lookup node matching a given pointer. If result->valid() is true
204   // then the pointer was found, otherwise, this is the location of
205   // the best place to insert it.
Lookup(T * ptr)206   NodeType* Lookup(T* ptr) const {
207     size_t hash = NodeType::MakeHash(ptr);
208     return NodeLookup(hash, [&](NodeType* node) { return node->ptr_ == ptr; });
209   }
210 };
211 
212 #endif  // SRC_GN_POINTER_SET_H_
213