xref: /llvm-project/compiler-rt/lib/xray/xray_segmented_array.h (revision cac7821438f625d6c8a36dd9363f9acd3a3d93de)
126e81209SDean Michael Berris //===-- xray_segmented_array.h ---------------------------------*- C++ -*-===//
226e81209SDean Michael Berris //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
626e81209SDean Michael Berris //
726e81209SDean Michael Berris //===----------------------------------------------------------------------===//
826e81209SDean Michael Berris //
926e81209SDean Michael Berris // This file is a part of XRay, a dynamic runtime instrumentation system.
1026e81209SDean Michael Berris //
114719c524SDean Michael Berris // Defines the implementation of a segmented array, with fixed-size segments
1226e81209SDean Michael Berris // backing the segments.
1326e81209SDean Michael Berris //
1426e81209SDean Michael Berris //===----------------------------------------------------------------------===//
1526e81209SDean Michael Berris #ifndef XRAY_SEGMENTED_ARRAY_H
1626e81209SDean Michael Berris #define XRAY_SEGMENTED_ARRAY_H
1726e81209SDean Michael Berris 
1826e81209SDean Michael Berris #include "sanitizer_common/sanitizer_allocator.h"
1926e81209SDean Michael Berris #include "xray_allocator.h"
200dd4f9f2SDean Michael Berris #include "xray_utils.h"
210dd4f9f2SDean Michael Berris #include <cassert>
2226e81209SDean Michael Berris #include <type_traits>
2326e81209SDean Michael Berris #include <utility>
2426e81209SDean Michael Berris 
2526e81209SDean Michael Berris namespace __xray {
2626e81209SDean Michael Berris 
2726e81209SDean Michael Berris /// The Array type provides an interface similar to std::vector<...> but does
2826e81209SDean Michael Berris /// not shrink in size. Once constructed, elements can be appended but cannot be
2926e81209SDean Michael Berris /// removed. The implementation is heavily dependent on the contract provided by
3026e81209SDean Michael Berris /// the Allocator type, in that all memory will be released when the Allocator
3126e81209SDean Michael Berris /// is destroyed. When an Array is destroyed, it will destroy elements in the
324719c524SDean Michael Berris /// backing store but will not free the memory.
334719c524SDean Michael Berris template <class T> class Array {
34190c49bcSDean Michael Berris   struct Segment {
35190c49bcSDean Michael Berris     Segment *Prev;
36190c49bcSDean Michael Berris     Segment *Next;
3712be7b7bSDavid Carlier     char Data[1];
384719c524SDean Michael Berris   };
394719c524SDean Michael Berris 
404719c524SDean Michael Berris public:
414719c524SDean Michael Berris   // Each segment of the array will be laid out with the following assumptions:
424719c524SDean Michael Berris   //
434719c524SDean Michael Berris   //   - Each segment will be on a cache-line address boundary (kCacheLineSize
444719c524SDean Michael Berris   //     aligned).
454719c524SDean Michael Berris   //
464719c524SDean Michael Berris   //   - The elements will be accessed through an aligned pointer, dependent on
474719c524SDean Michael Berris   //     the alignment of T.
484719c524SDean Michael Berris   //
494719c524SDean Michael Berris   //   - Each element is at least two-pointers worth from the beginning of the
504719c524SDean Michael Berris   //     Segment, aligned properly, and the rest of the elements are accessed
514719c524SDean Michael Berris   //     through appropriate alignment.
524719c524SDean Michael Berris   //
534719c524SDean Michael Berris   // We then compute the size of the segment to follow this logic:
544719c524SDean Michael Berris   //
554719c524SDean Michael Berris   //   - Compute the number of elements that can fit within
564719c524SDean Michael Berris   //     kCacheLineSize-multiple segments, minus the size of two pointers.
574719c524SDean Michael Berris   //
584719c524SDean Michael Berris   //   - Request cacheline-multiple sized elements from the allocator.
59*cac78214SMarc Auberer   static constexpr uint64_t AlignedElementStorageSize = sizeof(T);
604719c524SDean Michael Berris 
61190c49bcSDean Michael Berris   static constexpr uint64_t SegmentControlBlockSize = sizeof(Segment *) * 2;
62190c49bcSDean Michael Berris 
63190c49bcSDean Michael Berris   static constexpr uint64_t SegmentSize = nearest_boundary(
64190c49bcSDean Michael Berris       SegmentControlBlockSize + next_pow2(sizeof(T)), kCacheLineSize);
654719c524SDean Michael Berris 
664719c524SDean Michael Berris   using AllocatorType = Allocator<SegmentSize>;
674719c524SDean Michael Berris 
68190c49bcSDean Michael Berris   static constexpr uint64_t ElementsPerSegment =
69190c49bcSDean Michael Berris       (SegmentSize - SegmentControlBlockSize) / next_pow2(sizeof(T));
704719c524SDean Michael Berris 
714719c524SDean Michael Berris   static_assert(ElementsPerSegment > 0,
724719c524SDean Michael Berris                 "Must have at least 1 element per segment.");
734719c524SDean Michael Berris 
74190c49bcSDean Michael Berris   static Segment SentinelSegment;
7526e81209SDean Michael Berris 
76190c49bcSDean Michael Berris   using size_type = uint64_t;
77ebfbf890SDean Michael Berris 
7826e81209SDean Michael Berris private:
7926e81209SDean Michael Berris   // This Iterator models a BidirectionalIterator.
8026e81209SDean Michael Berris   template <class U> class Iterator {
81190c49bcSDean Michael Berris     Segment *S = &SentinelSegment;
82190c49bcSDean Michael Berris     uint64_t Offset = 0;
83190c49bcSDean Michael Berris     uint64_t Size = 0;
8426e81209SDean Michael Berris 
8526e81209SDean Michael Berris   public:
Iterator(Segment * IS,uint64_t Off,uint64_t S)86190c49bcSDean Michael Berris     Iterator(Segment *IS, uint64_t Off, uint64_t S) XRAY_NEVER_INSTRUMENT
87edf0f6a7SDean Michael Berris         : S(IS),
88edf0f6a7SDean Michael Berris           Offset(Off),
89edf0f6a7SDean Michael Berris           Size(S) {}
90edf0f6a7SDean Michael Berris     Iterator(const Iterator &) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
91edf0f6a7SDean Michael Berris     Iterator() NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
92edf0f6a7SDean Michael Berris     Iterator(Iterator &&) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
93edf0f6a7SDean Michael Berris     Iterator &operator=(const Iterator &) XRAY_NEVER_INSTRUMENT = default;
94edf0f6a7SDean Michael Berris     Iterator &operator=(Iterator &&) XRAY_NEVER_INSTRUMENT = default;
95edf0f6a7SDean Michael Berris     ~Iterator() XRAY_NEVER_INSTRUMENT = default;
9626e81209SDean Michael Berris 
97edf0f6a7SDean Michael Berris     Iterator &operator++() XRAY_NEVER_INSTRUMENT {
984719c524SDean Michael Berris       if (++Offset % ElementsPerSegment || Offset == Size)
9926e81209SDean Michael Berris         return *this;
10026e81209SDean Michael Berris 
10126e81209SDean Michael Berris       // At this point, we know that Offset % N == 0, so we must advance the
1024719c524SDean Michael Berris       // segment pointer.
1034719c524SDean Michael Berris       DCHECK_EQ(Offset % ElementsPerSegment, 0);
1040dd4f9f2SDean Michael Berris       DCHECK_NE(Offset, Size);
1054719c524SDean Michael Berris       DCHECK_NE(S, &SentinelSegment);
1064719c524SDean Michael Berris       DCHECK_NE(S->Next, &SentinelSegment);
1074719c524SDean Michael Berris       S = S->Next;
1084719c524SDean Michael Berris       DCHECK_NE(S, &SentinelSegment);
10926e81209SDean Michael Berris       return *this;
11026e81209SDean Michael Berris     }
11126e81209SDean Michael Berris 
112edf0f6a7SDean Michael Berris     Iterator &operator--() XRAY_NEVER_INSTRUMENT {
1134719c524SDean Michael Berris       DCHECK_NE(S, &SentinelSegment);
11426e81209SDean Michael Berris       DCHECK_GT(Offset, 0);
11526e81209SDean Michael Berris 
1160dd4f9f2SDean Michael Berris       auto PreviousOffset = Offset--;
1174719c524SDean Michael Berris       if (PreviousOffset != Size && PreviousOffset % ElementsPerSegment == 0) {
1184719c524SDean Michael Berris         DCHECK_NE(S->Prev, &SentinelSegment);
1194719c524SDean Michael Berris         S = S->Prev;
1200dd4f9f2SDean Michael Berris       }
1210dd4f9f2SDean Michael Berris 
12226e81209SDean Michael Berris       return *this;
12326e81209SDean Michael Berris     }
12426e81209SDean Michael Berris 
125edf0f6a7SDean Michael Berris     Iterator operator++(int) XRAY_NEVER_INSTRUMENT {
12626e81209SDean Michael Berris       Iterator Copy(*this);
12726e81209SDean Michael Berris       ++(*this);
12826e81209SDean Michael Berris       return Copy;
12926e81209SDean Michael Berris     }
13026e81209SDean Michael Berris 
131edf0f6a7SDean Michael Berris     Iterator operator--(int) XRAY_NEVER_INSTRUMENT {
13226e81209SDean Michael Berris       Iterator Copy(*this);
13326e81209SDean Michael Berris       --(*this);
13426e81209SDean Michael Berris       return Copy;
13526e81209SDean Michael Berris     }
13626e81209SDean Michael Berris 
13726e81209SDean Michael Berris     template <class V, class W>
138edf0f6a7SDean Michael Berris     friend bool operator==(const Iterator<V> &L,
139edf0f6a7SDean Michael Berris                            const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
1404719c524SDean Michael Berris       return L.S == R.S && L.Offset == R.Offset;
14126e81209SDean Michael Berris     }
14226e81209SDean Michael Berris 
14326e81209SDean Michael Berris     template <class V, class W>
144edf0f6a7SDean Michael Berris     friend bool operator!=(const Iterator<V> &L,
145edf0f6a7SDean Michael Berris                            const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
14626e81209SDean Michael Berris       return !(L == R);
14726e81209SDean Michael Berris     }
14826e81209SDean Michael Berris 
149edf0f6a7SDean Michael Berris     U &operator*() const XRAY_NEVER_INSTRUMENT {
1504719c524SDean Michael Berris       DCHECK_NE(S, &SentinelSegment);
1514719c524SDean Michael Berris       auto RelOff = Offset % ElementsPerSegment;
1524719c524SDean Michael Berris 
1534719c524SDean Michael Berris       // We need to compute the character-aligned pointer, offset from the
1544719c524SDean Michael Berris       // segment's Data location to get the element in the position of Offset.
155190c49bcSDean Michael Berris       auto Base = &S->Data;
1564719c524SDean Michael Berris       auto AlignedOffset = Base + (RelOff * AlignedElementStorageSize);
1574719c524SDean Michael Berris       return *reinterpret_cast<U *>(AlignedOffset);
15826e81209SDean Michael Berris     }
15926e81209SDean Michael Berris 
160edf0f6a7SDean Michael Berris     U *operator->() const XRAY_NEVER_INSTRUMENT { return &(**this); }
16126e81209SDean Michael Berris   };
16226e81209SDean Michael Berris 
163190c49bcSDean Michael Berris   AllocatorType *Alloc;
164190c49bcSDean Michael Berris   Segment *Head;
165190c49bcSDean Michael Berris   Segment *Tail;
166190c49bcSDean Michael Berris 
167190c49bcSDean Michael Berris   // Here we keep track of segments in the freelist, to allow us to re-use
168190c49bcSDean Michael Berris   // segments when elements are trimmed off the end.
169190c49bcSDean Michael Berris   Segment *Freelist;
170190c49bcSDean Michael Berris   uint64_t Size;
171190c49bcSDean Michael Berris 
172190c49bcSDean Michael Berris   // ===============================
173190c49bcSDean Michael Berris   // In the following implementation, we work through the algorithms and the
174190c49bcSDean Michael Berris   // list operations using the following notation:
175190c49bcSDean Michael Berris   //
176190c49bcSDean Michael Berris   //   - pred(s) is the predecessor (previous node accessor) and succ(s) is
177190c49bcSDean Michael Berris   //     the successor (next node accessor).
178190c49bcSDean Michael Berris   //
179190c49bcSDean Michael Berris   //   - S is a sentinel segment, which has the following property:
180190c49bcSDean Michael Berris   //
181190c49bcSDean Michael Berris   //         pred(S) == succ(S) == S
182190c49bcSDean Michael Berris   //
183190c49bcSDean Michael Berris   //   - @ is a loop operator, which can imply pred(s) == s if it appears on
184190c49bcSDean Michael Berris   //     the left of s, or succ(s) == S if it appears on the right of s.
185190c49bcSDean Michael Berris   //
186190c49bcSDean Michael Berris   //   - sL <-> sR : means a bidirectional relation between sL and sR, which
187190c49bcSDean Michael Berris   //     means:
188190c49bcSDean Michael Berris   //
189190c49bcSDean Michael Berris   //         succ(sL) == sR && pred(SR) == sL
190190c49bcSDean Michael Berris   //
191190c49bcSDean Michael Berris   //   - sL -> sR : implies a unidirectional relation between sL and SR,
192190c49bcSDean Michael Berris   //     with the following properties:
193190c49bcSDean Michael Berris   //
194190c49bcSDean Michael Berris   //         succ(sL) == sR
195190c49bcSDean Michael Berris   //
196190c49bcSDean Michael Berris   //     sL <- sR : implies a unidirectional relation between sR and sL,
197190c49bcSDean Michael Berris   //     with the following properties:
198190c49bcSDean Michael Berris   //
199190c49bcSDean Michael Berris   //         pred(sR) == sL
200190c49bcSDean Michael Berris   //
201190c49bcSDean Michael Berris   // ===============================
202190c49bcSDean Michael Berris 
NewSegment()203190c49bcSDean Michael Berris   Segment *NewSegment() XRAY_NEVER_INSTRUMENT {
204190c49bcSDean Michael Berris     // We need to handle the case in which enough elements have been trimmed to
205190c49bcSDean Michael Berris     // allow us to re-use segments we've allocated before. For this we look into
206190c49bcSDean Michael Berris     // the Freelist, to see whether we need to actually allocate new blocks or
207190c49bcSDean Michael Berris     // just re-use blocks we've already seen before.
208190c49bcSDean Michael Berris     if (Freelist != &SentinelSegment) {
209190c49bcSDean Michael Berris       // The current state of lists resemble something like this at this point:
210190c49bcSDean Michael Berris       //
211190c49bcSDean Michael Berris       //   Freelist: @S@<-f0->...<->fN->@S@
212190c49bcSDean Michael Berris       //                  ^ Freelist
213190c49bcSDean Michael Berris       //
214190c49bcSDean Michael Berris       // We want to perform a splice of `f0` from Freelist to a temporary list,
215190c49bcSDean Michael Berris       // which looks like:
216190c49bcSDean Michael Berris       //
217190c49bcSDean Michael Berris       //   Templist: @S@<-f0->@S@
218190c49bcSDean Michael Berris       //                  ^ FreeSegment
219190c49bcSDean Michael Berris       //
220190c49bcSDean Michael Berris       // Our algorithm preconditions are:
221190c49bcSDean Michael Berris       DCHECK_EQ(Freelist->Prev, &SentinelSegment);
222190c49bcSDean Michael Berris 
223190c49bcSDean Michael Berris       // Then the algorithm we implement is:
224190c49bcSDean Michael Berris       //
225190c49bcSDean Michael Berris       //   SFS = Freelist
226190c49bcSDean Michael Berris       //   Freelist = succ(Freelist)
227190c49bcSDean Michael Berris       //   if (Freelist != S)
228190c49bcSDean Michael Berris       //     pred(Freelist) = S
229190c49bcSDean Michael Berris       //   succ(SFS) = S
230190c49bcSDean Michael Berris       //   pred(SFS) = S
231190c49bcSDean Michael Berris       //
232190c49bcSDean Michael Berris       auto *FreeSegment = Freelist;
233190c49bcSDean Michael Berris       Freelist = Freelist->Next;
234190c49bcSDean Michael Berris 
235190c49bcSDean Michael Berris       // Note that we need to handle the case where Freelist is now pointing to
236190c49bcSDean Michael Berris       // S, which we don't want to be overwriting.
237190c49bcSDean Michael Berris       // TODO: Determine whether the cost of the branch is higher than the cost
238190c49bcSDean Michael Berris       // of the blind assignment.
239190c49bcSDean Michael Berris       if (Freelist != &SentinelSegment)
240190c49bcSDean Michael Berris         Freelist->Prev = &SentinelSegment;
241190c49bcSDean Michael Berris 
242190c49bcSDean Michael Berris       FreeSegment->Next = &SentinelSegment;
243190c49bcSDean Michael Berris       FreeSegment->Prev = &SentinelSegment;
244190c49bcSDean Michael Berris 
245190c49bcSDean Michael Berris       // Our postconditions are:
246190c49bcSDean Michael Berris       DCHECK_EQ(Freelist->Prev, &SentinelSegment);
247190c49bcSDean Michael Berris       DCHECK_NE(FreeSegment, &SentinelSegment);
248190c49bcSDean Michael Berris       return FreeSegment;
249190c49bcSDean Michael Berris     }
250190c49bcSDean Michael Berris 
251190c49bcSDean Michael Berris     auto SegmentBlock = Alloc->Allocate();
252190c49bcSDean Michael Berris     if (SegmentBlock.Data == nullptr)
253190c49bcSDean Michael Berris       return nullptr;
254190c49bcSDean Michael Berris 
255190c49bcSDean Michael Berris     // Placement-new the Segment element at the beginning of the SegmentBlock.
256190c49bcSDean Michael Berris     new (SegmentBlock.Data) Segment{&SentinelSegment, &SentinelSegment, {0}};
257190c49bcSDean Michael Berris     auto SB = reinterpret_cast<Segment *>(SegmentBlock.Data);
258190c49bcSDean Michael Berris     return SB;
259190c49bcSDean Michael Berris   }
260190c49bcSDean Michael Berris 
InitHeadAndTail()261190c49bcSDean Michael Berris   Segment *InitHeadAndTail() XRAY_NEVER_INSTRUMENT {
262190c49bcSDean Michael Berris     DCHECK_EQ(Head, &SentinelSegment);
263190c49bcSDean Michael Berris     DCHECK_EQ(Tail, &SentinelSegment);
264190c49bcSDean Michael Berris     auto S = NewSegment();
265190c49bcSDean Michael Berris     if (S == nullptr)
266190c49bcSDean Michael Berris       return nullptr;
267190c49bcSDean Michael Berris     DCHECK_EQ(S->Next, &SentinelSegment);
268190c49bcSDean Michael Berris     DCHECK_EQ(S->Prev, &SentinelSegment);
269190c49bcSDean Michael Berris     DCHECK_NE(S, &SentinelSegment);
270190c49bcSDean Michael Berris     Head = S;
271190c49bcSDean Michael Berris     Tail = S;
272190c49bcSDean Michael Berris     DCHECK_EQ(Head, Tail);
273190c49bcSDean Michael Berris     DCHECK_EQ(Tail->Next, &SentinelSegment);
274190c49bcSDean Michael Berris     DCHECK_EQ(Tail->Prev, &SentinelSegment);
275190c49bcSDean Michael Berris     return S;
276190c49bcSDean Michael Berris   }
277190c49bcSDean Michael Berris 
AppendNewSegment()278190c49bcSDean Michael Berris   Segment *AppendNewSegment() XRAY_NEVER_INSTRUMENT {
279190c49bcSDean Michael Berris     auto S = NewSegment();
280190c49bcSDean Michael Berris     if (S == nullptr)
281190c49bcSDean Michael Berris       return nullptr;
282190c49bcSDean Michael Berris     DCHECK_NE(Tail, &SentinelSegment);
283190c49bcSDean Michael Berris     DCHECK_EQ(Tail->Next, &SentinelSegment);
284190c49bcSDean Michael Berris     DCHECK_EQ(S->Prev, &SentinelSegment);
285190c49bcSDean Michael Berris     DCHECK_EQ(S->Next, &SentinelSegment);
286190c49bcSDean Michael Berris     S->Prev = Tail;
287190c49bcSDean Michael Berris     Tail->Next = S;
288190c49bcSDean Michael Berris     Tail = S;
289190c49bcSDean Michael Berris     DCHECK_EQ(S, S->Prev->Next);
290190c49bcSDean Michael Berris     DCHECK_EQ(Tail->Next, &SentinelSegment);
291190c49bcSDean Michael Berris     return S;
292190c49bcSDean Michael Berris   }
293190c49bcSDean Michael Berris 
29426e81209SDean Michael Berris public:
Array(AllocatorType & A)295190c49bcSDean Michael Berris   explicit Array(AllocatorType &A) XRAY_NEVER_INSTRUMENT
296190c49bcSDean Michael Berris       : Alloc(&A),
297190c49bcSDean Michael Berris         Head(&SentinelSegment),
298190c49bcSDean Michael Berris         Tail(&SentinelSegment),
299190c49bcSDean Michael Berris         Freelist(&SentinelSegment),
300190c49bcSDean Michael Berris         Size(0) {}
301190c49bcSDean Michael Berris 
Array()302190c49bcSDean Michael Berris   Array() XRAY_NEVER_INSTRUMENT : Alloc(nullptr),
303190c49bcSDean Michael Berris                                   Head(&SentinelSegment),
304190c49bcSDean Michael Berris                                   Tail(&SentinelSegment),
305190c49bcSDean Michael Berris                                   Freelist(&SentinelSegment),
306190c49bcSDean Michael Berris                                   Size(0) {}
30726e81209SDean Michael Berris 
30826e81209SDean Michael Berris   Array(const Array &) = delete;
309190c49bcSDean Michael Berris   Array &operator=(const Array &) = delete;
310190c49bcSDean Michael Berris 
Array(Array && O)311190c49bcSDean Michael Berris   Array(Array &&O) XRAY_NEVER_INSTRUMENT : Alloc(O.Alloc),
31226e81209SDean Michael Berris                                            Head(O.Head),
31326e81209SDean Michael Berris                                            Tail(O.Tail),
314190c49bcSDean Michael Berris                                            Freelist(O.Freelist),
31526e81209SDean Michael Berris                                            Size(O.Size) {
316190c49bcSDean Michael Berris     O.Alloc = nullptr;
3174719c524SDean Michael Berris     O.Head = &SentinelSegment;
3184719c524SDean Michael Berris     O.Tail = &SentinelSegment;
31926e81209SDean Michael Berris     O.Size = 0;
320190c49bcSDean Michael Berris     O.Freelist = &SentinelSegment;
321190c49bcSDean Michael Berris   }
322190c49bcSDean Michael Berris 
323190c49bcSDean Michael Berris   Array &operator=(Array &&O) XRAY_NEVER_INSTRUMENT {
324190c49bcSDean Michael Berris     Alloc = O.Alloc;
325190c49bcSDean Michael Berris     O.Alloc = nullptr;
326190c49bcSDean Michael Berris     Head = O.Head;
327190c49bcSDean Michael Berris     O.Head = &SentinelSegment;
328190c49bcSDean Michael Berris     Tail = O.Tail;
329190c49bcSDean Michael Berris     O.Tail = &SentinelSegment;
330190c49bcSDean Michael Berris     Freelist = O.Freelist;
331190c49bcSDean Michael Berris     O.Freelist = &SentinelSegment;
332190c49bcSDean Michael Berris     Size = O.Size;
333190c49bcSDean Michael Berris     O.Size = 0;
334190c49bcSDean Michael Berris     return *this;
335190c49bcSDean Michael Berris   }
336190c49bcSDean Michael Berris 
~Array()337190c49bcSDean Michael Berris   ~Array() XRAY_NEVER_INSTRUMENT {
338190c49bcSDean Michael Berris     for (auto &E : *this)
339190c49bcSDean Michael Berris       (&E)->~T();
34026e81209SDean Michael Berris   }
34126e81209SDean Michael Berris 
empty()342edf0f6a7SDean Michael Berris   bool empty() const XRAY_NEVER_INSTRUMENT { return Size == 0; }
34326e81209SDean Michael Berris 
allocator()344edf0f6a7SDean Michael Berris   AllocatorType &allocator() const XRAY_NEVER_INSTRUMENT {
345ca856e07SDean Michael Berris     DCHECK_NE(Alloc, nullptr);
346ca856e07SDean Michael Berris     return *Alloc;
34726e81209SDean Michael Berris   }
34826e81209SDean Michael Berris 
size()349190c49bcSDean Michael Berris   uint64_t size() const XRAY_NEVER_INSTRUMENT { return Size; }
35026e81209SDean Michael Berris 
351190c49bcSDean Michael Berris   template <class... Args>
AppendEmplace(Args &&...args)352190c49bcSDean Michael Berris   T *AppendEmplace(Args &&... args) XRAY_NEVER_INSTRUMENT {
353190c49bcSDean Michael Berris     DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) ||
354190c49bcSDean Michael Berris            (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
355190c49bcSDean Michael Berris     if (UNLIKELY(Head == &SentinelSegment)) {
356190c49bcSDean Michael Berris       auto R = InitHeadAndTail();
357190c49bcSDean Michael Berris       if (R == nullptr)
35826e81209SDean Michael Berris         return nullptr;
359190c49bcSDean Michael Berris     }
360190c49bcSDean Michael Berris 
361190c49bcSDean Michael Berris     DCHECK_NE(Head, &SentinelSegment);
362190c49bcSDean Michael Berris     DCHECK_NE(Tail, &SentinelSegment);
36326e81209SDean Michael Berris 
3644719c524SDean Michael Berris     auto Offset = Size % ElementsPerSegment;
36526e81209SDean Michael Berris     if (UNLIKELY(Size != 0 && Offset == 0))
3664719c524SDean Michael Berris       if (AppendNewSegment() == nullptr)
36726e81209SDean Michael Berris         return nullptr;
36826e81209SDean Michael Berris 
36982f7b21fSDean Michael Berris     DCHECK_NE(Tail, &SentinelSegment);
370190c49bcSDean Michael Berris     auto Base = &Tail->Data;
37182f7b21fSDean Michael Berris     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
372190c49bcSDean Michael Berris     DCHECK_LE(AlignedOffset + sizeof(T),
37325d50595SDean Michael Berris               reinterpret_cast<unsigned char *>(Base) + SegmentSize);
37482f7b21fSDean Michael Berris 
37582f7b21fSDean Michael Berris     // In-place construct at Position.
376190c49bcSDean Michael Berris     new (AlignedOffset) T{std::forward<Args>(args)...};
37782f7b21fSDean Michael Berris     ++Size;
378190c49bcSDean Michael Berris     return reinterpret_cast<T *>(AlignedOffset);
37982f7b21fSDean Michael Berris   }
38082f7b21fSDean Michael Berris 
Append(const T & E)381190c49bcSDean Michael Berris   T *Append(const T &E) XRAY_NEVER_INSTRUMENT {
382190c49bcSDean Michael Berris     // FIXME: This is a duplication of AppenEmplace with the copy semantics
383190c49bcSDean Michael Berris     // explicitly used, as a work-around to GCC 4.8 not invoking the copy
384190c49bcSDean Michael Berris     // constructor with the placement new with braced-init syntax.
385190c49bcSDean Michael Berris     DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) ||
386190c49bcSDean Michael Berris            (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
387190c49bcSDean Michael Berris     if (UNLIKELY(Head == &SentinelSegment)) {
388190c49bcSDean Michael Berris       auto R = InitHeadAndTail();
389190c49bcSDean Michael Berris       if (R == nullptr)
390190c49bcSDean Michael Berris         return nullptr;
391190c49bcSDean Michael Berris     }
392190c49bcSDean Michael Berris 
393190c49bcSDean Michael Berris     DCHECK_NE(Head, &SentinelSegment);
394190c49bcSDean Michael Berris     DCHECK_NE(Tail, &SentinelSegment);
395190c49bcSDean Michael Berris 
396190c49bcSDean Michael Berris     auto Offset = Size % ElementsPerSegment;
397190c49bcSDean Michael Berris     if (UNLIKELY(Size != 0 && Offset == 0))
398190c49bcSDean Michael Berris       if (AppendNewSegment() == nullptr)
399190c49bcSDean Michael Berris         return nullptr;
400190c49bcSDean Michael Berris 
401190c49bcSDean Michael Berris     DCHECK_NE(Tail, &SentinelSegment);
402190c49bcSDean Michael Berris     auto Base = &Tail->Data;
403190c49bcSDean Michael Berris     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
404190c49bcSDean Michael Berris     DCHECK_LE(AlignedOffset + sizeof(T),
405190c49bcSDean Michael Berris               reinterpret_cast<unsigned char *>(Tail) + SegmentSize);
406190c49bcSDean Michael Berris 
407190c49bcSDean Michael Berris     // In-place construct at Position.
408190c49bcSDean Michael Berris     new (AlignedOffset) T(E);
409190c49bcSDean Michael Berris     ++Size;
410190c49bcSDean Michael Berris     return reinterpret_cast<T *>(AlignedOffset);
411190c49bcSDean Michael Berris   }
412190c49bcSDean Michael Berris 
413190c49bcSDean Michael Berris   T &operator[](uint64_t Offset) const XRAY_NEVER_INSTRUMENT {
41426e81209SDean Michael Berris     DCHECK_LE(Offset, Size);
41526e81209SDean Michael Berris     // We need to traverse the array enough times to find the element at Offset.
4164719c524SDean Michael Berris     auto S = Head;
4174719c524SDean Michael Berris     while (Offset >= ElementsPerSegment) {
4184719c524SDean Michael Berris       S = S->Next;
4194719c524SDean Michael Berris       Offset -= ElementsPerSegment;
4204719c524SDean Michael Berris       DCHECK_NE(S, &SentinelSegment);
42126e81209SDean Michael Berris     }
422190c49bcSDean Michael Berris     auto Base = &S->Data;
4234719c524SDean Michael Berris     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
4244719c524SDean Michael Berris     auto Position = reinterpret_cast<T *>(AlignedOffset);
4254719c524SDean Michael Berris     return *reinterpret_cast<T *>(Position);
42626e81209SDean Michael Berris   }
42726e81209SDean Michael Berris 
front()428edf0f6a7SDean Michael Berris   T &front() const XRAY_NEVER_INSTRUMENT {
4294719c524SDean Michael Berris     DCHECK_NE(Head, &SentinelSegment);
43026e81209SDean Michael Berris     DCHECK_NE(Size, 0u);
4310dd4f9f2SDean Michael Berris     return *begin();
43226e81209SDean Michael Berris   }
43326e81209SDean Michael Berris 
back()434edf0f6a7SDean Michael Berris   T &back() const XRAY_NEVER_INSTRUMENT {
4354719c524SDean Michael Berris     DCHECK_NE(Tail, &SentinelSegment);
4360dd4f9f2SDean Michael Berris     DCHECK_NE(Size, 0u);
4370dd4f9f2SDean Michael Berris     auto It = end();
4380dd4f9f2SDean Michael Berris     --It;
4390dd4f9f2SDean Michael Berris     return *It;
44026e81209SDean Michael Berris   }
44126e81209SDean Michael Berris 
442edf0f6a7SDean Michael Berris   template <class Predicate>
find_element(Predicate P)443edf0f6a7SDean Michael Berris   T *find_element(Predicate P) const XRAY_NEVER_INSTRUMENT {
44426e81209SDean Michael Berris     if (empty())
44526e81209SDean Michael Berris       return nullptr;
44626e81209SDean Michael Berris 
44726e81209SDean Michael Berris     auto E = end();
44826e81209SDean Michael Berris     for (auto I = begin(); I != E; ++I)
44926e81209SDean Michael Berris       if (P(*I))
45026e81209SDean Michael Berris         return &(*I);
45126e81209SDean Michael Berris 
45226e81209SDean Michael Berris     return nullptr;
45326e81209SDean Michael Berris   }
45426e81209SDean Michael Berris 
45526e81209SDean Michael Berris   /// Remove N Elements from the end. This leaves the blocks behind, and not
45626e81209SDean Michael Berris   /// require allocation of new blocks for new elements added after trimming.
trim(uint64_t Elements)457190c49bcSDean Michael Berris   void trim(uint64_t Elements) XRAY_NEVER_INSTRUMENT {
4580dd4f9f2SDean Michael Berris     auto OldSize = Size;
459190c49bcSDean Michael Berris     Elements = Elements > Size ? Size : Elements;
46026e81209SDean Michael Berris     Size -= Elements;
46126e81209SDean Michael Berris 
462190c49bcSDean Michael Berris     // We compute the number of segments we're going to return from the tail by
463190c49bcSDean Michael Berris     // counting how many elements have been trimmed. Given the following:
464190c49bcSDean Michael Berris     //
465190c49bcSDean Michael Berris     // - Each segment has N valid positions, where N > 0
466190c49bcSDean Michael Berris     // - The previous size > current size
467190c49bcSDean Michael Berris     //
468190c49bcSDean Michael Berris     // To compute the number of segments to return, we need to perform the
469190c49bcSDean Michael Berris     // following calculations for the number of segments required given 'x'
470190c49bcSDean Michael Berris     // elements:
471190c49bcSDean Michael Berris     //
472190c49bcSDean Michael Berris     //   f(x) = {
473190c49bcSDean Michael Berris     //            x == 0          : 0
474190c49bcSDean Michael Berris     //          , 0 < x <= N      : 1
475190c49bcSDean Michael Berris     //          , N < x <= max    : x / N + (x % N ? 1 : 0)
476190c49bcSDean Michael Berris     //          }
477190c49bcSDean Michael Berris     //
478190c49bcSDean Michael Berris     // We can simplify this down to:
479190c49bcSDean Michael Berris     //
480190c49bcSDean Michael Berris     //   f(x) = {
481190c49bcSDean Michael Berris     //            x == 0          : 0,
482190c49bcSDean Michael Berris     //          , 0 < x <= max    : x / N + (x < N || x % N ? 1 : 0)
483190c49bcSDean Michael Berris     //          }
484190c49bcSDean Michael Berris     //
485190c49bcSDean Michael Berris     // And further down to:
486190c49bcSDean Michael Berris     //
487190c49bcSDean Michael Berris     //   f(x) = x ? x / N + (x < N || x % N ? 1 : 0) : 0
488190c49bcSDean Michael Berris     //
489190c49bcSDean Michael Berris     // We can then perform the following calculation `s` which counts the number
490190c49bcSDean Michael Berris     // of segments we need to remove from the end of the data structure:
491190c49bcSDean Michael Berris     //
492190c49bcSDean Michael Berris     //   s(p, c) = f(p) - f(c)
493190c49bcSDean Michael Berris     //
494190c49bcSDean Michael Berris     // If we treat p = previous size, and c = current size, and given the
495190c49bcSDean Michael Berris     // properties above, the possible range for s(...) is [0..max(typeof(p))/N]
496190c49bcSDean Michael Berris     // given that typeof(p) == typeof(c).
497190c49bcSDean Michael Berris     auto F = [](uint64_t X) {
498190c49bcSDean Michael Berris       return X ? (X / ElementsPerSegment) +
499190c49bcSDean Michael Berris                      (X < ElementsPerSegment || X % ElementsPerSegment ? 1 : 0)
500190c49bcSDean Michael Berris                : 0;
501190c49bcSDean Michael Berris     };
502190c49bcSDean Michael Berris     auto PS = F(OldSize);
503190c49bcSDean Michael Berris     auto CS = F(Size);
504190c49bcSDean Michael Berris     DCHECK_GE(PS, CS);
505190c49bcSDean Michael Berris     auto SegmentsToTrim = PS - CS;
506190c49bcSDean Michael Berris     for (auto I = 0uL; I < SegmentsToTrim; ++I) {
507190c49bcSDean Michael Berris       // Here we place the current tail segment to the freelist. To do this
508190c49bcSDean Michael Berris       // appropriately, we need to perform a splice operation on two
509190c49bcSDean Michael Berris       // bidirectional linked-lists. In particular, we have the current state of
510190c49bcSDean Michael Berris       // the doubly-linked list of segments:
511190c49bcSDean Michael Berris       //
512190c49bcSDean Michael Berris       //   @S@ <- s0 <-> s1 <-> ... <-> sT -> @S@
513190c49bcSDean Michael Berris       //
5144719c524SDean Michael Berris       DCHECK_NE(Head, &SentinelSegment);
5154719c524SDean Michael Berris       DCHECK_NE(Tail, &SentinelSegment);
516190c49bcSDean Michael Berris       DCHECK_EQ(Tail->Next, &SentinelSegment);
5170dd4f9f2SDean Michael Berris 
518190c49bcSDean Michael Berris       if (Freelist == &SentinelSegment) {
519190c49bcSDean Michael Berris         // Our two lists at this point are in this configuration:
520190c49bcSDean Michael Berris         //
521190c49bcSDean Michael Berris         //   Freelist: (potentially) @S@
522190c49bcSDean Michael Berris         //   Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@
523190c49bcSDean Michael Berris         //                  ^ Head                ^ Tail
524190c49bcSDean Michael Berris         //
525190c49bcSDean Michael Berris         // The end state for us will be this configuration:
526190c49bcSDean Michael Berris         //
527190c49bcSDean Michael Berris         //   Freelist: @S@<-sT->@S@
528190c49bcSDean Michael Berris         //   Mainlist: @S@<-s0<->s1<->...<->sPT->@S@
529190c49bcSDean Michael Berris         //                  ^ Head          ^ Tail
530190c49bcSDean Michael Berris         //
531190c49bcSDean Michael Berris         // The first step for us is to hold a reference to the tail of Mainlist,
532190c49bcSDean Michael Berris         // which in our notation is represented by sT. We call this our "free
533190c49bcSDean Michael Berris         // segment" which is the segment we are placing on the Freelist.
534190c49bcSDean Michael Berris         //
535190c49bcSDean Michael Berris         //   sF = sT
536190c49bcSDean Michael Berris         //
537190c49bcSDean Michael Berris         // Then, we also hold a reference to the "pre-tail" element, which we
538190c49bcSDean Michael Berris         // call sPT:
539190c49bcSDean Michael Berris         //
540190c49bcSDean Michael Berris         //   sPT = pred(sT)
541190c49bcSDean Michael Berris         //
542190c49bcSDean Michael Berris         // We want to splice sT into the beginning of the Freelist, which in
543190c49bcSDean Michael Berris         // an empty Freelist means placing a segment whose predecessor and
544190c49bcSDean Michael Berris         // successor is the sentinel segment.
545190c49bcSDean Michael Berris         //
546190c49bcSDean Michael Berris         // The splice operation then can be performed in the following
547190c49bcSDean Michael Berris         // algorithm:
548190c49bcSDean Michael Berris         //
549190c49bcSDean Michael Berris         //   succ(sPT) = S
550190c49bcSDean Michael Berris         //   pred(sT) = S
551190c49bcSDean Michael Berris         //   succ(sT) = Freelist
552190c49bcSDean Michael Berris         //   Freelist = sT
553190c49bcSDean Michael Berris         //   Tail = sPT
554190c49bcSDean Michael Berris         //
555190c49bcSDean Michael Berris         auto SPT = Tail->Prev;
556190c49bcSDean Michael Berris         SPT->Next = &SentinelSegment;
557190c49bcSDean Michael Berris         Tail->Prev = &SentinelSegment;
558190c49bcSDean Michael Berris         Tail->Next = Freelist;
559190c49bcSDean Michael Berris         Freelist = Tail;
560190c49bcSDean Michael Berris         Tail = SPT;
561ebfbf890SDean Michael Berris 
562190c49bcSDean Michael Berris         // Our post-conditions here are:
563190c49bcSDean Michael Berris         DCHECK_EQ(Tail->Next, &SentinelSegment);
564190c49bcSDean Michael Berris         DCHECK_EQ(Freelist->Prev, &SentinelSegment);
565190c49bcSDean Michael Berris       } else {
566190c49bcSDean Michael Berris         // In the other case, where the Freelist is not empty, we perform the
567190c49bcSDean Michael Berris         // following transformation instead:
568190c49bcSDean Michael Berris         //
569190c49bcSDean Michael Berris         // This transforms the current state:
570190c49bcSDean Michael Berris         //
571190c49bcSDean Michael Berris         //   Freelist: @S@<-f0->@S@
572190c49bcSDean Michael Berris         //                  ^ Freelist
573190c49bcSDean Michael Berris         //   Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@
574190c49bcSDean Michael Berris         //                  ^ Head                ^ Tail
575190c49bcSDean Michael Berris         //
576190c49bcSDean Michael Berris         // Into the following:
577190c49bcSDean Michael Berris         //
578190c49bcSDean Michael Berris         //   Freelist: @S@<-sT<->f0->@S@
579190c49bcSDean Michael Berris         //                  ^ Freelist
580190c49bcSDean Michael Berris         //   Mainlist: @S@<-s0<->s1<->...<->sPT->@S@
581190c49bcSDean Michael Berris         //                  ^ Head          ^ Tail
582190c49bcSDean Michael Berris         //
583190c49bcSDean Michael Berris         // The algorithm is:
584190c49bcSDean Michael Berris         //
585190c49bcSDean Michael Berris         //   sFH = Freelist
586190c49bcSDean Michael Berris         //   sPT = pred(sT)
587190c49bcSDean Michael Berris         //   pred(SFH) = sT
588190c49bcSDean Michael Berris         //   succ(sT) = Freelist
589190c49bcSDean Michael Berris         //   pred(sT) = S
590190c49bcSDean Michael Berris         //   succ(sPT) = S
591190c49bcSDean Michael Berris         //   Tail = sPT
592190c49bcSDean Michael Berris         //   Freelist = sT
593190c49bcSDean Michael Berris         //
594190c49bcSDean Michael Berris         auto SFH = Freelist;
595190c49bcSDean Michael Berris         auto SPT = Tail->Prev;
596190c49bcSDean Michael Berris         auto ST = Tail;
597190c49bcSDean Michael Berris         SFH->Prev = ST;
598190c49bcSDean Michael Berris         ST->Next = Freelist;
599190c49bcSDean Michael Berris         ST->Prev = &SentinelSegment;
600190c49bcSDean Michael Berris         SPT->Next = &SentinelSegment;
601190c49bcSDean Michael Berris         Tail = SPT;
602190c49bcSDean Michael Berris         Freelist = ST;
603ebfbf890SDean Michael Berris 
604190c49bcSDean Michael Berris         // Our post-conditions here are:
605190c49bcSDean Michael Berris         DCHECK_EQ(Tail->Next, &SentinelSegment);
606190c49bcSDean Michael Berris         DCHECK_EQ(Freelist->Prev, &SentinelSegment);
607190c49bcSDean Michael Berris         DCHECK_EQ(Freelist->Next->Prev, Freelist);
608190c49bcSDean Michael Berris       }
609190c49bcSDean Michael Berris     }
610190c49bcSDean Michael Berris 
611190c49bcSDean Michael Berris     // Now in case we've spliced all the segments in the end, we ensure that the
612190c49bcSDean Michael Berris     // main list is "empty", or both the head and tail pointing to the sentinel
613190c49bcSDean Michael Berris     // segment.
6144719c524SDean Michael Berris     if (Tail == &SentinelSegment)
61526e81209SDean Michael Berris       Head = Tail;
6160dd4f9f2SDean Michael Berris 
617190c49bcSDean Michael Berris     DCHECK(
618190c49bcSDean Michael Berris         (Size == 0 && Head == &SentinelSegment && Tail == &SentinelSegment) ||
619190c49bcSDean Michael Berris         (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
620190c49bcSDean Michael Berris     DCHECK(
621190c49bcSDean Michael Berris         (Freelist != &SentinelSegment && Freelist->Prev == &SentinelSegment) ||
622190c49bcSDean Michael Berris         (Freelist == &SentinelSegment && Tail->Next == &SentinelSegment));
62326e81209SDean Michael Berris   }
62426e81209SDean Michael Berris 
62526e81209SDean Michael Berris   // Provide iterators.
begin()626edf0f6a7SDean Michael Berris   Iterator<T> begin() const XRAY_NEVER_INSTRUMENT {
627edf0f6a7SDean Michael Berris     return Iterator<T>(Head, 0, Size);
628edf0f6a7SDean Michael Berris   }
end()629edf0f6a7SDean Michael Berris   Iterator<T> end() const XRAY_NEVER_INSTRUMENT {
630edf0f6a7SDean Michael Berris     return Iterator<T>(Tail, Size, Size);
631edf0f6a7SDean Michael Berris   }
cbegin()632edf0f6a7SDean Michael Berris   Iterator<const T> cbegin() const XRAY_NEVER_INSTRUMENT {
633edf0f6a7SDean Michael Berris     return Iterator<const T>(Head, 0, Size);
634edf0f6a7SDean Michael Berris   }
cend()635edf0f6a7SDean Michael Berris   Iterator<const T> cend() const XRAY_NEVER_INSTRUMENT {
636edf0f6a7SDean Michael Berris     return Iterator<const T>(Tail, Size, Size);
637edf0f6a7SDean Michael Berris   }
63826e81209SDean Michael Berris };
63926e81209SDean Michael Berris 
64026e81209SDean Michael Berris // We need to have this storage definition out-of-line so that the compiler can
6414719c524SDean Michael Berris // ensure that storage for the SentinelSegment is defined and has a single
64226e81209SDean Michael Berris // address.
6434719c524SDean Michael Berris template <class T>
644190c49bcSDean Michael Berris typename Array<T>::Segment Array<T>::SentinelSegment{
645190c49bcSDean Michael Berris     &Array<T>::SentinelSegment, &Array<T>::SentinelSegment, {'\0'}};
64626e81209SDean Michael Berris 
64726e81209SDean Michael Berris } // namespace __xray
64826e81209SDean Michael Berris 
64926e81209SDean Michael Berris #endif // XRAY_SEGMENTED_ARRAY_H
650