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