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