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