xref: /freebsd/contrib/llvm-project/compiler-rt/lib/xray/xray_segmented_array.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===-- xray_segmented_array.h ---------------------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file is a part of XRay, a dynamic runtime instrumentation system.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric // Defines the implementation of a segmented array, with fixed-size segments
120b57cec5SDimitry Andric // backing the segments.
130b57cec5SDimitry Andric //
140b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
150b57cec5SDimitry Andric #ifndef XRAY_SEGMENTED_ARRAY_H
160b57cec5SDimitry Andric #define XRAY_SEGMENTED_ARRAY_H
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric #include "sanitizer_common/sanitizer_allocator.h"
190b57cec5SDimitry Andric #include "xray_allocator.h"
200b57cec5SDimitry Andric #include "xray_utils.h"
210b57cec5SDimitry Andric #include <cassert>
220b57cec5SDimitry Andric #include <type_traits>
230b57cec5SDimitry Andric #include <utility>
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric namespace __xray {
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric /// The Array type provides an interface similar to std::vector<...> but does
280b57cec5SDimitry Andric /// not shrink in size. Once constructed, elements can be appended but cannot be
290b57cec5SDimitry Andric /// removed. The implementation is heavily dependent on the contract provided by
300b57cec5SDimitry Andric /// the Allocator type, in that all memory will be released when the Allocator
310b57cec5SDimitry Andric /// is destroyed. When an Array is destroyed, it will destroy elements in the
320b57cec5SDimitry Andric /// backing store but will not free the memory.
330b57cec5SDimitry Andric template <class T> class Array {
340b57cec5SDimitry Andric   struct Segment {
350b57cec5SDimitry Andric     Segment *Prev;
360b57cec5SDimitry Andric     Segment *Next;
370b57cec5SDimitry Andric     char Data[1];
380b57cec5SDimitry Andric   };
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric public:
410b57cec5SDimitry Andric   // Each segment of the array will be laid out with the following assumptions:
420b57cec5SDimitry Andric   //
430b57cec5SDimitry Andric   //   - Each segment will be on a cache-line address boundary (kCacheLineSize
440b57cec5SDimitry Andric   //     aligned).
450b57cec5SDimitry Andric   //
460b57cec5SDimitry Andric   //   - The elements will be accessed through an aligned pointer, dependent on
470b57cec5SDimitry Andric   //     the alignment of T.
480b57cec5SDimitry Andric   //
490b57cec5SDimitry Andric   //   - Each element is at least two-pointers worth from the beginning of the
500b57cec5SDimitry Andric   //     Segment, aligned properly, and the rest of the elements are accessed
510b57cec5SDimitry Andric   //     through appropriate alignment.
520b57cec5SDimitry Andric   //
530b57cec5SDimitry Andric   // We then compute the size of the segment to follow this logic:
540b57cec5SDimitry Andric   //
550b57cec5SDimitry Andric   //   - Compute the number of elements that can fit within
560b57cec5SDimitry Andric   //     kCacheLineSize-multiple segments, minus the size of two pointers.
570b57cec5SDimitry Andric   //
580b57cec5SDimitry Andric   //   - Request cacheline-multiple sized elements from the allocator.
59*0fca6ea1SDimitry Andric   static constexpr uint64_t AlignedElementStorageSize = sizeof(T);
600b57cec5SDimitry Andric 
610b57cec5SDimitry Andric   static constexpr uint64_t SegmentControlBlockSize = sizeof(Segment *) * 2;
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric   static constexpr uint64_t SegmentSize = nearest_boundary(
640b57cec5SDimitry Andric       SegmentControlBlockSize + next_pow2(sizeof(T)), kCacheLineSize);
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric   using AllocatorType = Allocator<SegmentSize>;
670b57cec5SDimitry Andric 
680b57cec5SDimitry Andric   static constexpr uint64_t ElementsPerSegment =
690b57cec5SDimitry Andric       (SegmentSize - SegmentControlBlockSize) / next_pow2(sizeof(T));
700b57cec5SDimitry Andric 
710b57cec5SDimitry Andric   static_assert(ElementsPerSegment > 0,
720b57cec5SDimitry Andric                 "Must have at least 1 element per segment.");
730b57cec5SDimitry Andric 
740b57cec5SDimitry Andric   static Segment SentinelSegment;
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric   using size_type = uint64_t;
770b57cec5SDimitry Andric 
780b57cec5SDimitry Andric private:
790b57cec5SDimitry Andric   // This Iterator models a BidirectionalIterator.
800b57cec5SDimitry Andric   template <class U> class Iterator {
810b57cec5SDimitry Andric     Segment *S = &SentinelSegment;
820b57cec5SDimitry Andric     uint64_t Offset = 0;
830b57cec5SDimitry Andric     uint64_t Size = 0;
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric   public:
Iterator(Segment * IS,uint64_t Off,uint64_t S)860b57cec5SDimitry Andric     Iterator(Segment *IS, uint64_t Off, uint64_t S) XRAY_NEVER_INSTRUMENT
870b57cec5SDimitry Andric         : S(IS),
880b57cec5SDimitry Andric           Offset(Off),
890b57cec5SDimitry Andric           Size(S) {}
900b57cec5SDimitry Andric     Iterator(const Iterator &) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
910b57cec5SDimitry Andric     Iterator() NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
920b57cec5SDimitry Andric     Iterator(Iterator &&) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
930b57cec5SDimitry Andric     Iterator &operator=(const Iterator &) XRAY_NEVER_INSTRUMENT = default;
940b57cec5SDimitry Andric     Iterator &operator=(Iterator &&) XRAY_NEVER_INSTRUMENT = default;
950b57cec5SDimitry Andric     ~Iterator() XRAY_NEVER_INSTRUMENT = default;
960b57cec5SDimitry Andric 
970b57cec5SDimitry Andric     Iterator &operator++() XRAY_NEVER_INSTRUMENT {
980b57cec5SDimitry Andric       if (++Offset % ElementsPerSegment || Offset == Size)
990b57cec5SDimitry Andric         return *this;
1000b57cec5SDimitry Andric 
1010b57cec5SDimitry Andric       // At this point, we know that Offset % N == 0, so we must advance the
1020b57cec5SDimitry Andric       // segment pointer.
1030b57cec5SDimitry Andric       DCHECK_EQ(Offset % ElementsPerSegment, 0);
1040b57cec5SDimitry Andric       DCHECK_NE(Offset, Size);
1050b57cec5SDimitry Andric       DCHECK_NE(S, &SentinelSegment);
1060b57cec5SDimitry Andric       DCHECK_NE(S->Next, &SentinelSegment);
1070b57cec5SDimitry Andric       S = S->Next;
1080b57cec5SDimitry Andric       DCHECK_NE(S, &SentinelSegment);
1090b57cec5SDimitry Andric       return *this;
1100b57cec5SDimitry Andric     }
1110b57cec5SDimitry Andric 
1120b57cec5SDimitry Andric     Iterator &operator--() XRAY_NEVER_INSTRUMENT {
1130b57cec5SDimitry Andric       DCHECK_NE(S, &SentinelSegment);
1140b57cec5SDimitry Andric       DCHECK_GT(Offset, 0);
1150b57cec5SDimitry Andric 
1160b57cec5SDimitry Andric       auto PreviousOffset = Offset--;
1170b57cec5SDimitry Andric       if (PreviousOffset != Size && PreviousOffset % ElementsPerSegment == 0) {
1180b57cec5SDimitry Andric         DCHECK_NE(S->Prev, &SentinelSegment);
1190b57cec5SDimitry Andric         S = S->Prev;
1200b57cec5SDimitry Andric       }
1210b57cec5SDimitry Andric 
1220b57cec5SDimitry Andric       return *this;
1230b57cec5SDimitry Andric     }
1240b57cec5SDimitry Andric 
1250b57cec5SDimitry Andric     Iterator operator++(int) XRAY_NEVER_INSTRUMENT {
1260b57cec5SDimitry Andric       Iterator Copy(*this);
1270b57cec5SDimitry Andric       ++(*this);
1280b57cec5SDimitry Andric       return Copy;
1290b57cec5SDimitry Andric     }
1300b57cec5SDimitry Andric 
1310b57cec5SDimitry Andric     Iterator operator--(int) XRAY_NEVER_INSTRUMENT {
1320b57cec5SDimitry Andric       Iterator Copy(*this);
1330b57cec5SDimitry Andric       --(*this);
1340b57cec5SDimitry Andric       return Copy;
1350b57cec5SDimitry Andric     }
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric     template <class V, class W>
1380b57cec5SDimitry Andric     friend bool operator==(const Iterator<V> &L,
1390b57cec5SDimitry Andric                            const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
1400b57cec5SDimitry Andric       return L.S == R.S && L.Offset == R.Offset;
1410b57cec5SDimitry Andric     }
1420b57cec5SDimitry Andric 
1430b57cec5SDimitry Andric     template <class V, class W>
1440b57cec5SDimitry Andric     friend bool operator!=(const Iterator<V> &L,
1450b57cec5SDimitry Andric                            const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
1460b57cec5SDimitry Andric       return !(L == R);
1470b57cec5SDimitry Andric     }
1480b57cec5SDimitry Andric 
1490b57cec5SDimitry Andric     U &operator*() const XRAY_NEVER_INSTRUMENT {
1500b57cec5SDimitry Andric       DCHECK_NE(S, &SentinelSegment);
1510b57cec5SDimitry Andric       auto RelOff = Offset % ElementsPerSegment;
1520b57cec5SDimitry Andric 
1530b57cec5SDimitry Andric       // We need to compute the character-aligned pointer, offset from the
1540b57cec5SDimitry Andric       // segment's Data location to get the element in the position of Offset.
1550b57cec5SDimitry Andric       auto Base = &S->Data;
1560b57cec5SDimitry Andric       auto AlignedOffset = Base + (RelOff * AlignedElementStorageSize);
1570b57cec5SDimitry Andric       return *reinterpret_cast<U *>(AlignedOffset);
1580b57cec5SDimitry Andric     }
1590b57cec5SDimitry Andric 
1600b57cec5SDimitry Andric     U *operator->() const XRAY_NEVER_INSTRUMENT { return &(**this); }
1610b57cec5SDimitry Andric   };
1620b57cec5SDimitry Andric 
1630b57cec5SDimitry Andric   AllocatorType *Alloc;
1640b57cec5SDimitry Andric   Segment *Head;
1650b57cec5SDimitry Andric   Segment *Tail;
1660b57cec5SDimitry Andric 
1670b57cec5SDimitry Andric   // Here we keep track of segments in the freelist, to allow us to re-use
1680b57cec5SDimitry Andric   // segments when elements are trimmed off the end.
1690b57cec5SDimitry Andric   Segment *Freelist;
1700b57cec5SDimitry Andric   uint64_t Size;
1710b57cec5SDimitry Andric 
1720b57cec5SDimitry Andric   // ===============================
1730b57cec5SDimitry Andric   // In the following implementation, we work through the algorithms and the
1740b57cec5SDimitry Andric   // list operations using the following notation:
1750b57cec5SDimitry Andric   //
1760b57cec5SDimitry Andric   //   - pred(s) is the predecessor (previous node accessor) and succ(s) is
1770b57cec5SDimitry Andric   //     the successor (next node accessor).
1780b57cec5SDimitry Andric   //
1790b57cec5SDimitry Andric   //   - S is a sentinel segment, which has the following property:
1800b57cec5SDimitry Andric   //
1810b57cec5SDimitry Andric   //         pred(S) == succ(S) == S
1820b57cec5SDimitry Andric   //
1830b57cec5SDimitry Andric   //   - @ is a loop operator, which can imply pred(s) == s if it appears on
1840b57cec5SDimitry Andric   //     the left of s, or succ(s) == S if it appears on the right of s.
1850b57cec5SDimitry Andric   //
1860b57cec5SDimitry Andric   //   - sL <-> sR : means a bidirectional relation between sL and sR, which
1870b57cec5SDimitry Andric   //     means:
1880b57cec5SDimitry Andric   //
1890b57cec5SDimitry Andric   //         succ(sL) == sR && pred(SR) == sL
1900b57cec5SDimitry Andric   //
1910b57cec5SDimitry Andric   //   - sL -> sR : implies a unidirectional relation between sL and SR,
1920b57cec5SDimitry Andric   //     with the following properties:
1930b57cec5SDimitry Andric   //
1940b57cec5SDimitry Andric   //         succ(sL) == sR
1950b57cec5SDimitry Andric   //
1960b57cec5SDimitry Andric   //     sL <- sR : implies a unidirectional relation between sR and sL,
1970b57cec5SDimitry Andric   //     with the following properties:
1980b57cec5SDimitry Andric   //
1990b57cec5SDimitry Andric   //         pred(sR) == sL
2000b57cec5SDimitry Andric   //
2010b57cec5SDimitry Andric   // ===============================
2020b57cec5SDimitry Andric 
NewSegment()2030b57cec5SDimitry Andric   Segment *NewSegment() XRAY_NEVER_INSTRUMENT {
2040b57cec5SDimitry Andric     // We need to handle the case in which enough elements have been trimmed to
2050b57cec5SDimitry Andric     // allow us to re-use segments we've allocated before. For this we look into
2060b57cec5SDimitry Andric     // the Freelist, to see whether we need to actually allocate new blocks or
2070b57cec5SDimitry Andric     // just re-use blocks we've already seen before.
2080b57cec5SDimitry Andric     if (Freelist != &SentinelSegment) {
2090b57cec5SDimitry Andric       // The current state of lists resemble something like this at this point:
2100b57cec5SDimitry Andric       //
2110b57cec5SDimitry Andric       //   Freelist: @S@<-f0->...<->fN->@S@
2120b57cec5SDimitry Andric       //                  ^ Freelist
2130b57cec5SDimitry Andric       //
2140b57cec5SDimitry Andric       // We want to perform a splice of `f0` from Freelist to a temporary list,
2150b57cec5SDimitry Andric       // which looks like:
2160b57cec5SDimitry Andric       //
2170b57cec5SDimitry Andric       //   Templist: @S@<-f0->@S@
2180b57cec5SDimitry Andric       //                  ^ FreeSegment
2190b57cec5SDimitry Andric       //
2200b57cec5SDimitry Andric       // Our algorithm preconditions are:
2210b57cec5SDimitry Andric       DCHECK_EQ(Freelist->Prev, &SentinelSegment);
2220b57cec5SDimitry Andric 
2230b57cec5SDimitry Andric       // Then the algorithm we implement is:
2240b57cec5SDimitry Andric       //
2250b57cec5SDimitry Andric       //   SFS = Freelist
2260b57cec5SDimitry Andric       //   Freelist = succ(Freelist)
2270b57cec5SDimitry Andric       //   if (Freelist != S)
2280b57cec5SDimitry Andric       //     pred(Freelist) = S
2290b57cec5SDimitry Andric       //   succ(SFS) = S
2300b57cec5SDimitry Andric       //   pred(SFS) = S
2310b57cec5SDimitry Andric       //
2320b57cec5SDimitry Andric       auto *FreeSegment = Freelist;
2330b57cec5SDimitry Andric       Freelist = Freelist->Next;
2340b57cec5SDimitry Andric 
2350b57cec5SDimitry Andric       // Note that we need to handle the case where Freelist is now pointing to
2360b57cec5SDimitry Andric       // S, which we don't want to be overwriting.
2370b57cec5SDimitry Andric       // TODO: Determine whether the cost of the branch is higher than the cost
2380b57cec5SDimitry Andric       // of the blind assignment.
2390b57cec5SDimitry Andric       if (Freelist != &SentinelSegment)
2400b57cec5SDimitry Andric         Freelist->Prev = &SentinelSegment;
2410b57cec5SDimitry Andric 
2420b57cec5SDimitry Andric       FreeSegment->Next = &SentinelSegment;
2430b57cec5SDimitry Andric       FreeSegment->Prev = &SentinelSegment;
2440b57cec5SDimitry Andric 
2450b57cec5SDimitry Andric       // Our postconditions are:
2460b57cec5SDimitry Andric       DCHECK_EQ(Freelist->Prev, &SentinelSegment);
2470b57cec5SDimitry Andric       DCHECK_NE(FreeSegment, &SentinelSegment);
2480b57cec5SDimitry Andric       return FreeSegment;
2490b57cec5SDimitry Andric     }
2500b57cec5SDimitry Andric 
2510b57cec5SDimitry Andric     auto SegmentBlock = Alloc->Allocate();
2520b57cec5SDimitry Andric     if (SegmentBlock.Data == nullptr)
2530b57cec5SDimitry Andric       return nullptr;
2540b57cec5SDimitry Andric 
2550b57cec5SDimitry Andric     // Placement-new the Segment element at the beginning of the SegmentBlock.
2560b57cec5SDimitry Andric     new (SegmentBlock.Data) Segment{&SentinelSegment, &SentinelSegment, {0}};
2570b57cec5SDimitry Andric     auto SB = reinterpret_cast<Segment *>(SegmentBlock.Data);
2580b57cec5SDimitry Andric     return SB;
2590b57cec5SDimitry Andric   }
2600b57cec5SDimitry Andric 
InitHeadAndTail()2610b57cec5SDimitry Andric   Segment *InitHeadAndTail() XRAY_NEVER_INSTRUMENT {
2620b57cec5SDimitry Andric     DCHECK_EQ(Head, &SentinelSegment);
2630b57cec5SDimitry Andric     DCHECK_EQ(Tail, &SentinelSegment);
2640b57cec5SDimitry Andric     auto S = NewSegment();
2650b57cec5SDimitry Andric     if (S == nullptr)
2660b57cec5SDimitry Andric       return nullptr;
2670b57cec5SDimitry Andric     DCHECK_EQ(S->Next, &SentinelSegment);
2680b57cec5SDimitry Andric     DCHECK_EQ(S->Prev, &SentinelSegment);
2690b57cec5SDimitry Andric     DCHECK_NE(S, &SentinelSegment);
2700b57cec5SDimitry Andric     Head = S;
2710b57cec5SDimitry Andric     Tail = S;
2720b57cec5SDimitry Andric     DCHECK_EQ(Head, Tail);
2730b57cec5SDimitry Andric     DCHECK_EQ(Tail->Next, &SentinelSegment);
2740b57cec5SDimitry Andric     DCHECK_EQ(Tail->Prev, &SentinelSegment);
2750b57cec5SDimitry Andric     return S;
2760b57cec5SDimitry Andric   }
2770b57cec5SDimitry Andric 
AppendNewSegment()2780b57cec5SDimitry Andric   Segment *AppendNewSegment() XRAY_NEVER_INSTRUMENT {
2790b57cec5SDimitry Andric     auto S = NewSegment();
2800b57cec5SDimitry Andric     if (S == nullptr)
2810b57cec5SDimitry Andric       return nullptr;
2820b57cec5SDimitry Andric     DCHECK_NE(Tail, &SentinelSegment);
2830b57cec5SDimitry Andric     DCHECK_EQ(Tail->Next, &SentinelSegment);
2840b57cec5SDimitry Andric     DCHECK_EQ(S->Prev, &SentinelSegment);
2850b57cec5SDimitry Andric     DCHECK_EQ(S->Next, &SentinelSegment);
2860b57cec5SDimitry Andric     S->Prev = Tail;
2870b57cec5SDimitry Andric     Tail->Next = S;
2880b57cec5SDimitry Andric     Tail = S;
2890b57cec5SDimitry Andric     DCHECK_EQ(S, S->Prev->Next);
2900b57cec5SDimitry Andric     DCHECK_EQ(Tail->Next, &SentinelSegment);
2910b57cec5SDimitry Andric     return S;
2920b57cec5SDimitry Andric   }
2930b57cec5SDimitry Andric 
2940b57cec5SDimitry Andric public:
Array(AllocatorType & A)2950b57cec5SDimitry Andric   explicit Array(AllocatorType &A) XRAY_NEVER_INSTRUMENT
2960b57cec5SDimitry Andric       : Alloc(&A),
2970b57cec5SDimitry Andric         Head(&SentinelSegment),
2980b57cec5SDimitry Andric         Tail(&SentinelSegment),
2990b57cec5SDimitry Andric         Freelist(&SentinelSegment),
3000b57cec5SDimitry Andric         Size(0) {}
3010b57cec5SDimitry Andric 
Array()3020b57cec5SDimitry Andric   Array() XRAY_NEVER_INSTRUMENT : Alloc(nullptr),
3030b57cec5SDimitry Andric                                   Head(&SentinelSegment),
3040b57cec5SDimitry Andric                                   Tail(&SentinelSegment),
3050b57cec5SDimitry Andric                                   Freelist(&SentinelSegment),
3060b57cec5SDimitry Andric                                   Size(0) {}
3070b57cec5SDimitry Andric 
3080b57cec5SDimitry Andric   Array(const Array &) = delete;
3090b57cec5SDimitry Andric   Array &operator=(const Array &) = delete;
3100b57cec5SDimitry Andric 
Array(Array && O)3110b57cec5SDimitry Andric   Array(Array &&O) XRAY_NEVER_INSTRUMENT : Alloc(O.Alloc),
3120b57cec5SDimitry Andric                                            Head(O.Head),
3130b57cec5SDimitry Andric                                            Tail(O.Tail),
3140b57cec5SDimitry Andric                                            Freelist(O.Freelist),
3150b57cec5SDimitry Andric                                            Size(O.Size) {
3160b57cec5SDimitry Andric     O.Alloc = nullptr;
3170b57cec5SDimitry Andric     O.Head = &SentinelSegment;
3180b57cec5SDimitry Andric     O.Tail = &SentinelSegment;
3190b57cec5SDimitry Andric     O.Size = 0;
3200b57cec5SDimitry Andric     O.Freelist = &SentinelSegment;
3210b57cec5SDimitry Andric   }
3220b57cec5SDimitry Andric 
3230b57cec5SDimitry Andric   Array &operator=(Array &&O) XRAY_NEVER_INSTRUMENT {
3240b57cec5SDimitry Andric     Alloc = O.Alloc;
3250b57cec5SDimitry Andric     O.Alloc = nullptr;
3260b57cec5SDimitry Andric     Head = O.Head;
3270b57cec5SDimitry Andric     O.Head = &SentinelSegment;
3280b57cec5SDimitry Andric     Tail = O.Tail;
3290b57cec5SDimitry Andric     O.Tail = &SentinelSegment;
3300b57cec5SDimitry Andric     Freelist = O.Freelist;
3310b57cec5SDimitry Andric     O.Freelist = &SentinelSegment;
3320b57cec5SDimitry Andric     Size = O.Size;
3330b57cec5SDimitry Andric     O.Size = 0;
3340b57cec5SDimitry Andric     return *this;
3350b57cec5SDimitry Andric   }
3360b57cec5SDimitry Andric 
~Array()3370b57cec5SDimitry Andric   ~Array() XRAY_NEVER_INSTRUMENT {
3380b57cec5SDimitry Andric     for (auto &E : *this)
3390b57cec5SDimitry Andric       (&E)->~T();
3400b57cec5SDimitry Andric   }
3410b57cec5SDimitry Andric 
empty()3420b57cec5SDimitry Andric   bool empty() const XRAY_NEVER_INSTRUMENT { return Size == 0; }
3430b57cec5SDimitry Andric 
allocator()3440b57cec5SDimitry Andric   AllocatorType &allocator() const XRAY_NEVER_INSTRUMENT {
3450b57cec5SDimitry Andric     DCHECK_NE(Alloc, nullptr);
3460b57cec5SDimitry Andric     return *Alloc;
3470b57cec5SDimitry Andric   }
3480b57cec5SDimitry Andric 
size()3490b57cec5SDimitry Andric   uint64_t size() const XRAY_NEVER_INSTRUMENT { return Size; }
3500b57cec5SDimitry Andric 
3510b57cec5SDimitry Andric   template <class... Args>
AppendEmplace(Args &&...args)3520b57cec5SDimitry Andric   T *AppendEmplace(Args &&... args) XRAY_NEVER_INSTRUMENT {
3530b57cec5SDimitry Andric     DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) ||
3540b57cec5SDimitry Andric            (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
3550b57cec5SDimitry Andric     if (UNLIKELY(Head == &SentinelSegment)) {
3560b57cec5SDimitry Andric       auto R = InitHeadAndTail();
3570b57cec5SDimitry Andric       if (R == nullptr)
3580b57cec5SDimitry Andric         return nullptr;
3590b57cec5SDimitry Andric     }
3600b57cec5SDimitry Andric 
3610b57cec5SDimitry Andric     DCHECK_NE(Head, &SentinelSegment);
3620b57cec5SDimitry Andric     DCHECK_NE(Tail, &SentinelSegment);
3630b57cec5SDimitry Andric 
3640b57cec5SDimitry Andric     auto Offset = Size % ElementsPerSegment;
3650b57cec5SDimitry Andric     if (UNLIKELY(Size != 0 && Offset == 0))
3660b57cec5SDimitry Andric       if (AppendNewSegment() == nullptr)
3670b57cec5SDimitry Andric         return nullptr;
3680b57cec5SDimitry Andric 
3690b57cec5SDimitry Andric     DCHECK_NE(Tail, &SentinelSegment);
3700b57cec5SDimitry Andric     auto Base = &Tail->Data;
3710b57cec5SDimitry Andric     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
3720b57cec5SDimitry Andric     DCHECK_LE(AlignedOffset + sizeof(T),
3730b57cec5SDimitry Andric               reinterpret_cast<unsigned char *>(Base) + SegmentSize);
3740b57cec5SDimitry Andric 
3750b57cec5SDimitry Andric     // In-place construct at Position.
3760b57cec5SDimitry Andric     new (AlignedOffset) T{std::forward<Args>(args)...};
3770b57cec5SDimitry Andric     ++Size;
3780b57cec5SDimitry Andric     return reinterpret_cast<T *>(AlignedOffset);
3790b57cec5SDimitry Andric   }
3800b57cec5SDimitry Andric 
Append(const T & E)3810b57cec5SDimitry Andric   T *Append(const T &E) XRAY_NEVER_INSTRUMENT {
3820b57cec5SDimitry Andric     // FIXME: This is a duplication of AppenEmplace with the copy semantics
3830b57cec5SDimitry Andric     // explicitly used, as a work-around to GCC 4.8 not invoking the copy
3840b57cec5SDimitry Andric     // constructor with the placement new with braced-init syntax.
3850b57cec5SDimitry Andric     DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) ||
3860b57cec5SDimitry Andric            (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
3870b57cec5SDimitry Andric     if (UNLIKELY(Head == &SentinelSegment)) {
3880b57cec5SDimitry Andric       auto R = InitHeadAndTail();
3890b57cec5SDimitry Andric       if (R == nullptr)
3900b57cec5SDimitry Andric         return nullptr;
3910b57cec5SDimitry Andric     }
3920b57cec5SDimitry Andric 
3930b57cec5SDimitry Andric     DCHECK_NE(Head, &SentinelSegment);
3940b57cec5SDimitry Andric     DCHECK_NE(Tail, &SentinelSegment);
3950b57cec5SDimitry Andric 
3960b57cec5SDimitry Andric     auto Offset = Size % ElementsPerSegment;
3970b57cec5SDimitry Andric     if (UNLIKELY(Size != 0 && Offset == 0))
3980b57cec5SDimitry Andric       if (AppendNewSegment() == nullptr)
3990b57cec5SDimitry Andric         return nullptr;
4000b57cec5SDimitry Andric 
4010b57cec5SDimitry Andric     DCHECK_NE(Tail, &SentinelSegment);
4020b57cec5SDimitry Andric     auto Base = &Tail->Data;
4030b57cec5SDimitry Andric     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
4040b57cec5SDimitry Andric     DCHECK_LE(AlignedOffset + sizeof(T),
4050b57cec5SDimitry Andric               reinterpret_cast<unsigned char *>(Tail) + SegmentSize);
4060b57cec5SDimitry Andric 
4070b57cec5SDimitry Andric     // In-place construct at Position.
4080b57cec5SDimitry Andric     new (AlignedOffset) T(E);
4090b57cec5SDimitry Andric     ++Size;
4100b57cec5SDimitry Andric     return reinterpret_cast<T *>(AlignedOffset);
4110b57cec5SDimitry Andric   }
4120b57cec5SDimitry Andric 
4130b57cec5SDimitry Andric   T &operator[](uint64_t Offset) const XRAY_NEVER_INSTRUMENT {
4140b57cec5SDimitry Andric     DCHECK_LE(Offset, Size);
4150b57cec5SDimitry Andric     // We need to traverse the array enough times to find the element at Offset.
4160b57cec5SDimitry Andric     auto S = Head;
4170b57cec5SDimitry Andric     while (Offset >= ElementsPerSegment) {
4180b57cec5SDimitry Andric       S = S->Next;
4190b57cec5SDimitry Andric       Offset -= ElementsPerSegment;
4200b57cec5SDimitry Andric       DCHECK_NE(S, &SentinelSegment);
4210b57cec5SDimitry Andric     }
4220b57cec5SDimitry Andric     auto Base = &S->Data;
4230b57cec5SDimitry Andric     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
4240b57cec5SDimitry Andric     auto Position = reinterpret_cast<T *>(AlignedOffset);
4250b57cec5SDimitry Andric     return *reinterpret_cast<T *>(Position);
4260b57cec5SDimitry Andric   }
4270b57cec5SDimitry Andric 
front()4280b57cec5SDimitry Andric   T &front() const XRAY_NEVER_INSTRUMENT {
4290b57cec5SDimitry Andric     DCHECK_NE(Head, &SentinelSegment);
4300b57cec5SDimitry Andric     DCHECK_NE(Size, 0u);
4310b57cec5SDimitry Andric     return *begin();
4320b57cec5SDimitry Andric   }
4330b57cec5SDimitry Andric 
back()4340b57cec5SDimitry Andric   T &back() const XRAY_NEVER_INSTRUMENT {
4350b57cec5SDimitry Andric     DCHECK_NE(Tail, &SentinelSegment);
4360b57cec5SDimitry Andric     DCHECK_NE(Size, 0u);
4370b57cec5SDimitry Andric     auto It = end();
4380b57cec5SDimitry Andric     --It;
4390b57cec5SDimitry Andric     return *It;
4400b57cec5SDimitry Andric   }
4410b57cec5SDimitry Andric 
4420b57cec5SDimitry Andric   template <class Predicate>
find_element(Predicate P)4430b57cec5SDimitry Andric   T *find_element(Predicate P) const XRAY_NEVER_INSTRUMENT {
4440b57cec5SDimitry Andric     if (empty())
4450b57cec5SDimitry Andric       return nullptr;
4460b57cec5SDimitry Andric 
4470b57cec5SDimitry Andric     auto E = end();
4480b57cec5SDimitry Andric     for (auto I = begin(); I != E; ++I)
4490b57cec5SDimitry Andric       if (P(*I))
4500b57cec5SDimitry Andric         return &(*I);
4510b57cec5SDimitry Andric 
4520b57cec5SDimitry Andric     return nullptr;
4530b57cec5SDimitry Andric   }
4540b57cec5SDimitry Andric 
4550b57cec5SDimitry Andric   /// Remove N Elements from the end. This leaves the blocks behind, and not
4560b57cec5SDimitry Andric   /// require allocation of new blocks for new elements added after trimming.
trim(uint64_t Elements)4570b57cec5SDimitry Andric   void trim(uint64_t Elements) XRAY_NEVER_INSTRUMENT {
4580b57cec5SDimitry Andric     auto OldSize = Size;
4590b57cec5SDimitry Andric     Elements = Elements > Size ? Size : Elements;
4600b57cec5SDimitry Andric     Size -= Elements;
4610b57cec5SDimitry Andric 
4620b57cec5SDimitry Andric     // We compute the number of segments we're going to return from the tail by
4630b57cec5SDimitry Andric     // counting how many elements have been trimmed. Given the following:
4640b57cec5SDimitry Andric     //
4650b57cec5SDimitry Andric     // - Each segment has N valid positions, where N > 0
4660b57cec5SDimitry Andric     // - The previous size > current size
4670b57cec5SDimitry Andric     //
4680b57cec5SDimitry Andric     // To compute the number of segments to return, we need to perform the
4690b57cec5SDimitry Andric     // following calculations for the number of segments required given 'x'
4700b57cec5SDimitry Andric     // elements:
4710b57cec5SDimitry Andric     //
4720b57cec5SDimitry Andric     //   f(x) = {
4730b57cec5SDimitry Andric     //            x == 0          : 0
4740b57cec5SDimitry Andric     //          , 0 < x <= N      : 1
4750b57cec5SDimitry Andric     //          , N < x <= max    : x / N + (x % N ? 1 : 0)
4760b57cec5SDimitry Andric     //          }
4770b57cec5SDimitry Andric     //
4780b57cec5SDimitry Andric     // We can simplify this down to:
4790b57cec5SDimitry Andric     //
4800b57cec5SDimitry Andric     //   f(x) = {
4810b57cec5SDimitry Andric     //            x == 0          : 0,
4820b57cec5SDimitry Andric     //          , 0 < x <= max    : x / N + (x < N || x % N ? 1 : 0)
4830b57cec5SDimitry Andric     //          }
4840b57cec5SDimitry Andric     //
4850b57cec5SDimitry Andric     // And further down to:
4860b57cec5SDimitry Andric     //
4870b57cec5SDimitry Andric     //   f(x) = x ? x / N + (x < N || x % N ? 1 : 0) : 0
4880b57cec5SDimitry Andric     //
4890b57cec5SDimitry Andric     // We can then perform the following calculation `s` which counts the number
4900b57cec5SDimitry Andric     // of segments we need to remove from the end of the data structure:
4910b57cec5SDimitry Andric     //
4920b57cec5SDimitry Andric     //   s(p, c) = f(p) - f(c)
4930b57cec5SDimitry Andric     //
4940b57cec5SDimitry Andric     // If we treat p = previous size, and c = current size, and given the
4950b57cec5SDimitry Andric     // properties above, the possible range for s(...) is [0..max(typeof(p))/N]
4960b57cec5SDimitry Andric     // given that typeof(p) == typeof(c).
4970b57cec5SDimitry Andric     auto F = [](uint64_t X) {
4980b57cec5SDimitry Andric       return X ? (X / ElementsPerSegment) +
4990b57cec5SDimitry Andric                      (X < ElementsPerSegment || X % ElementsPerSegment ? 1 : 0)
5000b57cec5SDimitry Andric                : 0;
5010b57cec5SDimitry Andric     };
5020b57cec5SDimitry Andric     auto PS = F(OldSize);
5030b57cec5SDimitry Andric     auto CS = F(Size);
5040b57cec5SDimitry Andric     DCHECK_GE(PS, CS);
5050b57cec5SDimitry Andric     auto SegmentsToTrim = PS - CS;
5060b57cec5SDimitry Andric     for (auto I = 0uL; I < SegmentsToTrim; ++I) {
5070b57cec5SDimitry Andric       // Here we place the current tail segment to the freelist. To do this
5080b57cec5SDimitry Andric       // appropriately, we need to perform a splice operation on two
5090b57cec5SDimitry Andric       // bidirectional linked-lists. In particular, we have the current state of
5100b57cec5SDimitry Andric       // the doubly-linked list of segments:
5110b57cec5SDimitry Andric       //
5120b57cec5SDimitry Andric       //   @S@ <- s0 <-> s1 <-> ... <-> sT -> @S@
5130b57cec5SDimitry Andric       //
5140b57cec5SDimitry Andric       DCHECK_NE(Head, &SentinelSegment);
5150b57cec5SDimitry Andric       DCHECK_NE(Tail, &SentinelSegment);
5160b57cec5SDimitry Andric       DCHECK_EQ(Tail->Next, &SentinelSegment);
5170b57cec5SDimitry Andric 
5180b57cec5SDimitry Andric       if (Freelist == &SentinelSegment) {
5190b57cec5SDimitry Andric         // Our two lists at this point are in this configuration:
5200b57cec5SDimitry Andric         //
5210b57cec5SDimitry Andric         //   Freelist: (potentially) @S@
5220b57cec5SDimitry Andric         //   Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@
5230b57cec5SDimitry Andric         //                  ^ Head                ^ Tail
5240b57cec5SDimitry Andric         //
5250b57cec5SDimitry Andric         // The end state for us will be this configuration:
5260b57cec5SDimitry Andric         //
5270b57cec5SDimitry Andric         //   Freelist: @S@<-sT->@S@
5280b57cec5SDimitry Andric         //   Mainlist: @S@<-s0<->s1<->...<->sPT->@S@
5290b57cec5SDimitry Andric         //                  ^ Head          ^ Tail
5300b57cec5SDimitry Andric         //
5310b57cec5SDimitry Andric         // The first step for us is to hold a reference to the tail of Mainlist,
5320b57cec5SDimitry Andric         // which in our notation is represented by sT. We call this our "free
5330b57cec5SDimitry Andric         // segment" which is the segment we are placing on the Freelist.
5340b57cec5SDimitry Andric         //
5350b57cec5SDimitry Andric         //   sF = sT
5360b57cec5SDimitry Andric         //
5370b57cec5SDimitry Andric         // Then, we also hold a reference to the "pre-tail" element, which we
5380b57cec5SDimitry Andric         // call sPT:
5390b57cec5SDimitry Andric         //
5400b57cec5SDimitry Andric         //   sPT = pred(sT)
5410b57cec5SDimitry Andric         //
5420b57cec5SDimitry Andric         // We want to splice sT into the beginning of the Freelist, which in
5430b57cec5SDimitry Andric         // an empty Freelist means placing a segment whose predecessor and
5440b57cec5SDimitry Andric         // successor is the sentinel segment.
5450b57cec5SDimitry Andric         //
5460b57cec5SDimitry Andric         // The splice operation then can be performed in the following
5470b57cec5SDimitry Andric         // algorithm:
5480b57cec5SDimitry Andric         //
5490b57cec5SDimitry Andric         //   succ(sPT) = S
5500b57cec5SDimitry Andric         //   pred(sT) = S
5510b57cec5SDimitry Andric         //   succ(sT) = Freelist
5520b57cec5SDimitry Andric         //   Freelist = sT
5530b57cec5SDimitry Andric         //   Tail = sPT
5540b57cec5SDimitry Andric         //
5550b57cec5SDimitry Andric         auto SPT = Tail->Prev;
5560b57cec5SDimitry Andric         SPT->Next = &SentinelSegment;
5570b57cec5SDimitry Andric         Tail->Prev = &SentinelSegment;
5580b57cec5SDimitry Andric         Tail->Next = Freelist;
5590b57cec5SDimitry Andric         Freelist = Tail;
5600b57cec5SDimitry Andric         Tail = SPT;
5610b57cec5SDimitry Andric 
5620b57cec5SDimitry Andric         // Our post-conditions here are:
5630b57cec5SDimitry Andric         DCHECK_EQ(Tail->Next, &SentinelSegment);
5640b57cec5SDimitry Andric         DCHECK_EQ(Freelist->Prev, &SentinelSegment);
5650b57cec5SDimitry Andric       } else {
5660b57cec5SDimitry Andric         // In the other case, where the Freelist is not empty, we perform the
5670b57cec5SDimitry Andric         // following transformation instead:
5680b57cec5SDimitry Andric         //
5690b57cec5SDimitry Andric         // This transforms the current state:
5700b57cec5SDimitry Andric         //
5710b57cec5SDimitry Andric         //   Freelist: @S@<-f0->@S@
5720b57cec5SDimitry Andric         //                  ^ Freelist
5730b57cec5SDimitry Andric         //   Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@
5740b57cec5SDimitry Andric         //                  ^ Head                ^ Tail
5750b57cec5SDimitry Andric         //
5760b57cec5SDimitry Andric         // Into the following:
5770b57cec5SDimitry Andric         //
5780b57cec5SDimitry Andric         //   Freelist: @S@<-sT<->f0->@S@
5790b57cec5SDimitry Andric         //                  ^ Freelist
5800b57cec5SDimitry Andric         //   Mainlist: @S@<-s0<->s1<->...<->sPT->@S@
5810b57cec5SDimitry Andric         //                  ^ Head          ^ Tail
5820b57cec5SDimitry Andric         //
5830b57cec5SDimitry Andric         // The algorithm is:
5840b57cec5SDimitry Andric         //
5850b57cec5SDimitry Andric         //   sFH = Freelist
5860b57cec5SDimitry Andric         //   sPT = pred(sT)
5870b57cec5SDimitry Andric         //   pred(SFH) = sT
5880b57cec5SDimitry Andric         //   succ(sT) = Freelist
5890b57cec5SDimitry Andric         //   pred(sT) = S
5900b57cec5SDimitry Andric         //   succ(sPT) = S
5910b57cec5SDimitry Andric         //   Tail = sPT
5920b57cec5SDimitry Andric         //   Freelist = sT
5930b57cec5SDimitry Andric         //
5940b57cec5SDimitry Andric         auto SFH = Freelist;
5950b57cec5SDimitry Andric         auto SPT = Tail->Prev;
5960b57cec5SDimitry Andric         auto ST = Tail;
5970b57cec5SDimitry Andric         SFH->Prev = ST;
5980b57cec5SDimitry Andric         ST->Next = Freelist;
5990b57cec5SDimitry Andric         ST->Prev = &SentinelSegment;
6000b57cec5SDimitry Andric         SPT->Next = &SentinelSegment;
6010b57cec5SDimitry Andric         Tail = SPT;
6020b57cec5SDimitry Andric         Freelist = ST;
6030b57cec5SDimitry Andric 
6040b57cec5SDimitry Andric         // Our post-conditions here are:
6050b57cec5SDimitry Andric         DCHECK_EQ(Tail->Next, &SentinelSegment);
6060b57cec5SDimitry Andric         DCHECK_EQ(Freelist->Prev, &SentinelSegment);
6070b57cec5SDimitry Andric         DCHECK_EQ(Freelist->Next->Prev, Freelist);
6080b57cec5SDimitry Andric       }
6090b57cec5SDimitry Andric     }
6100b57cec5SDimitry Andric 
6110b57cec5SDimitry Andric     // Now in case we've spliced all the segments in the end, we ensure that the
6120b57cec5SDimitry Andric     // main list is "empty", or both the head and tail pointing to the sentinel
6130b57cec5SDimitry Andric     // segment.
6140b57cec5SDimitry Andric     if (Tail == &SentinelSegment)
6150b57cec5SDimitry Andric       Head = Tail;
6160b57cec5SDimitry Andric 
6170b57cec5SDimitry Andric     DCHECK(
6180b57cec5SDimitry Andric         (Size == 0 && Head == &SentinelSegment && Tail == &SentinelSegment) ||
6190b57cec5SDimitry Andric         (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
6200b57cec5SDimitry Andric     DCHECK(
6210b57cec5SDimitry Andric         (Freelist != &SentinelSegment && Freelist->Prev == &SentinelSegment) ||
6220b57cec5SDimitry Andric         (Freelist == &SentinelSegment && Tail->Next == &SentinelSegment));
6230b57cec5SDimitry Andric   }
6240b57cec5SDimitry Andric 
6250b57cec5SDimitry Andric   // Provide iterators.
begin()6260b57cec5SDimitry Andric   Iterator<T> begin() const XRAY_NEVER_INSTRUMENT {
6270b57cec5SDimitry Andric     return Iterator<T>(Head, 0, Size);
6280b57cec5SDimitry Andric   }
end()6290b57cec5SDimitry Andric   Iterator<T> end() const XRAY_NEVER_INSTRUMENT {
6300b57cec5SDimitry Andric     return Iterator<T>(Tail, Size, Size);
6310b57cec5SDimitry Andric   }
cbegin()6320b57cec5SDimitry Andric   Iterator<const T> cbegin() const XRAY_NEVER_INSTRUMENT {
6330b57cec5SDimitry Andric     return Iterator<const T>(Head, 0, Size);
6340b57cec5SDimitry Andric   }
cend()6350b57cec5SDimitry Andric   Iterator<const T> cend() const XRAY_NEVER_INSTRUMENT {
6360b57cec5SDimitry Andric     return Iterator<const T>(Tail, Size, Size);
6370b57cec5SDimitry Andric   }
6380b57cec5SDimitry Andric };
6390b57cec5SDimitry Andric 
6400b57cec5SDimitry Andric // We need to have this storage definition out-of-line so that the compiler can
6410b57cec5SDimitry Andric // ensure that storage for the SentinelSegment is defined and has a single
6420b57cec5SDimitry Andric // address.
6430b57cec5SDimitry Andric template <class T>
6440b57cec5SDimitry Andric typename Array<T>::Segment Array<T>::SentinelSegment{
6450b57cec5SDimitry Andric     &Array<T>::SentinelSegment, &Array<T>::SentinelSegment, {'\0'}};
6460b57cec5SDimitry Andric 
6470b57cec5SDimitry Andric } // namespace __xray
6480b57cec5SDimitry Andric 
6490b57cec5SDimitry Andric #endif // XRAY_SEGMENTED_ARRAY_H
650