xref: /freebsd/contrib/llvm-project/llvm/include/llvm/ADT/SmallSet.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===- llvm/ADT/SmallSet.h - 'Normally small' sets --------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// This file defines the SmallSet class.
11 ///
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_ADT_SMALLSET_H
15 #define LLVM_ADT_SMALLSET_H
16 
17 #include "llvm/ADT/ADL.h"
18 #include "llvm/ADT/STLForwardCompat.h"
19 #include "llvm/ADT/SmallPtrSet.h"
20 #include "llvm/ADT/SmallVector.h"
21 #include "llvm/ADT/iterator.h"
22 #include <cstddef>
23 #include <functional>
24 #include <initializer_list>
25 #include <set>
26 #include <utility>
27 
28 namespace llvm {
29 
30 /// SmallSetIterator - This class implements a const_iterator for SmallSet by
31 /// delegating to the underlying SmallVector or Set iterators.
32 template <typename T, unsigned N, typename C>
33 class SmallSetIterator
34     : public iterator_facade_base<SmallSetIterator<T, N, C>,
35                                   std::forward_iterator_tag, T> {
36 private:
37   using SetIterTy = typename std::set<T, C>::const_iterator;
38   using VecIterTy = typename SmallVector<T, N>::const_iterator;
39   using SelfTy = SmallSetIterator<T, N, C>;
40 
41   /// Iterators to the parts of the SmallSet containing the data. They are set
42   /// depending on isSmall.
43   union {
44     SetIterTy SetIter;
45     VecIterTy VecIter;
46   };
47 
48   bool IsSmall;
49 
50 public:
SmallSetIterator(SetIterTy SetIter)51   SmallSetIterator(SetIterTy SetIter) : SetIter(SetIter), IsSmall(false) {}
52 
SmallSetIterator(VecIterTy VecIter)53   SmallSetIterator(VecIterTy VecIter) : VecIter(VecIter), IsSmall(true) {}
54 
55   // Spell out destructor, copy/move constructor and assignment operators for
56   // MSVC STL, where set<T>::const_iterator is not trivially copy constructible.
~SmallSetIterator()57   ~SmallSetIterator() {
58     if (IsSmall)
59       VecIter.~VecIterTy();
60     else
61       SetIter.~SetIterTy();
62   }
63 
SmallSetIterator(const SmallSetIterator & Other)64   SmallSetIterator(const SmallSetIterator &Other) : IsSmall(Other.IsSmall) {
65     if (IsSmall)
66       VecIter = Other.VecIter;
67     else
68       // Use placement new, to make sure SetIter is properly constructed, even
69       // if it is not trivially copy-able (e.g. in MSVC).
70       new (&SetIter) SetIterTy(Other.SetIter);
71   }
72 
SmallSetIterator(SmallSetIterator && Other)73   SmallSetIterator(SmallSetIterator &&Other) : IsSmall(Other.IsSmall) {
74     if (IsSmall)
75       VecIter = std::move(Other.VecIter);
76     else
77       // Use placement new, to make sure SetIter is properly constructed, even
78       // if it is not trivially copy-able (e.g. in MSVC).
79       new (&SetIter) SetIterTy(std::move(Other.SetIter));
80   }
81 
82   SmallSetIterator& operator=(const SmallSetIterator& Other) {
83     // Call destructor for SetIter, so it gets properly destroyed if it is
84     // not trivially destructible in case we are setting VecIter.
85     if (!IsSmall)
86       SetIter.~SetIterTy();
87 
88     IsSmall = Other.IsSmall;
89     if (IsSmall)
90       VecIter = Other.VecIter;
91     else
92       new (&SetIter) SetIterTy(Other.SetIter);
93     return *this;
94   }
95 
96   SmallSetIterator& operator=(SmallSetIterator&& Other) {
97     // Call destructor for SetIter, so it gets properly destroyed if it is
98     // not trivially destructible in case we are setting VecIter.
99     if (!IsSmall)
100       SetIter.~SetIterTy();
101 
102     IsSmall = Other.IsSmall;
103     if (IsSmall)
104       VecIter = std::move(Other.VecIter);
105     else
106       new (&SetIter) SetIterTy(std::move(Other.SetIter));
107     return *this;
108   }
109 
110   bool operator==(const SmallSetIterator &RHS) const {
111     if (IsSmall != RHS.IsSmall)
112       return false;
113     if (IsSmall)
114       return VecIter == RHS.VecIter;
115     return SetIter == RHS.SetIter;
116   }
117 
118   SmallSetIterator &operator++() { // Preincrement
119     if (IsSmall)
120       ++VecIter;
121     else
122       ++SetIter;
123     return *this;
124   }
125 
126   const T &operator*() const { return IsSmall ? *VecIter : *SetIter; }
127 };
128 
129 /// SmallSet - This maintains a set of unique values, optimizing for the case
130 /// when the set is small (less than N).  In this case, the set can be
131 /// maintained with no mallocs.  If the set gets large, we expand to using an
132 /// std::set to maintain reasonable lookup times.
133 template <typename T, unsigned N, typename C = std::less<T>>
134 class SmallSet {
135   /// Use a SmallVector to hold the elements here (even though it will never
136   /// reach its 'large' stage) to avoid calling the default ctors of elements
137   /// we will never use.
138   SmallVector<T, N> Vector;
139   std::set<T, C> Set;
140 
141   // In small mode SmallPtrSet uses linear search for the elements, so it is
142   // not a good idea to choose this value too high. You may consider using a
143   // DenseSet<> instead if you expect many elements in the set.
144   static_assert(N <= 32, "N should be small");
145 
146 public:
147   using key_type = T;
148   using size_type = size_t;
149   using value_type = T;
150   using const_iterator = SmallSetIterator<T, N, C>;
151 
152   SmallSet() = default;
153   SmallSet(const SmallSet &) = default;
154   SmallSet(SmallSet &&) = default;
155 
SmallSet(IterT Begin,IterT End)156   template <typename IterT> SmallSet(IterT Begin, IterT End) {
157     insert(Begin, End);
158   }
159 
160   template <typename Range>
SmallSet(llvm::from_range_t,Range && R)161   SmallSet(llvm::from_range_t, Range &&R)
162       : SmallSet(adl_begin(R), adl_end(R)) {}
163 
SmallSet(std::initializer_list<T> L)164   SmallSet(std::initializer_list<T> L) { insert(L.begin(), L.end()); }
165 
166   SmallSet &operator=(const SmallSet &) = default;
167   SmallSet &operator=(SmallSet &&) = default;
168 
empty()169   [[nodiscard]] bool empty() const { return Vector.empty() && Set.empty(); }
170 
size()171   size_type size() const {
172     return isSmall() ? Vector.size() : Set.size();
173   }
174 
175   /// count - Return 1 if the element is in the set, 0 otherwise.
count(const T & V)176   size_type count(const T &V) const { return contains(V) ? 1 : 0; }
177 
178   /// insert - Insert an element into the set if it isn't already there.
179   /// Returns a pair. The first value of it is an iterator to the inserted
180   /// element or the existing element in the set. The second value is true
181   /// if the element is inserted (it was not in the set before).
insert(const T & V)182   std::pair<const_iterator, bool> insert(const T &V) { return insertImpl(V); }
183 
insert(T && V)184   std::pair<const_iterator, bool> insert(T &&V) {
185     return insertImpl(std::move(V));
186   }
187 
188   template <typename IterT>
insert(IterT I,IterT E)189   void insert(IterT I, IterT E) {
190     for (; I != E; ++I)
191       insert(*I);
192   }
193 
insert_range(Range && R)194   template <typename Range> void insert_range(Range &&R) {
195     insert(adl_begin(R), adl_end(R));
196   }
197 
erase(const T & V)198   bool erase(const T &V) {
199     if (!isSmall())
200       return Set.erase(V);
201     auto I = vfind(V);
202     if (I != Vector.end()) {
203       Vector.erase(I);
204       return true;
205     }
206     return false;
207   }
208 
clear()209   void clear() {
210     Vector.clear();
211     Set.clear();
212   }
213 
begin()214   const_iterator begin() const {
215     if (isSmall())
216       return {Vector.begin()};
217     return {Set.begin()};
218   }
219 
end()220   const_iterator end() const {
221     if (isSmall())
222       return {Vector.end()};
223     return {Set.end()};
224   }
225 
226   /// Check if the SmallSet contains the given element.
contains(const T & V)227   bool contains(const T &V) const {
228     if (isSmall())
229       return vfind(V) != Vector.end();
230     return Set.find(V) != Set.end();
231   }
232 
233 private:
isSmall()234   bool isSmall() const { return Set.empty(); }
235 
236   template <typename ArgType>
insertImpl(ArgType && V)237   std::pair<const_iterator, bool> insertImpl(ArgType &&V) {
238     static_assert(std::is_convertible_v<ArgType, T>,
239                   "ArgType must be convertible to T!");
240     if (!isSmall()) {
241       auto [I, Inserted] = Set.insert(std::forward<ArgType>(V));
242       return {const_iterator(I), Inserted};
243     }
244 
245     auto I = vfind(V);
246     if (I != Vector.end()) // Don't reinsert if it already exists.
247       return {const_iterator(I), false};
248     if (Vector.size() < N) {
249       Vector.push_back(std::forward<ArgType>(V));
250       return {const_iterator(std::prev(Vector.end())), true};
251     }
252     // Otherwise, grow from vector to set.
253     Set.insert(std::make_move_iterator(Vector.begin()),
254                std::make_move_iterator(Vector.end()));
255     Vector.clear();
256     return {const_iterator(Set.insert(std::forward<ArgType>(V)).first), true};
257   }
258 
259   // Handwritten linear search. The use of std::find might hurt performance as
260   // its implementation may be optimized for larger containers.
vfind(const T & V)261   typename SmallVector<T, N>::const_iterator vfind(const T &V) const {
262     for (auto I = Vector.begin(), E = Vector.end(); I != E; ++I)
263       if (*I == V)
264         return I;
265     return Vector.end();
266   }
267 };
268 
269 /// If this set is of pointer values, transparently switch over to using
270 /// SmallPtrSet for performance.
271 template <typename PointeeType, unsigned N>
272 class SmallSet<PointeeType*, N> : public SmallPtrSet<PointeeType*, N> {};
273 
274 /// Equality comparison for SmallSet.
275 ///
276 /// Iterates over elements of LHS confirming that each element is also a member
277 /// of RHS, and that RHS contains no additional values.
278 /// Equivalent to N calls to RHS.count.
279 /// For small-set mode amortized complexity is O(N^2)
280 /// For large-set mode amortized complexity is linear, worst case is O(N^2) (if
281 /// every hash collides).
282 template <typename T, unsigned LN, unsigned RN, typename C>
283 bool operator==(const SmallSet<T, LN, C> &LHS, const SmallSet<T, RN, C> &RHS) {
284   if (LHS.size() != RHS.size())
285     return false;
286 
287   // All elements in LHS must also be in RHS
288   return all_of(LHS, [&RHS](const T &E) { return RHS.count(E); });
289 }
290 
291 /// Inequality comparison for SmallSet.
292 ///
293 /// Equivalent to !(LHS == RHS). See operator== for performance notes.
294 template <typename T, unsigned LN, unsigned RN, typename C>
295 bool operator!=(const SmallSet<T, LN, C> &LHS, const SmallSet<T, RN, C> &RHS) {
296   return !(LHS == RHS);
297 }
298 
299 } // end namespace llvm
300 
301 #endif // LLVM_ADT_SMALLSET_H
302