xref: /freebsd/contrib/llvm-project/llvm/include/llvm/ADT/SmallPtrSet.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===- llvm/ADT/SmallPtrSet.h - 'Normally small' pointer set ----*- 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 SmallPtrSet class.  See the doxygen comment for
11 /// SmallPtrSetImplBase for more details on the algorithm used.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef LLVM_ADT_SMALLPTRSET_H
16 #define LLVM_ADT_SMALLPTRSET_H
17 
18 #include "llvm/ADT/EpochTracker.h"
19 #include "llvm/Support/Compiler.h"
20 #include "llvm/Support/ReverseIteration.h"
21 #include "llvm/Support/type_traits.h"
22 #include <cassert>
23 #include <cstddef>
24 #include <cstdlib>
25 #include <cstring>
26 #include <initializer_list>
27 #include <iterator>
28 #include <limits>
29 #include <utility>
30 
31 namespace llvm {
32 
33 /// SmallPtrSetImplBase - This is the common code shared among all the
34 /// SmallPtrSet<>'s, which is almost everything.  SmallPtrSet has two modes, one
35 /// for small and one for large sets.
36 ///
37 /// Small sets use an array of pointers allocated in the SmallPtrSet object,
38 /// which is treated as a simple array of pointers.  When a pointer is added to
39 /// the set, the array is scanned to see if the element already exists, if not
40 /// the element is 'pushed back' onto the array.  If we run out of space in the
41 /// array, we grow into the 'large set' case.  SmallSet should be used when the
42 /// sets are often small.  In this case, no memory allocation is used, and only
43 /// light-weight and cache-efficient scanning is used.
44 ///
45 /// Large sets use a classic exponentially-probed hash table.  Empty buckets are
46 /// represented with an illegal pointer value (-1) to allow null pointers to be
47 /// inserted.  Tombstones are represented with another illegal pointer value
48 /// (-2), to allow deletion.  The hash table is resized when the table is 3/4 or
49 /// more.  When this happens, the table is doubled in size.
50 ///
51 class SmallPtrSetImplBase : public DebugEpochBase {
52   friend class SmallPtrSetIteratorImpl;
53 
54 protected:
55   /// SmallArray - Points to a fixed size set of buckets, used in 'small mode'.
56   const void **SmallArray;
57   /// CurArray - This is the current set of buckets.  If equal to SmallArray,
58   /// then the set is in 'small mode'.
59   const void **CurArray;
60   /// CurArraySize - The allocated size of CurArray, always a power of two.
61   unsigned CurArraySize;
62 
63   /// Number of elements in CurArray that contain a value or are a tombstone.
64   /// If small, all these elements are at the beginning of CurArray and the rest
65   /// is uninitialized.
66   unsigned NumNonEmpty;
67   /// Number of tombstones in CurArray.
68   unsigned NumTombstones;
69 
70   // Helpers to copy and move construct a SmallPtrSet.
71   SmallPtrSetImplBase(const void **SmallStorage,
72                       const SmallPtrSetImplBase &that);
73   SmallPtrSetImplBase(const void **SmallStorage, unsigned SmallSize,
74                       SmallPtrSetImplBase &&that);
75 
SmallPtrSetImplBase(const void ** SmallStorage,unsigned SmallSize)76   explicit SmallPtrSetImplBase(const void **SmallStorage, unsigned SmallSize)
77       : SmallArray(SmallStorage), CurArray(SmallStorage),
78         CurArraySize(SmallSize), NumNonEmpty(0), NumTombstones(0) {
79     assert(SmallSize && (SmallSize & (SmallSize-1)) == 0 &&
80            "Initial size must be a power of two!");
81   }
82 
~SmallPtrSetImplBase()83   ~SmallPtrSetImplBase() {
84     if (!isSmall())
85       free(CurArray);
86   }
87 
88 public:
89   using size_type = unsigned;
90 
91   SmallPtrSetImplBase &operator=(const SmallPtrSetImplBase &) = delete;
92 
empty()93   [[nodiscard]] bool empty() const { return size() == 0; }
size()94   size_type size() const { return NumNonEmpty - NumTombstones; }
95 
clear()96   void clear() {
97     incrementEpoch();
98     // If the capacity of the array is huge, and the # elements used is small,
99     // shrink the array.
100     if (!isSmall()) {
101       if (size() * 4 < CurArraySize && CurArraySize > 32)
102         return shrink_and_clear();
103       // Fill the array with empty markers.
104       memset(CurArray, -1, CurArraySize * sizeof(void *));
105     }
106 
107     NumNonEmpty = 0;
108     NumTombstones = 0;
109   }
110 
111 protected:
getTombstoneMarker()112   static void *getTombstoneMarker() { return reinterpret_cast<void*>(-2); }
113 
getEmptyMarker()114   static void *getEmptyMarker() {
115     // Note that -1 is chosen to make clear() efficiently implementable with
116     // memset and because it's not a valid pointer value.
117     return reinterpret_cast<void*>(-1);
118   }
119 
EndPointer()120   const void **EndPointer() const {
121     return isSmall() ? CurArray + NumNonEmpty : CurArray + CurArraySize;
122   }
123 
124   /// insert_imp - This returns true if the pointer was new to the set, false if
125   /// it was already in the set.  This is hidden from the client so that the
126   /// derived class can check that the right type of pointer is passed in.
insert_imp(const void * Ptr)127   std::pair<const void *const *, bool> insert_imp(const void *Ptr) {
128     if (isSmall()) {
129       // Check to see if it is already in the set.
130       for (const void **APtr = SmallArray, **E = SmallArray + NumNonEmpty;
131            APtr != E; ++APtr) {
132         const void *Value = *APtr;
133         if (Value == Ptr)
134           return std::make_pair(APtr, false);
135       }
136 
137       // Nope, there isn't.  If we stay small, just 'pushback' now.
138       if (NumNonEmpty < CurArraySize) {
139         SmallArray[NumNonEmpty++] = Ptr;
140         incrementEpoch();
141         return std::make_pair(SmallArray + (NumNonEmpty - 1), true);
142       }
143       // Otherwise, hit the big set case, which will call grow.
144     }
145     return insert_imp_big(Ptr);
146   }
147 
148   /// erase_imp - If the set contains the specified pointer, remove it and
149   /// return true, otherwise return false.  This is hidden from the client so
150   /// that the derived class can check that the right type of pointer is passed
151   /// in.
erase_imp(const void * Ptr)152   bool erase_imp(const void * Ptr) {
153     if (isSmall()) {
154       for (const void **APtr = SmallArray, **E = SmallArray + NumNonEmpty;
155            APtr != E; ++APtr) {
156         if (*APtr == Ptr) {
157           *APtr = SmallArray[--NumNonEmpty];
158           incrementEpoch();
159           return true;
160         }
161       }
162       return false;
163     }
164 
165     auto *Bucket = FindBucketFor(Ptr);
166     if (*Bucket != Ptr)
167       return false;
168 
169     *const_cast<const void **>(Bucket) = getTombstoneMarker();
170     NumTombstones++;
171     // Treat this consistently from an API perspective, even if we don't
172     // actually invalidate iterators here.
173     incrementEpoch();
174     return true;
175   }
176 
177   /// Returns the raw pointer needed to construct an iterator.  If element not
178   /// found, this will be EndPointer.  Otherwise, it will be a pointer to the
179   /// slot which stores Ptr;
find_imp(const void * Ptr)180   const void *const * find_imp(const void * Ptr) const {
181     if (isSmall()) {
182       // Linear search for the item.
183       for (const void *const *APtr = SmallArray,
184                       *const *E = SmallArray + NumNonEmpty; APtr != E; ++APtr)
185         if (*APtr == Ptr)
186           return APtr;
187       return EndPointer();
188     }
189 
190     // Big set case.
191     auto *Bucket = FindBucketFor(Ptr);
192     if (*Bucket == Ptr)
193       return Bucket;
194     return EndPointer();
195   }
196 
isSmall()197   bool isSmall() const { return CurArray == SmallArray; }
198 
199 private:
200   std::pair<const void *const *, bool> insert_imp_big(const void *Ptr);
201 
202   const void * const *FindBucketFor(const void *Ptr) const;
203   void shrink_and_clear();
204 
205   /// Grow - Allocate a larger backing store for the buckets and move it over.
206   void Grow(unsigned NewSize);
207 
208 protected:
209   /// swap - Swaps the elements of two sets.
210   /// Note: This method assumes that both sets have the same small size.
211   void swap(SmallPtrSetImplBase &RHS);
212 
213   void CopyFrom(const SmallPtrSetImplBase &RHS);
214   void MoveFrom(unsigned SmallSize, SmallPtrSetImplBase &&RHS);
215 
216 private:
217   /// Code shared by MoveFrom() and move constructor.
218   void MoveHelper(unsigned SmallSize, SmallPtrSetImplBase &&RHS);
219   /// Code shared by CopyFrom() and copy constructor.
220   void CopyHelper(const SmallPtrSetImplBase &RHS);
221 };
222 
223 /// SmallPtrSetIteratorImpl - This is the common base class shared between all
224 /// instances of SmallPtrSetIterator.
225 class SmallPtrSetIteratorImpl {
226 protected:
227   const void *const *Bucket;
228   const void *const *End;
229 
230 public:
SmallPtrSetIteratorImpl(const void * const * BP,const void * const * E)231   explicit SmallPtrSetIteratorImpl(const void *const *BP, const void*const *E)
232     : Bucket(BP), End(E) {
233     if (shouldReverseIterate()) {
234       RetreatIfNotValid();
235       return;
236     }
237     AdvanceIfNotValid();
238   }
239 
240   bool operator==(const SmallPtrSetIteratorImpl &RHS) const {
241     return Bucket == RHS.Bucket;
242   }
243   bool operator!=(const SmallPtrSetIteratorImpl &RHS) const {
244     return Bucket != RHS.Bucket;
245   }
246 
247 protected:
248   /// AdvanceIfNotValid - If the current bucket isn't valid, advance to a bucket
249   /// that is.   This is guaranteed to stop because the end() bucket is marked
250   /// valid.
AdvanceIfNotValid()251   void AdvanceIfNotValid() {
252     assert(Bucket <= End);
253     while (Bucket != End &&
254            (*Bucket == SmallPtrSetImplBase::getEmptyMarker() ||
255             *Bucket == SmallPtrSetImplBase::getTombstoneMarker()))
256       ++Bucket;
257   }
RetreatIfNotValid()258   void RetreatIfNotValid() {
259     assert(Bucket >= End);
260     while (Bucket != End &&
261            (Bucket[-1] == SmallPtrSetImplBase::getEmptyMarker() ||
262             Bucket[-1] == SmallPtrSetImplBase::getTombstoneMarker())) {
263       --Bucket;
264     }
265   }
266 };
267 
268 /// SmallPtrSetIterator - This implements a const_iterator for SmallPtrSet.
269 template <typename PtrTy>
270 class LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE SmallPtrSetIterator
271     : public SmallPtrSetIteratorImpl,
272       DebugEpochBase::HandleBase {
273   using PtrTraits = PointerLikeTypeTraits<PtrTy>;
274 
275 public:
276   using value_type = PtrTy;
277   using reference = PtrTy;
278   using pointer = PtrTy;
279   using difference_type = std::ptrdiff_t;
280   using iterator_category = std::forward_iterator_tag;
281 
SmallPtrSetIterator(const void * const * BP,const void * const * E,const DebugEpochBase & Epoch)282   explicit SmallPtrSetIterator(const void *const *BP, const void *const *E,
283                                const DebugEpochBase &Epoch)
284       : SmallPtrSetIteratorImpl(BP, E), DebugEpochBase::HandleBase(&Epoch) {}
285 
286   // Most methods are provided by the base class.
287 
288   const PtrTy operator*() const {
289     assert(isHandleInSync() && "invalid iterator access!");
290     if (shouldReverseIterate()) {
291       assert(Bucket > End);
292       return PtrTraits::getFromVoidPointer(const_cast<void *>(Bucket[-1]));
293     }
294     assert(Bucket < End);
295     return PtrTraits::getFromVoidPointer(const_cast<void*>(*Bucket));
296   }
297 
298   inline SmallPtrSetIterator& operator++() {          // Preincrement
299     assert(isHandleInSync() && "invalid iterator access!");
300     if (shouldReverseIterate()) {
301       --Bucket;
302       RetreatIfNotValid();
303       return *this;
304     }
305     ++Bucket;
306     AdvanceIfNotValid();
307     return *this;
308   }
309 
310   SmallPtrSetIterator operator++(int) {        // Postincrement
311     SmallPtrSetIterator tmp = *this;
312     ++*this;
313     return tmp;
314   }
315 };
316 
317 /// A templated base class for \c SmallPtrSet which provides the
318 /// typesafe interface that is common across all small sizes.
319 ///
320 /// This is particularly useful for passing around between interface boundaries
321 /// to avoid encoding a particular small size in the interface boundary.
322 template <typename PtrType>
323 class SmallPtrSetImpl : public SmallPtrSetImplBase {
324   using ConstPtrType = typename add_const_past_pointer<PtrType>::type;
325   using PtrTraits = PointerLikeTypeTraits<PtrType>;
326   using ConstPtrTraits = PointerLikeTypeTraits<ConstPtrType>;
327 
328 protected:
329   // Forward constructors to the base.
330   using SmallPtrSetImplBase::SmallPtrSetImplBase;
331 
332 public:
333   using iterator = SmallPtrSetIterator<PtrType>;
334   using const_iterator = SmallPtrSetIterator<PtrType>;
335   using key_type = ConstPtrType;
336   using value_type = PtrType;
337 
338   SmallPtrSetImpl(const SmallPtrSetImpl &) = delete;
339 
340   /// Inserts Ptr if and only if there is no element in the container equal to
341   /// Ptr. The bool component of the returned pair is true if and only if the
342   /// insertion takes place, and the iterator component of the pair points to
343   /// the element equal to Ptr.
insert(PtrType Ptr)344   std::pair<iterator, bool> insert(PtrType Ptr) {
345     auto p = insert_imp(PtrTraits::getAsVoidPointer(Ptr));
346     return std::make_pair(makeIterator(p.first), p.second);
347   }
348 
349   /// Insert the given pointer with an iterator hint that is ignored. This is
350   /// identical to calling insert(Ptr), but allows SmallPtrSet to be used by
351   /// std::insert_iterator and std::inserter().
insert(iterator,PtrType Ptr)352   iterator insert(iterator, PtrType Ptr) {
353     return insert(Ptr).first;
354   }
355 
356   /// Remove pointer from the set.
357   ///
358   /// Returns whether the pointer was in the set. Invalidates iterators if
359   /// true is returned. To remove elements while iterating over the set, use
360   /// remove_if() instead.
erase(PtrType Ptr)361   bool erase(PtrType Ptr) {
362     return erase_imp(PtrTraits::getAsVoidPointer(Ptr));
363   }
364 
365   /// Remove elements that match the given predicate.
366   ///
367   /// This method is a safe replacement for the following pattern, which is not
368   /// valid, because the erase() calls would invalidate the iterator:
369   ///
370   ///     for (PtrType *Ptr : Set)
371   ///       if (Pred(P))
372   ///         Set.erase(P);
373   ///
374   /// Returns whether anything was removed. It is safe to read the set inside
375   /// the predicate function. However, the predicate must not modify the set
376   /// itself, only indicate a removal by returning true.
377   template <typename UnaryPredicate>
remove_if(UnaryPredicate P)378   bool remove_if(UnaryPredicate P) {
379     bool Removed = false;
380     if (isSmall()) {
381       const void **APtr = SmallArray, **E = SmallArray + NumNonEmpty;
382       while (APtr != E) {
383         PtrType Ptr = PtrTraits::getFromVoidPointer(const_cast<void *>(*APtr));
384         if (P(Ptr)) {
385           *APtr = *--E;
386           --NumNonEmpty;
387           incrementEpoch();
388           Removed = true;
389         } else {
390           ++APtr;
391         }
392       }
393       return Removed;
394     }
395 
396     for (const void **APtr = CurArray, **E = EndPointer(); APtr != E; ++APtr) {
397       const void *Value = *APtr;
398       if (Value == getTombstoneMarker() || Value == getEmptyMarker())
399         continue;
400       PtrType Ptr = PtrTraits::getFromVoidPointer(const_cast<void *>(Value));
401       if (P(Ptr)) {
402         *APtr = getTombstoneMarker();
403         ++NumTombstones;
404         incrementEpoch();
405         Removed = true;
406       }
407     }
408     return Removed;
409   }
410 
411   /// count - Return 1 if the specified pointer is in the set, 0 otherwise.
count(ConstPtrType Ptr)412   size_type count(ConstPtrType Ptr) const {
413     return find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)) != EndPointer();
414   }
find(ConstPtrType Ptr)415   iterator find(ConstPtrType Ptr) const {
416     return makeIterator(find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)));
417   }
contains(ConstPtrType Ptr)418   bool contains(ConstPtrType Ptr) const {
419     return find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)) != EndPointer();
420   }
421 
422   template <typename IterT>
insert(IterT I,IterT E)423   void insert(IterT I, IterT E) {
424     for (; I != E; ++I)
425       insert(*I);
426   }
427 
insert(std::initializer_list<PtrType> IL)428   void insert(std::initializer_list<PtrType> IL) {
429     insert(IL.begin(), IL.end());
430   }
431 
begin()432   iterator begin() const {
433     if (shouldReverseIterate())
434       return makeIterator(EndPointer() - 1);
435     return makeIterator(CurArray);
436   }
end()437   iterator end() const { return makeIterator(EndPointer()); }
438 
439 private:
440   /// Create an iterator that dereferences to same place as the given pointer.
makeIterator(const void * const * P)441   iterator makeIterator(const void *const *P) const {
442     if (shouldReverseIterate())
443       return iterator(P == EndPointer() ? CurArray : P + 1, CurArray, *this);
444     return iterator(P, EndPointer(), *this);
445   }
446 };
447 
448 /// Equality comparison for SmallPtrSet.
449 ///
450 /// Iterates over elements of LHS confirming that each value from LHS is also in
451 /// RHS, and that no additional values are in RHS.
452 template <typename PtrType>
453 bool operator==(const SmallPtrSetImpl<PtrType> &LHS,
454                 const SmallPtrSetImpl<PtrType> &RHS) {
455   if (LHS.size() != RHS.size())
456     return false;
457 
458   for (const auto *KV : LHS)
459     if (!RHS.count(KV))
460       return false;
461 
462   return true;
463 }
464 
465 /// Inequality comparison for SmallPtrSet.
466 ///
467 /// Equivalent to !(LHS == RHS).
468 template <typename PtrType>
469 bool operator!=(const SmallPtrSetImpl<PtrType> &LHS,
470                 const SmallPtrSetImpl<PtrType> &RHS) {
471   return !(LHS == RHS);
472 }
473 
474 /// SmallPtrSet - This class implements a set which is optimized for holding
475 /// SmallSize or less elements.  This internally rounds up SmallSize to the next
476 /// power of two if it is not already a power of two.  See the comments above
477 /// SmallPtrSetImplBase for details of the algorithm.
478 template<class PtrType, unsigned SmallSize>
479 class SmallPtrSet : public SmallPtrSetImpl<PtrType> {
480   // In small mode SmallPtrSet uses linear search for the elements, so it is
481   // not a good idea to choose this value too high. You may consider using a
482   // DenseSet<> instead if you expect many elements in the set.
483   static_assert(SmallSize <= 32, "SmallSize should be small");
484 
485   using BaseT = SmallPtrSetImpl<PtrType>;
486 
487   // A constexpr version of llvm::bit_ceil.
488   // TODO: Replace this with std::bit_ceil once C++20 is available.
RoundUpToPowerOfTwo(size_t X)489   static constexpr size_t RoundUpToPowerOfTwo(size_t X) {
490     size_t C = 1;
491     size_t CMax = C << (std::numeric_limits<size_t>::digits - 1);
492     while (C < X && C < CMax)
493       C <<= 1;
494     return C;
495   }
496 
497   // Make sure that SmallSize is a power of two, round up if not.
498   static constexpr size_t SmallSizePowTwo = RoundUpToPowerOfTwo(SmallSize);
499   /// SmallStorage - Fixed size storage used in 'small mode'.
500   const void *SmallStorage[SmallSizePowTwo];
501 
502 public:
SmallPtrSet()503   SmallPtrSet() : BaseT(SmallStorage, SmallSizePowTwo) {}
SmallPtrSet(const SmallPtrSet & that)504   SmallPtrSet(const SmallPtrSet &that) : BaseT(SmallStorage, that) {}
SmallPtrSet(SmallPtrSet && that)505   SmallPtrSet(SmallPtrSet &&that)
506       : BaseT(SmallStorage, SmallSizePowTwo, std::move(that)) {}
507 
508   template<typename It>
SmallPtrSet(It I,It E)509   SmallPtrSet(It I, It E) : BaseT(SmallStorage, SmallSizePowTwo) {
510     this->insert(I, E);
511   }
512 
SmallPtrSet(std::initializer_list<PtrType> IL)513   SmallPtrSet(std::initializer_list<PtrType> IL)
514       : BaseT(SmallStorage, SmallSizePowTwo) {
515     this->insert(IL.begin(), IL.end());
516   }
517 
518   SmallPtrSet<PtrType, SmallSize> &
519   operator=(const SmallPtrSet<PtrType, SmallSize> &RHS) {
520     if (&RHS != this)
521       this->CopyFrom(RHS);
522     return *this;
523   }
524 
525   SmallPtrSet<PtrType, SmallSize> &
526   operator=(SmallPtrSet<PtrType, SmallSize> &&RHS) {
527     if (&RHS != this)
528       this->MoveFrom(SmallSizePowTwo, std::move(RHS));
529     return *this;
530   }
531 
532   SmallPtrSet<PtrType, SmallSize> &
533   operator=(std::initializer_list<PtrType> IL) {
534     this->clear();
535     this->insert(IL.begin(), IL.end());
536     return *this;
537   }
538 
539   /// swap - Swaps the elements of two sets.
swap(SmallPtrSet<PtrType,SmallSize> & RHS)540   void swap(SmallPtrSet<PtrType, SmallSize> &RHS) {
541     SmallPtrSetImplBase::swap(RHS);
542   }
543 };
544 
545 } // end namespace llvm
546 
547 namespace std {
548 
549   /// Implement std::swap in terms of SmallPtrSet swap.
550   template<class T, unsigned N>
swap(llvm::SmallPtrSet<T,N> & LHS,llvm::SmallPtrSet<T,N> & RHS)551   inline void swap(llvm::SmallPtrSet<T, N> &LHS, llvm::SmallPtrSet<T, N> &RHS) {
552     LHS.swap(RHS);
553   }
554 
555 } // end namespace std
556 
557 #endif // LLVM_ADT_SMALLPTRSET_H
558