forked from OSchip/llvm-project
622 lines
20 KiB
C++
622 lines
20 KiB
C++
//===-- xray_segmented_array.h ---------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file is a part of XRay, a dynamic runtime instrumentation system.
|
|
//
|
|
// Defines the implementation of a segmented array, with fixed-size segments
|
|
// backing the segments.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#ifndef XRAY_SEGMENTED_ARRAY_H
|
|
#define XRAY_SEGMENTED_ARRAY_H
|
|
|
|
#include "sanitizer_common/sanitizer_allocator.h"
|
|
#include "xray_allocator.h"
|
|
#include "xray_utils.h"
|
|
#include <cassert>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
namespace __xray {
|
|
|
|
/// The Array type provides an interface similar to std::vector<...> but does
|
|
/// not shrink in size. Once constructed, elements can be appended but cannot be
|
|
/// removed. The implementation is heavily dependent on the contract provided by
|
|
/// the Allocator type, in that all memory will be released when the Allocator
|
|
/// is destroyed. When an Array is destroyed, it will destroy elements in the
|
|
/// backing store but will not free the memory.
|
|
template <class T> class Array {
|
|
struct Segment {
|
|
Segment *Prev;
|
|
Segment *Next;
|
|
char Data[1];
|
|
};
|
|
|
|
public:
|
|
// Each segment of the array will be laid out with the following assumptions:
|
|
//
|
|
// - Each segment will be on a cache-line address boundary (kCacheLineSize
|
|
// aligned).
|
|
//
|
|
// - The elements will be accessed through an aligned pointer, dependent on
|
|
// the alignment of T.
|
|
//
|
|
// - Each element is at least two-pointers worth from the beginning of the
|
|
// Segment, aligned properly, and the rest of the elements are accessed
|
|
// through appropriate alignment.
|
|
//
|
|
// We then compute the size of the segment to follow this logic:
|
|
//
|
|
// - Compute the number of elements that can fit within
|
|
// kCacheLineSize-multiple segments, minus the size of two pointers.
|
|
//
|
|
// - Request cacheline-multiple sized elements from the allocator.
|
|
static constexpr uint64_t AlignedElementStorageSize =
|
|
sizeof(typename std::aligned_storage<sizeof(T), alignof(T)>::type);
|
|
|
|
static constexpr uint64_t SegmentControlBlockSize = sizeof(Segment *) * 2;
|
|
|
|
static constexpr uint64_t SegmentSize = nearest_boundary(
|
|
SegmentControlBlockSize + next_pow2(sizeof(T)), kCacheLineSize);
|
|
|
|
using AllocatorType = Allocator<SegmentSize>;
|
|
|
|
static constexpr uint64_t ElementsPerSegment =
|
|
(SegmentSize - SegmentControlBlockSize) / next_pow2(sizeof(T));
|
|
|
|
static_assert(ElementsPerSegment > 0,
|
|
"Must have at least 1 element per segment.");
|
|
|
|
static Segment SentinelSegment;
|
|
|
|
using size_type = uint64_t;
|
|
|
|
private:
|
|
// This Iterator models a BidirectionalIterator.
|
|
template <class U> class Iterator {
|
|
Segment *S = &SentinelSegment;
|
|
uint64_t Offset = 0;
|
|
uint64_t Size = 0;
|
|
|
|
public:
|
|
Iterator(Segment *IS, uint64_t Off, uint64_t S) XRAY_NEVER_INSTRUMENT
|
|
: S(IS),
|
|
Offset(Off),
|
|
Size(S) {}
|
|
Iterator(const Iterator &) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
|
|
Iterator() NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
|
|
Iterator(Iterator &&) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
|
|
Iterator &operator=(const Iterator &) XRAY_NEVER_INSTRUMENT = default;
|
|
Iterator &operator=(Iterator &&) XRAY_NEVER_INSTRUMENT = default;
|
|
~Iterator() XRAY_NEVER_INSTRUMENT = default;
|
|
|
|
Iterator &operator++() XRAY_NEVER_INSTRUMENT {
|
|
if (++Offset % ElementsPerSegment || Offset == Size)
|
|
return *this;
|
|
|
|
// At this point, we know that Offset % N == 0, so we must advance the
|
|
// segment pointer.
|
|
DCHECK_EQ(Offset % ElementsPerSegment, 0);
|
|
DCHECK_NE(Offset, Size);
|
|
DCHECK_NE(S, &SentinelSegment);
|
|
DCHECK_NE(S->Next, &SentinelSegment);
|
|
S = S->Next;
|
|
DCHECK_NE(S, &SentinelSegment);
|
|
return *this;
|
|
}
|
|
|
|
Iterator &operator--() XRAY_NEVER_INSTRUMENT {
|
|
DCHECK_NE(S, &SentinelSegment);
|
|
DCHECK_GT(Offset, 0);
|
|
|
|
auto PreviousOffset = Offset--;
|
|
if (PreviousOffset != Size && PreviousOffset % ElementsPerSegment == 0) {
|
|
DCHECK_NE(S->Prev, &SentinelSegment);
|
|
S = S->Prev;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
Iterator operator++(int) XRAY_NEVER_INSTRUMENT {
|
|
Iterator Copy(*this);
|
|
++(*this);
|
|
return Copy;
|
|
}
|
|
|
|
Iterator operator--(int) XRAY_NEVER_INSTRUMENT {
|
|
Iterator Copy(*this);
|
|
--(*this);
|
|
return Copy;
|
|
}
|
|
|
|
template <class V, class W>
|
|
friend bool operator==(const Iterator<V> &L,
|
|
const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
|
|
return L.S == R.S && L.Offset == R.Offset;
|
|
}
|
|
|
|
template <class V, class W>
|
|
friend bool operator!=(const Iterator<V> &L,
|
|
const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
|
|
return !(L == R);
|
|
}
|
|
|
|
U &operator*() const XRAY_NEVER_INSTRUMENT {
|
|
DCHECK_NE(S, &SentinelSegment);
|
|
auto RelOff = Offset % ElementsPerSegment;
|
|
|
|
// We need to compute the character-aligned pointer, offset from the
|
|
// segment's Data location to get the element in the position of Offset.
|
|
auto Base = &S->Data;
|
|
auto AlignedOffset = Base + (RelOff * AlignedElementStorageSize);
|
|
return *reinterpret_cast<U *>(AlignedOffset);
|
|
}
|
|
|
|
U *operator->() const XRAY_NEVER_INSTRUMENT { return &(**this); }
|
|
};
|
|
|
|
AllocatorType *Alloc;
|
|
Segment *Head;
|
|
Segment *Tail;
|
|
|
|
// Here we keep track of segments in the freelist, to allow us to re-use
|
|
// segments when elements are trimmed off the end.
|
|
Segment *Freelist;
|
|
uint64_t Size;
|
|
|
|
// ===============================
|
|
// In the following implementation, we work through the algorithms and the
|
|
// list operations using the following notation:
|
|
//
|
|
// - pred(s) is the predecessor (previous node accessor) and succ(s) is
|
|
// the successor (next node accessor).
|
|
//
|
|
// - S is a sentinel segment, which has the following property:
|
|
//
|
|
// pred(S) == succ(S) == S
|
|
//
|
|
// - @ is a loop operator, which can imply pred(s) == s if it appears on
|
|
// the left of s, or succ(s) == S if it appears on the right of s.
|
|
//
|
|
// - sL <-> sR : means a bidirectional relation between sL and sR, which
|
|
// means:
|
|
//
|
|
// succ(sL) == sR && pred(SR) == sL
|
|
//
|
|
// - sL -> sR : implies a unidirectional relation between sL and SR,
|
|
// with the following properties:
|
|
//
|
|
// succ(sL) == sR
|
|
//
|
|
// sL <- sR : implies a unidirectional relation between sR and sL,
|
|
// with the following properties:
|
|
//
|
|
// pred(sR) == sL
|
|
//
|
|
// ===============================
|
|
|
|
Segment *NewSegment() XRAY_NEVER_INSTRUMENT {
|
|
// We need to handle the case in which enough elements have been trimmed to
|
|
// allow us to re-use segments we've allocated before. For this we look into
|
|
// the Freelist, to see whether we need to actually allocate new blocks or
|
|
// just re-use blocks we've already seen before.
|
|
if (Freelist != &SentinelSegment) {
|
|
// The current state of lists resemble something like this at this point:
|
|
//
|
|
// Freelist: @S@<-f0->...<->fN->@S@
|
|
// ^ Freelist
|
|
//
|
|
// We want to perform a splice of `f0` from Freelist to a temporary list,
|
|
// which looks like:
|
|
//
|
|
// Templist: @S@<-f0->@S@
|
|
// ^ FreeSegment
|
|
//
|
|
// Our algorithm preconditions are:
|
|
DCHECK_EQ(Freelist->Prev, &SentinelSegment);
|
|
|
|
// Then the algorithm we implement is:
|
|
//
|
|
// SFS = Freelist
|
|
// Freelist = succ(Freelist)
|
|
// if (Freelist != S)
|
|
// pred(Freelist) = S
|
|
// succ(SFS) = S
|
|
// pred(SFS) = S
|
|
//
|
|
auto *FreeSegment = Freelist;
|
|
Freelist = Freelist->Next;
|
|
|
|
// Note that we need to handle the case where Freelist is now pointing to
|
|
// S, which we don't want to be overwriting.
|
|
// TODO: Determine whether the cost of the branch is higher than the cost
|
|
// of the blind assignment.
|
|
if (Freelist != &SentinelSegment)
|
|
Freelist->Prev = &SentinelSegment;
|
|
|
|
FreeSegment->Next = &SentinelSegment;
|
|
FreeSegment->Prev = &SentinelSegment;
|
|
|
|
// Our postconditions are:
|
|
DCHECK_EQ(Freelist->Prev, &SentinelSegment);
|
|
DCHECK_NE(FreeSegment, &SentinelSegment);
|
|
return FreeSegment;
|
|
}
|
|
|
|
auto SegmentBlock = Alloc->Allocate();
|
|
if (SegmentBlock.Data == nullptr)
|
|
return nullptr;
|
|
|
|
// Placement-new the Segment element at the beginning of the SegmentBlock.
|
|
new (SegmentBlock.Data) Segment{&SentinelSegment, &SentinelSegment, {0}};
|
|
auto SB = reinterpret_cast<Segment *>(SegmentBlock.Data);
|
|
return SB;
|
|
}
|
|
|
|
Segment *InitHeadAndTail() XRAY_NEVER_INSTRUMENT {
|
|
DCHECK_EQ(Head, &SentinelSegment);
|
|
DCHECK_EQ(Tail, &SentinelSegment);
|
|
auto S = NewSegment();
|
|
if (S == nullptr)
|
|
return nullptr;
|
|
DCHECK_EQ(S->Next, &SentinelSegment);
|
|
DCHECK_EQ(S->Prev, &SentinelSegment);
|
|
DCHECK_NE(S, &SentinelSegment);
|
|
Head = S;
|
|
Tail = S;
|
|
DCHECK_EQ(Head, Tail);
|
|
DCHECK_EQ(Tail->Next, &SentinelSegment);
|
|
DCHECK_EQ(Tail->Prev, &SentinelSegment);
|
|
return S;
|
|
}
|
|
|
|
Segment *AppendNewSegment() XRAY_NEVER_INSTRUMENT {
|
|
auto S = NewSegment();
|
|
if (S == nullptr)
|
|
return nullptr;
|
|
DCHECK_NE(Tail, &SentinelSegment);
|
|
DCHECK_EQ(Tail->Next, &SentinelSegment);
|
|
DCHECK_EQ(S->Prev, &SentinelSegment);
|
|
DCHECK_EQ(S->Next, &SentinelSegment);
|
|
S->Prev = Tail;
|
|
Tail->Next = S;
|
|
Tail = S;
|
|
DCHECK_EQ(S, S->Prev->Next);
|
|
DCHECK_EQ(Tail->Next, &SentinelSegment);
|
|
return S;
|
|
}
|
|
|
|
public:
|
|
explicit Array(AllocatorType &A) XRAY_NEVER_INSTRUMENT
|
|
: Alloc(&A),
|
|
Head(&SentinelSegment),
|
|
Tail(&SentinelSegment),
|
|
Freelist(&SentinelSegment),
|
|
Size(0) {}
|
|
|
|
Array() XRAY_NEVER_INSTRUMENT : Alloc(nullptr),
|
|
Head(&SentinelSegment),
|
|
Tail(&SentinelSegment),
|
|
Freelist(&SentinelSegment),
|
|
Size(0) {}
|
|
|
|
Array(const Array &) = delete;
|
|
Array &operator=(const Array &) = delete;
|
|
|
|
Array(Array &&O) XRAY_NEVER_INSTRUMENT : Alloc(O.Alloc),
|
|
Head(O.Head),
|
|
Tail(O.Tail),
|
|
Freelist(O.Freelist),
|
|
Size(O.Size) {
|
|
O.Alloc = nullptr;
|
|
O.Head = &SentinelSegment;
|
|
O.Tail = &SentinelSegment;
|
|
O.Size = 0;
|
|
O.Freelist = &SentinelSegment;
|
|
}
|
|
|
|
Array &operator=(Array &&O) XRAY_NEVER_INSTRUMENT {
|
|
Alloc = O.Alloc;
|
|
O.Alloc = nullptr;
|
|
Head = O.Head;
|
|
O.Head = &SentinelSegment;
|
|
Tail = O.Tail;
|
|
O.Tail = &SentinelSegment;
|
|
Freelist = O.Freelist;
|
|
O.Freelist = &SentinelSegment;
|
|
Size = O.Size;
|
|
O.Size = 0;
|
|
return *this;
|
|
}
|
|
|
|
~Array() XRAY_NEVER_INSTRUMENT {
|
|
for (auto &E : *this)
|
|
(&E)->~T();
|
|
}
|
|
|
|
bool empty() const XRAY_NEVER_INSTRUMENT { return Size == 0; }
|
|
|
|
AllocatorType &allocator() const XRAY_NEVER_INSTRUMENT {
|
|
DCHECK_NE(Alloc, nullptr);
|
|
return *Alloc;
|
|
}
|
|
|
|
uint64_t size() const XRAY_NEVER_INSTRUMENT { return Size; }
|
|
|
|
template <class... Args>
|
|
T *AppendEmplace(Args &&... args) XRAY_NEVER_INSTRUMENT {
|
|
DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) ||
|
|
(Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
|
|
if (UNLIKELY(Head == &SentinelSegment)) {
|
|
auto R = InitHeadAndTail();
|
|
if (R == nullptr)
|
|
return nullptr;
|
|
}
|
|
|
|
DCHECK_NE(Head, &SentinelSegment);
|
|
DCHECK_NE(Tail, &SentinelSegment);
|
|
|
|
auto Offset = Size % ElementsPerSegment;
|
|
if (UNLIKELY(Size != 0 && Offset == 0))
|
|
if (AppendNewSegment() == nullptr)
|
|
return nullptr;
|
|
|
|
DCHECK_NE(Tail, &SentinelSegment);
|
|
auto Base = &Tail->Data;
|
|
auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
|
|
DCHECK_LE(AlignedOffset + sizeof(T),
|
|
reinterpret_cast<unsigned char *>(Tail) + SegmentSize);
|
|
|
|
// In-place construct at Position.
|
|
new (AlignedOffset) T{std::forward<Args>(args)...};
|
|
++Size;
|
|
return reinterpret_cast<T *>(AlignedOffset);
|
|
}
|
|
|
|
T *Append(const T &E) XRAY_NEVER_INSTRUMENT { return AppendEmplace(E); }
|
|
|
|
T &operator[](uint64_t Offset) const XRAY_NEVER_INSTRUMENT {
|
|
DCHECK_LE(Offset, Size);
|
|
// We need to traverse the array enough times to find the element at Offset.
|
|
auto S = Head;
|
|
while (Offset >= ElementsPerSegment) {
|
|
S = S->Next;
|
|
Offset -= ElementsPerSegment;
|
|
DCHECK_NE(S, &SentinelSegment);
|
|
}
|
|
auto Base = &S->Data;
|
|
auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
|
|
auto Position = reinterpret_cast<T *>(AlignedOffset);
|
|
return *reinterpret_cast<T *>(Position);
|
|
}
|
|
|
|
T &front() const XRAY_NEVER_INSTRUMENT {
|
|
DCHECK_NE(Head, &SentinelSegment);
|
|
DCHECK_NE(Size, 0u);
|
|
return *begin();
|
|
}
|
|
|
|
T &back() const XRAY_NEVER_INSTRUMENT {
|
|
DCHECK_NE(Tail, &SentinelSegment);
|
|
DCHECK_NE(Size, 0u);
|
|
auto It = end();
|
|
--It;
|
|
return *It;
|
|
}
|
|
|
|
template <class Predicate>
|
|
T *find_element(Predicate P) const XRAY_NEVER_INSTRUMENT {
|
|
if (empty())
|
|
return nullptr;
|
|
|
|
auto E = end();
|
|
for (auto I = begin(); I != E; ++I)
|
|
if (P(*I))
|
|
return &(*I);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// Remove N Elements from the end. This leaves the blocks behind, and not
|
|
/// require allocation of new blocks for new elements added after trimming.
|
|
void trim(uint64_t Elements) XRAY_NEVER_INSTRUMENT {
|
|
auto OldSize = Size;
|
|
Elements = Elements > Size ? Size : Elements;
|
|
Size -= Elements;
|
|
|
|
// We compute the number of segments we're going to return from the tail by
|
|
// counting how many elements have been trimmed. Given the following:
|
|
//
|
|
// - Each segment has N valid positions, where N > 0
|
|
// - The previous size > current size
|
|
//
|
|
// To compute the number of segments to return, we need to perform the
|
|
// following calculations for the number of segments required given 'x'
|
|
// elements:
|
|
//
|
|
// f(x) = {
|
|
// x == 0 : 0
|
|
// , 0 < x <= N : 1
|
|
// , N < x <= max : x / N + (x % N ? 1 : 0)
|
|
// }
|
|
//
|
|
// We can simplify this down to:
|
|
//
|
|
// f(x) = {
|
|
// x == 0 : 0,
|
|
// , 0 < x <= max : x / N + (x < N || x % N ? 1 : 0)
|
|
// }
|
|
//
|
|
// And further down to:
|
|
//
|
|
// f(x) = x ? x / N + (x < N || x % N ? 1 : 0) : 0
|
|
//
|
|
// We can then perform the following calculation `s` which counts the number
|
|
// of segments we need to remove from the end of the data structure:
|
|
//
|
|
// s(p, c) = f(p) - f(c)
|
|
//
|
|
// If we treat p = previous size, and c = current size, and given the
|
|
// properties above, the possible range for s(...) is [0..max(typeof(p))/N]
|
|
// given that typeof(p) == typeof(c).
|
|
auto F = [](uint64_t X) {
|
|
return X ? (X / ElementsPerSegment) +
|
|
(X < ElementsPerSegment || X % ElementsPerSegment ? 1 : 0)
|
|
: 0;
|
|
};
|
|
auto PS = F(OldSize);
|
|
auto CS = F(Size);
|
|
DCHECK_GE(PS, CS);
|
|
auto SegmentsToTrim = PS - CS;
|
|
for (auto I = 0uL; I < SegmentsToTrim; ++I) {
|
|
// Here we place the current tail segment to the freelist. To do this
|
|
// appropriately, we need to perform a splice operation on two
|
|
// bidirectional linked-lists. In particular, we have the current state of
|
|
// the doubly-linked list of segments:
|
|
//
|
|
// @S@ <- s0 <-> s1 <-> ... <-> sT -> @S@
|
|
//
|
|
DCHECK_NE(Head, &SentinelSegment);
|
|
DCHECK_NE(Tail, &SentinelSegment);
|
|
DCHECK_EQ(Tail->Next, &SentinelSegment);
|
|
|
|
if (Freelist == &SentinelSegment) {
|
|
// Our two lists at this point are in this configuration:
|
|
//
|
|
// Freelist: (potentially) @S@
|
|
// Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@
|
|
// ^ Head ^ Tail
|
|
//
|
|
// The end state for us will be this configuration:
|
|
//
|
|
// Freelist: @S@<-sT->@S@
|
|
// Mainlist: @S@<-s0<->s1<->...<->sPT->@S@
|
|
// ^ Head ^ Tail
|
|
//
|
|
// The first step for us is to hold a reference to the tail of Mainlist,
|
|
// which in our notation is represented by sT. We call this our "free
|
|
// segment" which is the segment we are placing on the Freelist.
|
|
//
|
|
// sF = sT
|
|
//
|
|
// Then, we also hold a reference to the "pre-tail" element, which we
|
|
// call sPT:
|
|
//
|
|
// sPT = pred(sT)
|
|
//
|
|
// We want to splice sT into the beginning of the Freelist, which in
|
|
// an empty Freelist means placing a segment whose predecessor and
|
|
// successor is the sentinel segment.
|
|
//
|
|
// The splice operation then can be performed in the following
|
|
// algorithm:
|
|
//
|
|
// succ(sPT) = S
|
|
// pred(sT) = S
|
|
// succ(sT) = Freelist
|
|
// Freelist = sT
|
|
// Tail = sPT
|
|
//
|
|
auto SPT = Tail->Prev;
|
|
SPT->Next = &SentinelSegment;
|
|
Tail->Prev = &SentinelSegment;
|
|
Tail->Next = Freelist;
|
|
Freelist = Tail;
|
|
Tail = SPT;
|
|
|
|
// Our post-conditions here are:
|
|
DCHECK_EQ(Tail->Next, &SentinelSegment);
|
|
DCHECK_EQ(Freelist->Prev, &SentinelSegment);
|
|
} else {
|
|
// In the other case, where the Freelist is not empty, we perform the
|
|
// following transformation instead:
|
|
//
|
|
// This transforms the current state:
|
|
//
|
|
// Freelist: @S@<-f0->@S@
|
|
// ^ Freelist
|
|
// Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@
|
|
// ^ Head ^ Tail
|
|
//
|
|
// Into the following:
|
|
//
|
|
// Freelist: @S@<-sT<->f0->@S@
|
|
// ^ Freelist
|
|
// Mainlist: @S@<-s0<->s1<->...<->sPT->@S@
|
|
// ^ Head ^ Tail
|
|
//
|
|
// The algorithm is:
|
|
//
|
|
// sFH = Freelist
|
|
// sPT = pred(sT)
|
|
// pred(SFH) = sT
|
|
// succ(sT) = Freelist
|
|
// pred(sT) = S
|
|
// succ(sPT) = S
|
|
// Tail = sPT
|
|
// Freelist = sT
|
|
//
|
|
auto SFH = Freelist;
|
|
auto SPT = Tail->Prev;
|
|
auto ST = Tail;
|
|
SFH->Prev = ST;
|
|
ST->Next = Freelist;
|
|
ST->Prev = &SentinelSegment;
|
|
SPT->Next = &SentinelSegment;
|
|
Tail = SPT;
|
|
Freelist = ST;
|
|
|
|
// Our post-conditions here are:
|
|
DCHECK_EQ(Tail->Next, &SentinelSegment);
|
|
DCHECK_EQ(Freelist->Prev, &SentinelSegment);
|
|
DCHECK_EQ(Freelist->Next->Prev, Freelist);
|
|
}
|
|
}
|
|
|
|
// Now in case we've spliced all the segments in the end, we ensure that the
|
|
// main list is "empty", or both the head and tail pointing to the sentinel
|
|
// segment.
|
|
if (Tail == &SentinelSegment)
|
|
Head = Tail;
|
|
|
|
DCHECK(
|
|
(Size == 0 && Head == &SentinelSegment && Tail == &SentinelSegment) ||
|
|
(Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
|
|
DCHECK(
|
|
(Freelist != &SentinelSegment && Freelist->Prev == &SentinelSegment) ||
|
|
(Freelist == &SentinelSegment && Tail->Next == &SentinelSegment));
|
|
}
|
|
|
|
// Provide iterators.
|
|
Iterator<T> begin() const XRAY_NEVER_INSTRUMENT {
|
|
return Iterator<T>(Head, 0, Size);
|
|
}
|
|
Iterator<T> end() const XRAY_NEVER_INSTRUMENT {
|
|
return Iterator<T>(Tail, Size, Size);
|
|
}
|
|
Iterator<const T> cbegin() const XRAY_NEVER_INSTRUMENT {
|
|
return Iterator<const T>(Head, 0, Size);
|
|
}
|
|
Iterator<const T> cend() const XRAY_NEVER_INSTRUMENT {
|
|
return Iterator<const T>(Tail, Size, Size);
|
|
}
|
|
};
|
|
|
|
// We need to have this storage definition out-of-line so that the compiler can
|
|
// ensure that storage for the SentinelSegment is defined and has a single
|
|
// address.
|
|
template <class T>
|
|
typename Array<T>::Segment Array<T>::SentinelSegment{
|
|
&Array<T>::SentinelSegment, &Array<T>::SentinelSegment, {'\0'}};
|
|
|
|
} // namespace __xray
|
|
|
|
#endif // XRAY_SEGMENTED_ARRAY_H
|