tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

vector_buffer.h (6874B)


      1 // Copyright 2017 The Chromium Authors
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef BASE_CONTAINERS_VECTOR_BUFFER_H_
      6 #define BASE_CONTAINERS_VECTOR_BUFFER_H_
      7 
      8 #include <stdlib.h>
      9 #include <string.h>
     10 
     11 #include <type_traits>
     12 #include <utility>
     13 
     14 #include "base/check.h"
     15 #include "base/check_op.h"
     16 #include "base/compiler_specific.h"
     17 #include "base/containers/util.h"
     18 #include "base/memory/raw_ptr_exclusion.h"
     19 #include "base/numerics/checked_math.h"
     20 
     21 namespace base::internal {
     22 
     23 // Internal implementation detail of base/containers.
     24 //
     25 // Implements a vector-like buffer that holds a certain capacity of T. Unlike
     26 // std::vector, VectorBuffer never constructs or destructs its arguments, and
     27 // can't change sizes. But it does implement templates to assist in efficient
     28 // moving and destruction of those items manually.
     29 //
     30 // In particular, the destructor function does not iterate over the items if
     31 // there is no destructor. Moves should be implemented as a memcpy/memmove for
     32 // trivially copyable objects (POD) otherwise, it should be a std::move if
     33 // possible, and as a last resort it falls back to a copy. This behavior is
     34 // similar to std::vector.
     35 //
     36 // No special consideration is done for noexcept move constructors since
     37 // we compile without exceptions.
     38 //
     39 // The current API does not support moving overlapping ranges.
     40 template <typename T>
     41 class VectorBuffer {
     42 public:
     43  constexpr VectorBuffer() = default;
     44 
     45 #if defined(__clang__) && !defined(__native_client__)
     46  // This constructor converts an uninitialized void* to a T* which triggers
     47  // clang Control Flow Integrity. Since this is as-designed, disable.
     48  __attribute__((no_sanitize("cfi-unrelated-cast", "vptr")))
     49 #endif
     50  VectorBuffer(size_t count)
     51      : buffer_(reinterpret_cast<T*>(
     52            malloc(CheckMul(sizeof(T), count).ValueOrDie()))),
     53        capacity_(count) {
     54  }
     55  VectorBuffer(VectorBuffer&& other) noexcept
     56      : buffer_(other.buffer_), capacity_(other.capacity_) {
     57    other.buffer_ = nullptr;
     58    other.capacity_ = 0;
     59  }
     60 
     61  VectorBuffer(const VectorBuffer&) = delete;
     62  VectorBuffer& operator=(const VectorBuffer&) = delete;
     63 
     64  ~VectorBuffer() { free(buffer_); }
     65 
     66  VectorBuffer& operator=(VectorBuffer&& other) {
     67    free(buffer_);
     68    buffer_ = other.buffer_;
     69    capacity_ = other.capacity_;
     70 
     71    other.buffer_ = nullptr;
     72    other.capacity_ = 0;
     73    return *this;
     74  }
     75 
     76  size_t capacity() const { return capacity_; }
     77 
     78  T& operator[](size_t i) {
     79    // TODO(crbug.com/817982): Some call sites (at least circular_deque.h) are
     80    // calling this with `i == capacity_` as a way of getting `end()`. Therefore
     81    // we have to allow this for now (`i <= capacity_`), until we fix those call
     82    // sites to use real iterators. This comment applies here and to `const T&
     83    // operator[]`, below.
     84    CHECK_LE(i, capacity_);
     85    return buffer_[i];
     86  }
     87 
     88  const T& operator[](size_t i) const {
     89    CHECK_LE(i, capacity_);
     90    return buffer_[i];
     91  }
     92 
     93  T* begin() { return buffer_; }
     94  T* end() { return &buffer_[capacity_]; }
     95 
     96  // DestructRange ------------------------------------------------------------
     97 
     98  // Trivially destructible objects need not have their destructors called.
     99  template <typename T2 = T,
    100            std::enable_if_t<std::is_trivially_destructible_v<T2>, int> = 0>
    101  void DestructRange(T* begin, T* end) {}
    102 
    103  // Non-trivially destructible objects must have their destructors called
    104  // individually.
    105  template <typename T2 = T,
    106            std::enable_if_t<!std::is_trivially_destructible_v<T2>, int> = 0>
    107  void DestructRange(T* begin, T* end) {
    108    CHECK_LE(begin, end);
    109    while (begin != end) {
    110      begin->~T();
    111      begin++;
    112    }
    113  }
    114 
    115  // MoveRange ----------------------------------------------------------------
    116  //
    117  // The destructor will be called (as necessary) for all moved types. The
    118  // ranges must not overlap.
    119  //
    120  // The parameters and begin and end (one past the last) of the input buffer,
    121  // and the address of the first element to copy to. There must be sufficient
    122  // room in the destination for all items in the range [begin, end).
    123 
    124  // Trivially copyable types can use memcpy. Trivially copyable implies
    125  // that there is a trivial destructor as we don't have to call it.
    126 
    127  // Trivially relocatable types can also use memcpy. Trivially relocatable
    128  // imples that memcpy is equivalent to move + destroy.
    129 
    130  template <typename T2>
    131  static inline constexpr bool is_trivially_copyable_or_relocatable =
    132      std::is_trivially_copyable_v<T2> || IS_TRIVIALLY_RELOCATABLE(T2);
    133 
    134  template <typename T2 = T,
    135            std::enable_if_t<is_trivially_copyable_or_relocatable<T2>, int> = 0>
    136  static void MoveRange(T* from_begin, T* from_end, T* to) {
    137    CHECK(!RangesOverlap(from_begin, from_end, to));
    138 
    139    memcpy(
    140        static_cast<void*>(to), from_begin,
    141        CheckSub(get_uintptr(from_end), get_uintptr(from_begin)).ValueOrDie());
    142  }
    143 
    144  // Not trivially copyable, but movable: call the move constructor and
    145  // destruct the original.
    146  template <typename T2 = T,
    147            std::enable_if_t<std::is_move_constructible_v<T2> &&
    148                                 !is_trivially_copyable_or_relocatable<T2>,
    149                             int> = 0>
    150  static void MoveRange(T* from_begin, T* from_end, T* to) {
    151    CHECK(!RangesOverlap(from_begin, from_end, to));
    152    while (from_begin != from_end) {
    153      new (to) T(std::move(*from_begin));
    154      from_begin->~T();
    155      from_begin++;
    156      to++;
    157    }
    158  }
    159 
    160  // Not movable, not trivially copyable: call the copy constructor and
    161  // destruct the original.
    162  template <typename T2 = T,
    163            std::enable_if_t<!std::is_move_constructible_v<T2> &&
    164                                 !is_trivially_copyable_or_relocatable<T2>,
    165                             int> = 0>
    166  static void MoveRange(T* from_begin, T* from_end, T* to) {
    167    CHECK(!RangesOverlap(from_begin, from_end, to));
    168    while (from_begin != from_end) {
    169      new (to) T(*from_begin);
    170      from_begin->~T();
    171      from_begin++;
    172      to++;
    173    }
    174  }
    175 
    176 private:
    177  static bool RangesOverlap(const T* from_begin,
    178                            const T* from_end,
    179                            const T* to) {
    180    const auto from_begin_uintptr = get_uintptr(from_begin);
    181    const auto from_end_uintptr = get_uintptr(from_end);
    182    const auto to_uintptr = get_uintptr(to);
    183    return !(
    184        to >= from_end ||
    185        CheckAdd(to_uintptr, CheckSub(from_end_uintptr, from_begin_uintptr))
    186                .ValueOrDie() <= from_begin_uintptr);
    187  }
    188 
    189  // `buffer_` is not a raw_ptr<...> for performance reasons (based on analysis
    190  // of sampling profiler data and tab_search:top100:2020).
    191  RAW_PTR_EXCLUSION T* buffer_ = nullptr;
    192  size_t capacity_ = 0;
    193 };
    194 
    195 }  // namespace base::internal
    196 
    197 #endif  // BASE_CONTAINERS_VECTOR_BUFFER_H_