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_