651 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			651 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- xray_segmented_array.h ---------------------------------*- C++ -*-===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// 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 *>(Base) + 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 {
 | 
						|
    // FIXME: This is a duplication of AppenEmplace with the copy semantics
 | 
						|
    // explicitly used, as a work-around to GCC 4.8 not invoking the copy
 | 
						|
    // constructor with the placement new with braced-init syntax.
 | 
						|
    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(E);
 | 
						|
    ++Size;
 | 
						|
    return reinterpret_cast<T *>(AlignedOffset);
 | 
						|
  }
 | 
						|
 | 
						|
  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
 |