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