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