xref: /openbsd-src/gnu/llvm/compiler-rt/lib/xray/xray_segmented_array.h (revision 3cab2bb3f667058bece8e38b12449a63a9d73c4b)
1*3cab2bb3Spatrick //===-- xray_segmented_array.h ---------------------------------*- C++ -*-===//
2*3cab2bb3Spatrick //
3*3cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*3cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
5*3cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*3cab2bb3Spatrick //
7*3cab2bb3Spatrick //===----------------------------------------------------------------------===//
8*3cab2bb3Spatrick //
9*3cab2bb3Spatrick // This file is a part of XRay, a dynamic runtime instrumentation system.
10*3cab2bb3Spatrick //
11*3cab2bb3Spatrick // Defines the implementation of a segmented array, with fixed-size segments
12*3cab2bb3Spatrick // backing the segments.
13*3cab2bb3Spatrick //
14*3cab2bb3Spatrick //===----------------------------------------------------------------------===//
15*3cab2bb3Spatrick #ifndef XRAY_SEGMENTED_ARRAY_H
16*3cab2bb3Spatrick #define XRAY_SEGMENTED_ARRAY_H
17*3cab2bb3Spatrick 
18*3cab2bb3Spatrick #include "sanitizer_common/sanitizer_allocator.h"
19*3cab2bb3Spatrick #include "xray_allocator.h"
20*3cab2bb3Spatrick #include "xray_utils.h"
21*3cab2bb3Spatrick #include <cassert>
22*3cab2bb3Spatrick #include <type_traits>
23*3cab2bb3Spatrick #include <utility>
24*3cab2bb3Spatrick 
25*3cab2bb3Spatrick namespace __xray {
26*3cab2bb3Spatrick 
27*3cab2bb3Spatrick /// The Array type provides an interface similar to std::vector<...> but does
28*3cab2bb3Spatrick /// not shrink in size. Once constructed, elements can be appended but cannot be
29*3cab2bb3Spatrick /// removed. The implementation is heavily dependent on the contract provided by
30*3cab2bb3Spatrick /// the Allocator type, in that all memory will be released when the Allocator
31*3cab2bb3Spatrick /// is destroyed. When an Array is destroyed, it will destroy elements in the
32*3cab2bb3Spatrick /// backing store but will not free the memory.
33*3cab2bb3Spatrick template <class T> class Array {
34*3cab2bb3Spatrick   struct Segment {
35*3cab2bb3Spatrick     Segment *Prev;
36*3cab2bb3Spatrick     Segment *Next;
37*3cab2bb3Spatrick     char Data[1];
38*3cab2bb3Spatrick   };
39*3cab2bb3Spatrick 
40*3cab2bb3Spatrick public:
41*3cab2bb3Spatrick   // Each segment of the array will be laid out with the following assumptions:
42*3cab2bb3Spatrick   //
43*3cab2bb3Spatrick   //   - Each segment will be on a cache-line address boundary (kCacheLineSize
44*3cab2bb3Spatrick   //     aligned).
45*3cab2bb3Spatrick   //
46*3cab2bb3Spatrick   //   - The elements will be accessed through an aligned pointer, dependent on
47*3cab2bb3Spatrick   //     the alignment of T.
48*3cab2bb3Spatrick   //
49*3cab2bb3Spatrick   //   - Each element is at least two-pointers worth from the beginning of the
50*3cab2bb3Spatrick   //     Segment, aligned properly, and the rest of the elements are accessed
51*3cab2bb3Spatrick   //     through appropriate alignment.
52*3cab2bb3Spatrick   //
53*3cab2bb3Spatrick   // We then compute the size of the segment to follow this logic:
54*3cab2bb3Spatrick   //
55*3cab2bb3Spatrick   //   - Compute the number of elements that can fit within
56*3cab2bb3Spatrick   //     kCacheLineSize-multiple segments, minus the size of two pointers.
57*3cab2bb3Spatrick   //
58*3cab2bb3Spatrick   //   - Request cacheline-multiple sized elements from the allocator.
59*3cab2bb3Spatrick   static constexpr uint64_t AlignedElementStorageSize =
60*3cab2bb3Spatrick       sizeof(typename std::aligned_storage<sizeof(T), alignof(T)>::type);
61*3cab2bb3Spatrick 
62*3cab2bb3Spatrick   static constexpr uint64_t SegmentControlBlockSize = sizeof(Segment *) * 2;
63*3cab2bb3Spatrick 
64*3cab2bb3Spatrick   static constexpr uint64_t SegmentSize = nearest_boundary(
65*3cab2bb3Spatrick       SegmentControlBlockSize + next_pow2(sizeof(T)), kCacheLineSize);
66*3cab2bb3Spatrick 
67*3cab2bb3Spatrick   using AllocatorType = Allocator<SegmentSize>;
68*3cab2bb3Spatrick 
69*3cab2bb3Spatrick   static constexpr uint64_t ElementsPerSegment =
70*3cab2bb3Spatrick       (SegmentSize - SegmentControlBlockSize) / next_pow2(sizeof(T));
71*3cab2bb3Spatrick 
72*3cab2bb3Spatrick   static_assert(ElementsPerSegment > 0,
73*3cab2bb3Spatrick                 "Must have at least 1 element per segment.");
74*3cab2bb3Spatrick 
75*3cab2bb3Spatrick   static Segment SentinelSegment;
76*3cab2bb3Spatrick 
77*3cab2bb3Spatrick   using size_type = uint64_t;
78*3cab2bb3Spatrick 
79*3cab2bb3Spatrick private:
80*3cab2bb3Spatrick   // This Iterator models a BidirectionalIterator.
81*3cab2bb3Spatrick   template <class U> class Iterator {
82*3cab2bb3Spatrick     Segment *S = &SentinelSegment;
83*3cab2bb3Spatrick     uint64_t Offset = 0;
84*3cab2bb3Spatrick     uint64_t Size = 0;
85*3cab2bb3Spatrick 
86*3cab2bb3Spatrick   public:
Iterator(Segment * IS,uint64_t Off,uint64_t S)87*3cab2bb3Spatrick     Iterator(Segment *IS, uint64_t Off, uint64_t S) XRAY_NEVER_INSTRUMENT
88*3cab2bb3Spatrick         : S(IS),
89*3cab2bb3Spatrick           Offset(Off),
90*3cab2bb3Spatrick           Size(S) {}
91*3cab2bb3Spatrick     Iterator(const Iterator &) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
92*3cab2bb3Spatrick     Iterator() NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
93*3cab2bb3Spatrick     Iterator(Iterator &&) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
94*3cab2bb3Spatrick     Iterator &operator=(const Iterator &) XRAY_NEVER_INSTRUMENT = default;
95*3cab2bb3Spatrick     Iterator &operator=(Iterator &&) XRAY_NEVER_INSTRUMENT = default;
96*3cab2bb3Spatrick     ~Iterator() XRAY_NEVER_INSTRUMENT = default;
97*3cab2bb3Spatrick 
98*3cab2bb3Spatrick     Iterator &operator++() XRAY_NEVER_INSTRUMENT {
99*3cab2bb3Spatrick       if (++Offset % ElementsPerSegment || Offset == Size)
100*3cab2bb3Spatrick         return *this;
101*3cab2bb3Spatrick 
102*3cab2bb3Spatrick       // At this point, we know that Offset % N == 0, so we must advance the
103*3cab2bb3Spatrick       // segment pointer.
104*3cab2bb3Spatrick       DCHECK_EQ(Offset % ElementsPerSegment, 0);
105*3cab2bb3Spatrick       DCHECK_NE(Offset, Size);
106*3cab2bb3Spatrick       DCHECK_NE(S, &SentinelSegment);
107*3cab2bb3Spatrick       DCHECK_NE(S->Next, &SentinelSegment);
108*3cab2bb3Spatrick       S = S->Next;
109*3cab2bb3Spatrick       DCHECK_NE(S, &SentinelSegment);
110*3cab2bb3Spatrick       return *this;
111*3cab2bb3Spatrick     }
112*3cab2bb3Spatrick 
113*3cab2bb3Spatrick     Iterator &operator--() XRAY_NEVER_INSTRUMENT {
114*3cab2bb3Spatrick       DCHECK_NE(S, &SentinelSegment);
115*3cab2bb3Spatrick       DCHECK_GT(Offset, 0);
116*3cab2bb3Spatrick 
117*3cab2bb3Spatrick       auto PreviousOffset = Offset--;
118*3cab2bb3Spatrick       if (PreviousOffset != Size && PreviousOffset % ElementsPerSegment == 0) {
119*3cab2bb3Spatrick         DCHECK_NE(S->Prev, &SentinelSegment);
120*3cab2bb3Spatrick         S = S->Prev;
121*3cab2bb3Spatrick       }
122*3cab2bb3Spatrick 
123*3cab2bb3Spatrick       return *this;
124*3cab2bb3Spatrick     }
125*3cab2bb3Spatrick 
126*3cab2bb3Spatrick     Iterator operator++(int) XRAY_NEVER_INSTRUMENT {
127*3cab2bb3Spatrick       Iterator Copy(*this);
128*3cab2bb3Spatrick       ++(*this);
129*3cab2bb3Spatrick       return Copy;
130*3cab2bb3Spatrick     }
131*3cab2bb3Spatrick 
132*3cab2bb3Spatrick     Iterator operator--(int) XRAY_NEVER_INSTRUMENT {
133*3cab2bb3Spatrick       Iterator Copy(*this);
134*3cab2bb3Spatrick       --(*this);
135*3cab2bb3Spatrick       return Copy;
136*3cab2bb3Spatrick     }
137*3cab2bb3Spatrick 
138*3cab2bb3Spatrick     template <class V, class W>
139*3cab2bb3Spatrick     friend bool operator==(const Iterator<V> &L,
140*3cab2bb3Spatrick                            const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
141*3cab2bb3Spatrick       return L.S == R.S && L.Offset == R.Offset;
142*3cab2bb3Spatrick     }
143*3cab2bb3Spatrick 
144*3cab2bb3Spatrick     template <class V, class W>
145*3cab2bb3Spatrick     friend bool operator!=(const Iterator<V> &L,
146*3cab2bb3Spatrick                            const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
147*3cab2bb3Spatrick       return !(L == R);
148*3cab2bb3Spatrick     }
149*3cab2bb3Spatrick 
150*3cab2bb3Spatrick     U &operator*() const XRAY_NEVER_INSTRUMENT {
151*3cab2bb3Spatrick       DCHECK_NE(S, &SentinelSegment);
152*3cab2bb3Spatrick       auto RelOff = Offset % ElementsPerSegment;
153*3cab2bb3Spatrick 
154*3cab2bb3Spatrick       // We need to compute the character-aligned pointer, offset from the
155*3cab2bb3Spatrick       // segment's Data location to get the element in the position of Offset.
156*3cab2bb3Spatrick       auto Base = &S->Data;
157*3cab2bb3Spatrick       auto AlignedOffset = Base + (RelOff * AlignedElementStorageSize);
158*3cab2bb3Spatrick       return *reinterpret_cast<U *>(AlignedOffset);
159*3cab2bb3Spatrick     }
160*3cab2bb3Spatrick 
161*3cab2bb3Spatrick     U *operator->() const XRAY_NEVER_INSTRUMENT { return &(**this); }
162*3cab2bb3Spatrick   };
163*3cab2bb3Spatrick 
164*3cab2bb3Spatrick   AllocatorType *Alloc;
165*3cab2bb3Spatrick   Segment *Head;
166*3cab2bb3Spatrick   Segment *Tail;
167*3cab2bb3Spatrick 
168*3cab2bb3Spatrick   // Here we keep track of segments in the freelist, to allow us to re-use
169*3cab2bb3Spatrick   // segments when elements are trimmed off the end.
170*3cab2bb3Spatrick   Segment *Freelist;
171*3cab2bb3Spatrick   uint64_t Size;
172*3cab2bb3Spatrick 
173*3cab2bb3Spatrick   // ===============================
174*3cab2bb3Spatrick   // In the following implementation, we work through the algorithms and the
175*3cab2bb3Spatrick   // list operations using the following notation:
176*3cab2bb3Spatrick   //
177*3cab2bb3Spatrick   //   - pred(s) is the predecessor (previous node accessor) and succ(s) is
178*3cab2bb3Spatrick   //     the successor (next node accessor).
179*3cab2bb3Spatrick   //
180*3cab2bb3Spatrick   //   - S is a sentinel segment, which has the following property:
181*3cab2bb3Spatrick   //
182*3cab2bb3Spatrick   //         pred(S) == succ(S) == S
183*3cab2bb3Spatrick   //
184*3cab2bb3Spatrick   //   - @ is a loop operator, which can imply pred(s) == s if it appears on
185*3cab2bb3Spatrick   //     the left of s, or succ(s) == S if it appears on the right of s.
186*3cab2bb3Spatrick   //
187*3cab2bb3Spatrick   //   - sL <-> sR : means a bidirectional relation between sL and sR, which
188*3cab2bb3Spatrick   //     means:
189*3cab2bb3Spatrick   //
190*3cab2bb3Spatrick   //         succ(sL) == sR && pred(SR) == sL
191*3cab2bb3Spatrick   //
192*3cab2bb3Spatrick   //   - sL -> sR : implies a unidirectional relation between sL and SR,
193*3cab2bb3Spatrick   //     with the following properties:
194*3cab2bb3Spatrick   //
195*3cab2bb3Spatrick   //         succ(sL) == sR
196*3cab2bb3Spatrick   //
197*3cab2bb3Spatrick   //     sL <- sR : implies a unidirectional relation between sR and sL,
198*3cab2bb3Spatrick   //     with the following properties:
199*3cab2bb3Spatrick   //
200*3cab2bb3Spatrick   //         pred(sR) == sL
201*3cab2bb3Spatrick   //
202*3cab2bb3Spatrick   // ===============================
203*3cab2bb3Spatrick 
NewSegment()204*3cab2bb3Spatrick   Segment *NewSegment() XRAY_NEVER_INSTRUMENT {
205*3cab2bb3Spatrick     // We need to handle the case in which enough elements have been trimmed to
206*3cab2bb3Spatrick     // allow us to re-use segments we've allocated before. For this we look into
207*3cab2bb3Spatrick     // the Freelist, to see whether we need to actually allocate new blocks or
208*3cab2bb3Spatrick     // just re-use blocks we've already seen before.
209*3cab2bb3Spatrick     if (Freelist != &SentinelSegment) {
210*3cab2bb3Spatrick       // The current state of lists resemble something like this at this point:
211*3cab2bb3Spatrick       //
212*3cab2bb3Spatrick       //   Freelist: @S@<-f0->...<->fN->@S@
213*3cab2bb3Spatrick       //                  ^ Freelist
214*3cab2bb3Spatrick       //
215*3cab2bb3Spatrick       // We want to perform a splice of `f0` from Freelist to a temporary list,
216*3cab2bb3Spatrick       // which looks like:
217*3cab2bb3Spatrick       //
218*3cab2bb3Spatrick       //   Templist: @S@<-f0->@S@
219*3cab2bb3Spatrick       //                  ^ FreeSegment
220*3cab2bb3Spatrick       //
221*3cab2bb3Spatrick       // Our algorithm preconditions are:
222*3cab2bb3Spatrick       DCHECK_EQ(Freelist->Prev, &SentinelSegment);
223*3cab2bb3Spatrick 
224*3cab2bb3Spatrick       // Then the algorithm we implement is:
225*3cab2bb3Spatrick       //
226*3cab2bb3Spatrick       //   SFS = Freelist
227*3cab2bb3Spatrick       //   Freelist = succ(Freelist)
228*3cab2bb3Spatrick       //   if (Freelist != S)
229*3cab2bb3Spatrick       //     pred(Freelist) = S
230*3cab2bb3Spatrick       //   succ(SFS) = S
231*3cab2bb3Spatrick       //   pred(SFS) = S
232*3cab2bb3Spatrick       //
233*3cab2bb3Spatrick       auto *FreeSegment = Freelist;
234*3cab2bb3Spatrick       Freelist = Freelist->Next;
235*3cab2bb3Spatrick 
236*3cab2bb3Spatrick       // Note that we need to handle the case where Freelist is now pointing to
237*3cab2bb3Spatrick       // S, which we don't want to be overwriting.
238*3cab2bb3Spatrick       // TODO: Determine whether the cost of the branch is higher than the cost
239*3cab2bb3Spatrick       // of the blind assignment.
240*3cab2bb3Spatrick       if (Freelist != &SentinelSegment)
241*3cab2bb3Spatrick         Freelist->Prev = &SentinelSegment;
242*3cab2bb3Spatrick 
243*3cab2bb3Spatrick       FreeSegment->Next = &SentinelSegment;
244*3cab2bb3Spatrick       FreeSegment->Prev = &SentinelSegment;
245*3cab2bb3Spatrick 
246*3cab2bb3Spatrick       // Our postconditions are:
247*3cab2bb3Spatrick       DCHECK_EQ(Freelist->Prev, &SentinelSegment);
248*3cab2bb3Spatrick       DCHECK_NE(FreeSegment, &SentinelSegment);
249*3cab2bb3Spatrick       return FreeSegment;
250*3cab2bb3Spatrick     }
251*3cab2bb3Spatrick 
252*3cab2bb3Spatrick     auto SegmentBlock = Alloc->Allocate();
253*3cab2bb3Spatrick     if (SegmentBlock.Data == nullptr)
254*3cab2bb3Spatrick       return nullptr;
255*3cab2bb3Spatrick 
256*3cab2bb3Spatrick     // Placement-new the Segment element at the beginning of the SegmentBlock.
257*3cab2bb3Spatrick     new (SegmentBlock.Data) Segment{&SentinelSegment, &SentinelSegment, {0}};
258*3cab2bb3Spatrick     auto SB = reinterpret_cast<Segment *>(SegmentBlock.Data);
259*3cab2bb3Spatrick     return SB;
260*3cab2bb3Spatrick   }
261*3cab2bb3Spatrick 
InitHeadAndTail()262*3cab2bb3Spatrick   Segment *InitHeadAndTail() XRAY_NEVER_INSTRUMENT {
263*3cab2bb3Spatrick     DCHECK_EQ(Head, &SentinelSegment);
264*3cab2bb3Spatrick     DCHECK_EQ(Tail, &SentinelSegment);
265*3cab2bb3Spatrick     auto S = NewSegment();
266*3cab2bb3Spatrick     if (S == nullptr)
267*3cab2bb3Spatrick       return nullptr;
268*3cab2bb3Spatrick     DCHECK_EQ(S->Next, &SentinelSegment);
269*3cab2bb3Spatrick     DCHECK_EQ(S->Prev, &SentinelSegment);
270*3cab2bb3Spatrick     DCHECK_NE(S, &SentinelSegment);
271*3cab2bb3Spatrick     Head = S;
272*3cab2bb3Spatrick     Tail = S;
273*3cab2bb3Spatrick     DCHECK_EQ(Head, Tail);
274*3cab2bb3Spatrick     DCHECK_EQ(Tail->Next, &SentinelSegment);
275*3cab2bb3Spatrick     DCHECK_EQ(Tail->Prev, &SentinelSegment);
276*3cab2bb3Spatrick     return S;
277*3cab2bb3Spatrick   }
278*3cab2bb3Spatrick 
AppendNewSegment()279*3cab2bb3Spatrick   Segment *AppendNewSegment() XRAY_NEVER_INSTRUMENT {
280*3cab2bb3Spatrick     auto S = NewSegment();
281*3cab2bb3Spatrick     if (S == nullptr)
282*3cab2bb3Spatrick       return nullptr;
283*3cab2bb3Spatrick     DCHECK_NE(Tail, &SentinelSegment);
284*3cab2bb3Spatrick     DCHECK_EQ(Tail->Next, &SentinelSegment);
285*3cab2bb3Spatrick     DCHECK_EQ(S->Prev, &SentinelSegment);
286*3cab2bb3Spatrick     DCHECK_EQ(S->Next, &SentinelSegment);
287*3cab2bb3Spatrick     S->Prev = Tail;
288*3cab2bb3Spatrick     Tail->Next = S;
289*3cab2bb3Spatrick     Tail = S;
290*3cab2bb3Spatrick     DCHECK_EQ(S, S->Prev->Next);
291*3cab2bb3Spatrick     DCHECK_EQ(Tail->Next, &SentinelSegment);
292*3cab2bb3Spatrick     return S;
293*3cab2bb3Spatrick   }
294*3cab2bb3Spatrick 
295*3cab2bb3Spatrick public:
Array(AllocatorType & A)296*3cab2bb3Spatrick   explicit Array(AllocatorType &A) XRAY_NEVER_INSTRUMENT
297*3cab2bb3Spatrick       : Alloc(&A),
298*3cab2bb3Spatrick         Head(&SentinelSegment),
299*3cab2bb3Spatrick         Tail(&SentinelSegment),
300*3cab2bb3Spatrick         Freelist(&SentinelSegment),
301*3cab2bb3Spatrick         Size(0) {}
302*3cab2bb3Spatrick 
Array()303*3cab2bb3Spatrick   Array() XRAY_NEVER_INSTRUMENT : Alloc(nullptr),
304*3cab2bb3Spatrick                                   Head(&SentinelSegment),
305*3cab2bb3Spatrick                                   Tail(&SentinelSegment),
306*3cab2bb3Spatrick                                   Freelist(&SentinelSegment),
307*3cab2bb3Spatrick                                   Size(0) {}
308*3cab2bb3Spatrick 
309*3cab2bb3Spatrick   Array(const Array &) = delete;
310*3cab2bb3Spatrick   Array &operator=(const Array &) = delete;
311*3cab2bb3Spatrick 
Array(Array && O)312*3cab2bb3Spatrick   Array(Array &&O) XRAY_NEVER_INSTRUMENT : Alloc(O.Alloc),
313*3cab2bb3Spatrick                                            Head(O.Head),
314*3cab2bb3Spatrick                                            Tail(O.Tail),
315*3cab2bb3Spatrick                                            Freelist(O.Freelist),
316*3cab2bb3Spatrick                                            Size(O.Size) {
317*3cab2bb3Spatrick     O.Alloc = nullptr;
318*3cab2bb3Spatrick     O.Head = &SentinelSegment;
319*3cab2bb3Spatrick     O.Tail = &SentinelSegment;
320*3cab2bb3Spatrick     O.Size = 0;
321*3cab2bb3Spatrick     O.Freelist = &SentinelSegment;
322*3cab2bb3Spatrick   }
323*3cab2bb3Spatrick 
324*3cab2bb3Spatrick   Array &operator=(Array &&O) XRAY_NEVER_INSTRUMENT {
325*3cab2bb3Spatrick     Alloc = O.Alloc;
326*3cab2bb3Spatrick     O.Alloc = nullptr;
327*3cab2bb3Spatrick     Head = O.Head;
328*3cab2bb3Spatrick     O.Head = &SentinelSegment;
329*3cab2bb3Spatrick     Tail = O.Tail;
330*3cab2bb3Spatrick     O.Tail = &SentinelSegment;
331*3cab2bb3Spatrick     Freelist = O.Freelist;
332*3cab2bb3Spatrick     O.Freelist = &SentinelSegment;
333*3cab2bb3Spatrick     Size = O.Size;
334*3cab2bb3Spatrick     O.Size = 0;
335*3cab2bb3Spatrick     return *this;
336*3cab2bb3Spatrick   }
337*3cab2bb3Spatrick 
~Array()338*3cab2bb3Spatrick   ~Array() XRAY_NEVER_INSTRUMENT {
339*3cab2bb3Spatrick     for (auto &E : *this)
340*3cab2bb3Spatrick       (&E)->~T();
341*3cab2bb3Spatrick   }
342*3cab2bb3Spatrick 
empty()343*3cab2bb3Spatrick   bool empty() const XRAY_NEVER_INSTRUMENT { return Size == 0; }
344*3cab2bb3Spatrick 
allocator()345*3cab2bb3Spatrick   AllocatorType &allocator() const XRAY_NEVER_INSTRUMENT {
346*3cab2bb3Spatrick     DCHECK_NE(Alloc, nullptr);
347*3cab2bb3Spatrick     return *Alloc;
348*3cab2bb3Spatrick   }
349*3cab2bb3Spatrick 
size()350*3cab2bb3Spatrick   uint64_t size() const XRAY_NEVER_INSTRUMENT { return Size; }
351*3cab2bb3Spatrick 
352*3cab2bb3Spatrick   template <class... Args>
AppendEmplace(Args &&...args)353*3cab2bb3Spatrick   T *AppendEmplace(Args &&... args) XRAY_NEVER_INSTRUMENT {
354*3cab2bb3Spatrick     DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) ||
355*3cab2bb3Spatrick            (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
356*3cab2bb3Spatrick     if (UNLIKELY(Head == &SentinelSegment)) {
357*3cab2bb3Spatrick       auto R = InitHeadAndTail();
358*3cab2bb3Spatrick       if (R == nullptr)
359*3cab2bb3Spatrick         return nullptr;
360*3cab2bb3Spatrick     }
361*3cab2bb3Spatrick 
362*3cab2bb3Spatrick     DCHECK_NE(Head, &SentinelSegment);
363*3cab2bb3Spatrick     DCHECK_NE(Tail, &SentinelSegment);
364*3cab2bb3Spatrick 
365*3cab2bb3Spatrick     auto Offset = Size % ElementsPerSegment;
366*3cab2bb3Spatrick     if (UNLIKELY(Size != 0 && Offset == 0))
367*3cab2bb3Spatrick       if (AppendNewSegment() == nullptr)
368*3cab2bb3Spatrick         return nullptr;
369*3cab2bb3Spatrick 
370*3cab2bb3Spatrick     DCHECK_NE(Tail, &SentinelSegment);
371*3cab2bb3Spatrick     auto Base = &Tail->Data;
372*3cab2bb3Spatrick     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
373*3cab2bb3Spatrick     DCHECK_LE(AlignedOffset + sizeof(T),
374*3cab2bb3Spatrick               reinterpret_cast<unsigned char *>(Base) + SegmentSize);
375*3cab2bb3Spatrick 
376*3cab2bb3Spatrick     // In-place construct at Position.
377*3cab2bb3Spatrick     new (AlignedOffset) T{std::forward<Args>(args)...};
378*3cab2bb3Spatrick     ++Size;
379*3cab2bb3Spatrick     return reinterpret_cast<T *>(AlignedOffset);
380*3cab2bb3Spatrick   }
381*3cab2bb3Spatrick 
Append(const T & E)382*3cab2bb3Spatrick   T *Append(const T &E) XRAY_NEVER_INSTRUMENT {
383*3cab2bb3Spatrick     // FIXME: This is a duplication of AppenEmplace with the copy semantics
384*3cab2bb3Spatrick     // explicitly used, as a work-around to GCC 4.8 not invoking the copy
385*3cab2bb3Spatrick     // constructor with the placement new with braced-init syntax.
386*3cab2bb3Spatrick     DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) ||
387*3cab2bb3Spatrick            (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
388*3cab2bb3Spatrick     if (UNLIKELY(Head == &SentinelSegment)) {
389*3cab2bb3Spatrick       auto R = InitHeadAndTail();
390*3cab2bb3Spatrick       if (R == nullptr)
391*3cab2bb3Spatrick         return nullptr;
392*3cab2bb3Spatrick     }
393*3cab2bb3Spatrick 
394*3cab2bb3Spatrick     DCHECK_NE(Head, &SentinelSegment);
395*3cab2bb3Spatrick     DCHECK_NE(Tail, &SentinelSegment);
396*3cab2bb3Spatrick 
397*3cab2bb3Spatrick     auto Offset = Size % ElementsPerSegment;
398*3cab2bb3Spatrick     if (UNLIKELY(Size != 0 && Offset == 0))
399*3cab2bb3Spatrick       if (AppendNewSegment() == nullptr)
400*3cab2bb3Spatrick         return nullptr;
401*3cab2bb3Spatrick 
402*3cab2bb3Spatrick     DCHECK_NE(Tail, &SentinelSegment);
403*3cab2bb3Spatrick     auto Base = &Tail->Data;
404*3cab2bb3Spatrick     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
405*3cab2bb3Spatrick     DCHECK_LE(AlignedOffset + sizeof(T),
406*3cab2bb3Spatrick               reinterpret_cast<unsigned char *>(Tail) + SegmentSize);
407*3cab2bb3Spatrick 
408*3cab2bb3Spatrick     // In-place construct at Position.
409*3cab2bb3Spatrick     new (AlignedOffset) T(E);
410*3cab2bb3Spatrick     ++Size;
411*3cab2bb3Spatrick     return reinterpret_cast<T *>(AlignedOffset);
412*3cab2bb3Spatrick   }
413*3cab2bb3Spatrick 
414*3cab2bb3Spatrick   T &operator[](uint64_t Offset) const XRAY_NEVER_INSTRUMENT {
415*3cab2bb3Spatrick     DCHECK_LE(Offset, Size);
416*3cab2bb3Spatrick     // We need to traverse the array enough times to find the element at Offset.
417*3cab2bb3Spatrick     auto S = Head;
418*3cab2bb3Spatrick     while (Offset >= ElementsPerSegment) {
419*3cab2bb3Spatrick       S = S->Next;
420*3cab2bb3Spatrick       Offset -= ElementsPerSegment;
421*3cab2bb3Spatrick       DCHECK_NE(S, &SentinelSegment);
422*3cab2bb3Spatrick     }
423*3cab2bb3Spatrick     auto Base = &S->Data;
424*3cab2bb3Spatrick     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
425*3cab2bb3Spatrick     auto Position = reinterpret_cast<T *>(AlignedOffset);
426*3cab2bb3Spatrick     return *reinterpret_cast<T *>(Position);
427*3cab2bb3Spatrick   }
428*3cab2bb3Spatrick 
front()429*3cab2bb3Spatrick   T &front() const XRAY_NEVER_INSTRUMENT {
430*3cab2bb3Spatrick     DCHECK_NE(Head, &SentinelSegment);
431*3cab2bb3Spatrick     DCHECK_NE(Size, 0u);
432*3cab2bb3Spatrick     return *begin();
433*3cab2bb3Spatrick   }
434*3cab2bb3Spatrick 
back()435*3cab2bb3Spatrick   T &back() const XRAY_NEVER_INSTRUMENT {
436*3cab2bb3Spatrick     DCHECK_NE(Tail, &SentinelSegment);
437*3cab2bb3Spatrick     DCHECK_NE(Size, 0u);
438*3cab2bb3Spatrick     auto It = end();
439*3cab2bb3Spatrick     --It;
440*3cab2bb3Spatrick     return *It;
441*3cab2bb3Spatrick   }
442*3cab2bb3Spatrick 
443*3cab2bb3Spatrick   template <class Predicate>
find_element(Predicate P)444*3cab2bb3Spatrick   T *find_element(Predicate P) const XRAY_NEVER_INSTRUMENT {
445*3cab2bb3Spatrick     if (empty())
446*3cab2bb3Spatrick       return nullptr;
447*3cab2bb3Spatrick 
448*3cab2bb3Spatrick     auto E = end();
449*3cab2bb3Spatrick     for (auto I = begin(); I != E; ++I)
450*3cab2bb3Spatrick       if (P(*I))
451*3cab2bb3Spatrick         return &(*I);
452*3cab2bb3Spatrick 
453*3cab2bb3Spatrick     return nullptr;
454*3cab2bb3Spatrick   }
455*3cab2bb3Spatrick 
456*3cab2bb3Spatrick   /// Remove N Elements from the end. This leaves the blocks behind, and not
457*3cab2bb3Spatrick   /// require allocation of new blocks for new elements added after trimming.
trim(uint64_t Elements)458*3cab2bb3Spatrick   void trim(uint64_t Elements) XRAY_NEVER_INSTRUMENT {
459*3cab2bb3Spatrick     auto OldSize = Size;
460*3cab2bb3Spatrick     Elements = Elements > Size ? Size : Elements;
461*3cab2bb3Spatrick     Size -= Elements;
462*3cab2bb3Spatrick 
463*3cab2bb3Spatrick     // We compute the number of segments we're going to return from the tail by
464*3cab2bb3Spatrick     // counting how many elements have been trimmed. Given the following:
465*3cab2bb3Spatrick     //
466*3cab2bb3Spatrick     // - Each segment has N valid positions, where N > 0
467*3cab2bb3Spatrick     // - The previous size > current size
468*3cab2bb3Spatrick     //
469*3cab2bb3Spatrick     // To compute the number of segments to return, we need to perform the
470*3cab2bb3Spatrick     // following calculations for the number of segments required given 'x'
471*3cab2bb3Spatrick     // elements:
472*3cab2bb3Spatrick     //
473*3cab2bb3Spatrick     //   f(x) = {
474*3cab2bb3Spatrick     //            x == 0          : 0
475*3cab2bb3Spatrick     //          , 0 < x <= N      : 1
476*3cab2bb3Spatrick     //          , N < x <= max    : x / N + (x % N ? 1 : 0)
477*3cab2bb3Spatrick     //          }
478*3cab2bb3Spatrick     //
479*3cab2bb3Spatrick     // We can simplify this down to:
480*3cab2bb3Spatrick     //
481*3cab2bb3Spatrick     //   f(x) = {
482*3cab2bb3Spatrick     //            x == 0          : 0,
483*3cab2bb3Spatrick     //          , 0 < x <= max    : x / N + (x < N || x % N ? 1 : 0)
484*3cab2bb3Spatrick     //          }
485*3cab2bb3Spatrick     //
486*3cab2bb3Spatrick     // And further down to:
487*3cab2bb3Spatrick     //
488*3cab2bb3Spatrick     //   f(x) = x ? x / N + (x < N || x % N ? 1 : 0) : 0
489*3cab2bb3Spatrick     //
490*3cab2bb3Spatrick     // We can then perform the following calculation `s` which counts the number
491*3cab2bb3Spatrick     // of segments we need to remove from the end of the data structure:
492*3cab2bb3Spatrick     //
493*3cab2bb3Spatrick     //   s(p, c) = f(p) - f(c)
494*3cab2bb3Spatrick     //
495*3cab2bb3Spatrick     // If we treat p = previous size, and c = current size, and given the
496*3cab2bb3Spatrick     // properties above, the possible range for s(...) is [0..max(typeof(p))/N]
497*3cab2bb3Spatrick     // given that typeof(p) == typeof(c).
498*3cab2bb3Spatrick     auto F = [](uint64_t X) {
499*3cab2bb3Spatrick       return X ? (X / ElementsPerSegment) +
500*3cab2bb3Spatrick                      (X < ElementsPerSegment || X % ElementsPerSegment ? 1 : 0)
501*3cab2bb3Spatrick                : 0;
502*3cab2bb3Spatrick     };
503*3cab2bb3Spatrick     auto PS = F(OldSize);
504*3cab2bb3Spatrick     auto CS = F(Size);
505*3cab2bb3Spatrick     DCHECK_GE(PS, CS);
506*3cab2bb3Spatrick     auto SegmentsToTrim = PS - CS;
507*3cab2bb3Spatrick     for (auto I = 0uL; I < SegmentsToTrim; ++I) {
508*3cab2bb3Spatrick       // Here we place the current tail segment to the freelist. To do this
509*3cab2bb3Spatrick       // appropriately, we need to perform a splice operation on two
510*3cab2bb3Spatrick       // bidirectional linked-lists. In particular, we have the current state of
511*3cab2bb3Spatrick       // the doubly-linked list of segments:
512*3cab2bb3Spatrick       //
513*3cab2bb3Spatrick       //   @S@ <- s0 <-> s1 <-> ... <-> sT -> @S@
514*3cab2bb3Spatrick       //
515*3cab2bb3Spatrick       DCHECK_NE(Head, &SentinelSegment);
516*3cab2bb3Spatrick       DCHECK_NE(Tail, &SentinelSegment);
517*3cab2bb3Spatrick       DCHECK_EQ(Tail->Next, &SentinelSegment);
518*3cab2bb3Spatrick 
519*3cab2bb3Spatrick       if (Freelist == &SentinelSegment) {
520*3cab2bb3Spatrick         // Our two lists at this point are in this configuration:
521*3cab2bb3Spatrick         //
522*3cab2bb3Spatrick         //   Freelist: (potentially) @S@
523*3cab2bb3Spatrick         //   Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@
524*3cab2bb3Spatrick         //                  ^ Head                ^ Tail
525*3cab2bb3Spatrick         //
526*3cab2bb3Spatrick         // The end state for us will be this configuration:
527*3cab2bb3Spatrick         //
528*3cab2bb3Spatrick         //   Freelist: @S@<-sT->@S@
529*3cab2bb3Spatrick         //   Mainlist: @S@<-s0<->s1<->...<->sPT->@S@
530*3cab2bb3Spatrick         //                  ^ Head          ^ Tail
531*3cab2bb3Spatrick         //
532*3cab2bb3Spatrick         // The first step for us is to hold a reference to the tail of Mainlist,
533*3cab2bb3Spatrick         // which in our notation is represented by sT. We call this our "free
534*3cab2bb3Spatrick         // segment" which is the segment we are placing on the Freelist.
535*3cab2bb3Spatrick         //
536*3cab2bb3Spatrick         //   sF = sT
537*3cab2bb3Spatrick         //
538*3cab2bb3Spatrick         // Then, we also hold a reference to the "pre-tail" element, which we
539*3cab2bb3Spatrick         // call sPT:
540*3cab2bb3Spatrick         //
541*3cab2bb3Spatrick         //   sPT = pred(sT)
542*3cab2bb3Spatrick         //
543*3cab2bb3Spatrick         // We want to splice sT into the beginning of the Freelist, which in
544*3cab2bb3Spatrick         // an empty Freelist means placing a segment whose predecessor and
545*3cab2bb3Spatrick         // successor is the sentinel segment.
546*3cab2bb3Spatrick         //
547*3cab2bb3Spatrick         // The splice operation then can be performed in the following
548*3cab2bb3Spatrick         // algorithm:
549*3cab2bb3Spatrick         //
550*3cab2bb3Spatrick         //   succ(sPT) = S
551*3cab2bb3Spatrick         //   pred(sT) = S
552*3cab2bb3Spatrick         //   succ(sT) = Freelist
553*3cab2bb3Spatrick         //   Freelist = sT
554*3cab2bb3Spatrick         //   Tail = sPT
555*3cab2bb3Spatrick         //
556*3cab2bb3Spatrick         auto SPT = Tail->Prev;
557*3cab2bb3Spatrick         SPT->Next = &SentinelSegment;
558*3cab2bb3Spatrick         Tail->Prev = &SentinelSegment;
559*3cab2bb3Spatrick         Tail->Next = Freelist;
560*3cab2bb3Spatrick         Freelist = Tail;
561*3cab2bb3Spatrick         Tail = SPT;
562*3cab2bb3Spatrick 
563*3cab2bb3Spatrick         // Our post-conditions here are:
564*3cab2bb3Spatrick         DCHECK_EQ(Tail->Next, &SentinelSegment);
565*3cab2bb3Spatrick         DCHECK_EQ(Freelist->Prev, &SentinelSegment);
566*3cab2bb3Spatrick       } else {
567*3cab2bb3Spatrick         // In the other case, where the Freelist is not empty, we perform the
568*3cab2bb3Spatrick         // following transformation instead:
569*3cab2bb3Spatrick         //
570*3cab2bb3Spatrick         // This transforms the current state:
571*3cab2bb3Spatrick         //
572*3cab2bb3Spatrick         //   Freelist: @S@<-f0->@S@
573*3cab2bb3Spatrick         //                  ^ Freelist
574*3cab2bb3Spatrick         //   Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@
575*3cab2bb3Spatrick         //                  ^ Head                ^ Tail
576*3cab2bb3Spatrick         //
577*3cab2bb3Spatrick         // Into the following:
578*3cab2bb3Spatrick         //
579*3cab2bb3Spatrick         //   Freelist: @S@<-sT<->f0->@S@
580*3cab2bb3Spatrick         //                  ^ Freelist
581*3cab2bb3Spatrick         //   Mainlist: @S@<-s0<->s1<->...<->sPT->@S@
582*3cab2bb3Spatrick         //                  ^ Head          ^ Tail
583*3cab2bb3Spatrick         //
584*3cab2bb3Spatrick         // The algorithm is:
585*3cab2bb3Spatrick         //
586*3cab2bb3Spatrick         //   sFH = Freelist
587*3cab2bb3Spatrick         //   sPT = pred(sT)
588*3cab2bb3Spatrick         //   pred(SFH) = sT
589*3cab2bb3Spatrick         //   succ(sT) = Freelist
590*3cab2bb3Spatrick         //   pred(sT) = S
591*3cab2bb3Spatrick         //   succ(sPT) = S
592*3cab2bb3Spatrick         //   Tail = sPT
593*3cab2bb3Spatrick         //   Freelist = sT
594*3cab2bb3Spatrick         //
595*3cab2bb3Spatrick         auto SFH = Freelist;
596*3cab2bb3Spatrick         auto SPT = Tail->Prev;
597*3cab2bb3Spatrick         auto ST = Tail;
598*3cab2bb3Spatrick         SFH->Prev = ST;
599*3cab2bb3Spatrick         ST->Next = Freelist;
600*3cab2bb3Spatrick         ST->Prev = &SentinelSegment;
601*3cab2bb3Spatrick         SPT->Next = &SentinelSegment;
602*3cab2bb3Spatrick         Tail = SPT;
603*3cab2bb3Spatrick         Freelist = ST;
604*3cab2bb3Spatrick 
605*3cab2bb3Spatrick         // Our post-conditions here are:
606*3cab2bb3Spatrick         DCHECK_EQ(Tail->Next, &SentinelSegment);
607*3cab2bb3Spatrick         DCHECK_EQ(Freelist->Prev, &SentinelSegment);
608*3cab2bb3Spatrick         DCHECK_EQ(Freelist->Next->Prev, Freelist);
609*3cab2bb3Spatrick       }
610*3cab2bb3Spatrick     }
611*3cab2bb3Spatrick 
612*3cab2bb3Spatrick     // Now in case we've spliced all the segments in the end, we ensure that the
613*3cab2bb3Spatrick     // main list is "empty", or both the head and tail pointing to the sentinel
614*3cab2bb3Spatrick     // segment.
615*3cab2bb3Spatrick     if (Tail == &SentinelSegment)
616*3cab2bb3Spatrick       Head = Tail;
617*3cab2bb3Spatrick 
618*3cab2bb3Spatrick     DCHECK(
619*3cab2bb3Spatrick         (Size == 0 && Head == &SentinelSegment && Tail == &SentinelSegment) ||
620*3cab2bb3Spatrick         (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
621*3cab2bb3Spatrick     DCHECK(
622*3cab2bb3Spatrick         (Freelist != &SentinelSegment && Freelist->Prev == &SentinelSegment) ||
623*3cab2bb3Spatrick         (Freelist == &SentinelSegment && Tail->Next == &SentinelSegment));
624*3cab2bb3Spatrick   }
625*3cab2bb3Spatrick 
626*3cab2bb3Spatrick   // Provide iterators.
begin()627*3cab2bb3Spatrick   Iterator<T> begin() const XRAY_NEVER_INSTRUMENT {
628*3cab2bb3Spatrick     return Iterator<T>(Head, 0, Size);
629*3cab2bb3Spatrick   }
end()630*3cab2bb3Spatrick   Iterator<T> end() const XRAY_NEVER_INSTRUMENT {
631*3cab2bb3Spatrick     return Iterator<T>(Tail, Size, Size);
632*3cab2bb3Spatrick   }
cbegin()633*3cab2bb3Spatrick   Iterator<const T> cbegin() const XRAY_NEVER_INSTRUMENT {
634*3cab2bb3Spatrick     return Iterator<const T>(Head, 0, Size);
635*3cab2bb3Spatrick   }
cend()636*3cab2bb3Spatrick   Iterator<const T> cend() const XRAY_NEVER_INSTRUMENT {
637*3cab2bb3Spatrick     return Iterator<const T>(Tail, Size, Size);
638*3cab2bb3Spatrick   }
639*3cab2bb3Spatrick };
640*3cab2bb3Spatrick 
641*3cab2bb3Spatrick // We need to have this storage definition out-of-line so that the compiler can
642*3cab2bb3Spatrick // ensure that storage for the SentinelSegment is defined and has a single
643*3cab2bb3Spatrick // address.
644*3cab2bb3Spatrick template <class T>
645*3cab2bb3Spatrick typename Array<T>::Segment Array<T>::SentinelSegment{
646*3cab2bb3Spatrick     &Array<T>::SentinelSegment, &Array<T>::SentinelSegment, {'\0'}};
647*3cab2bb3Spatrick 
648*3cab2bb3Spatrick } // namespace __xray
649*3cab2bb3Spatrick 
650*3cab2bb3Spatrick #endif // XRAY_SEGMENTED_ARRAY_H
651