| Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2017 The Chromium Authors |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| Tom Sepez | 8726d30e | 2025-01-29 02:11:08 | [diff] [blame] | 5 | #ifdef UNSAFE_BUFFERS_BUILD |
| 6 | // TODO(crbug.com/390223051): Remove C-library calls to fix the errors. |
| 7 | #pragma allow_unsafe_libc_calls |
| 8 | #endif |
| 9 | |
| Lei Zhang | 6825df46 | 2020-06-23 16:17:43 | [diff] [blame] | 10 | #ifndef BASE_CONTAINERS_VECTOR_BUFFER_H_ |
| 11 | #define BASE_CONTAINERS_VECTOR_BUFFER_H_ |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 12 | |
| 13 | #include <stdlib.h> |
| 14 | #include <string.h> |
| 15 | |
| 16 | #include <type_traits> |
| 17 | #include <utility> |
| 18 | |
| David Sanders | 8cfb63a | 2022-04-14 19:36:30 | [diff] [blame] | 19 | #include "base/check.h" |
| Hans Wennborg | 7b53371 | 2020-06-22 20:52:27 | [diff] [blame] | 20 | #include "base/check_op.h" |
| Adam Rice | fb288d0 | 2023-10-13 08:36:21 | [diff] [blame] | 21 | #include "base/compiler_specific.h" |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 22 | #include "base/containers/span.h" |
| Keishi Hattori | 488b760 | 2022-05-02 13:09:31 | [diff] [blame] | 23 | #include "base/memory/raw_ptr_exclusion.h" |
| Chris Palmer | b586d770 | 2018-08-15 03:57:37 | [diff] [blame] | 24 | #include "base/numerics/checked_math.h" |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 25 | |
| Adam Rice | fb288d0 | 2023-10-13 08:36:21 | [diff] [blame] | 26 | namespace base::internal { |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 27 | |
| 28 | // Internal implementation detail of base/containers. |
| 29 | // |
| 30 | // Implements a vector-like buffer that holds a certain capacity of T. Unlike |
| 31 | // std::vector, VectorBuffer never constructs or destructs its arguments, and |
| 32 | // can't change sizes. But it does implement templates to assist in efficient |
| 33 | // moving and destruction of those items manually. |
| 34 | // |
| 35 | // In particular, the destructor function does not iterate over the items if |
| 36 | // there is no destructor. Moves should be implemented as a memcpy/memmove for |
| 37 | // trivially copyable objects (POD) otherwise, it should be a std::move if |
| 38 | // possible, and as a last resort it falls back to a copy. This behavior is |
| 39 | // similar to std::vector. |
| 40 | // |
| 41 | // No special consideration is done for noexcept move constructors since |
| 42 | // we compile without exceptions. |
| 43 | // |
| 44 | // The current API does not support moving overlapping ranges. |
| 45 | template <typename T> |
| 46 | class VectorBuffer { |
| 47 | public: |
| Gabriel Charette | 63fe706 | 2018-01-25 14:14:29 | [diff] [blame] | 48 | constexpr VectorBuffer() = default; |
| Brett Wilson | cff2c65 | 2017-08-22 23:46:40 | [diff] [blame] | 49 | |
| Nico Weber | e98208d7 | 2025-07-10 15:18:20 | [diff] [blame] | 50 | #if defined(__clang__) |
| Brett Wilson | cff2c65 | 2017-08-22 23:46:40 | [diff] [blame] | 51 | // This constructor converts an uninitialized void* to a T* which triggers |
| 52 | // clang Control Flow Integrity. Since this is as-designed, disable. |
| 53 | __attribute__((no_sanitize("cfi-unrelated-cast", "vptr"))) |
| 54 | #endif |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 55 | VectorBuffer(size_t count) |
| Chris Palmer | b586d770 | 2018-08-15 03:57:37 | [diff] [blame] | 56 | : buffer_(reinterpret_cast<T*>( |
| 57 | malloc(CheckMul(sizeof(T), count).ValueOrDie()))), |
| Brett Wilson | cff2c65 | 2017-08-22 23:46:40 | [diff] [blame] | 58 | capacity_(count) { |
| 59 | } |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 60 | VectorBuffer(VectorBuffer&& other) noexcept |
| 61 | : buffer_(other.buffer_), capacity_(other.capacity_) { |
| 62 | other.buffer_ = nullptr; |
| 63 | other.capacity_ = 0; |
| 64 | } |
| 65 | |
| Lei Zhang | 6825df46 | 2020-06-23 16:17:43 | [diff] [blame] | 66 | VectorBuffer(const VectorBuffer&) = delete; |
| 67 | VectorBuffer& operator=(const VectorBuffer&) = delete; |
| 68 | |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 69 | ~VectorBuffer() { free(buffer_); } |
| 70 | |
| 71 | VectorBuffer& operator=(VectorBuffer&& other) { |
| 72 | free(buffer_); |
| 73 | buffer_ = other.buffer_; |
| 74 | capacity_ = other.capacity_; |
| 75 | |
| 76 | other.buffer_ = nullptr; |
| 77 | other.capacity_ = 0; |
| 78 | return *this; |
| 79 | } |
| 80 | |
| 81 | size_t capacity() const { return capacity_; } |
| 82 | |
| Chris Palmer | b586d770 | 2018-08-15 03:57:37 | [diff] [blame] | 83 | T& operator[](size_t i) { |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 84 | CHECK_LT(i, capacity_); |
| 85 | // SAFETY: `capacity_` is the size of the array pointed to by `buffer_`, |
| 86 | // which `i` is less than, so the dereference is inside the allocation. |
| 87 | return UNSAFE_BUFFERS(buffer_[i]); |
| Chris Palmer | b586d770 | 2018-08-15 03:57:37 | [diff] [blame] | 88 | } |
| 89 | |
| 90 | const T& operator[](size_t i) const { |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 91 | CHECK_LT(i, capacity_); |
| 92 | // SAFETY: `capacity_` is the size of the array pointed to by `buffer_`, |
| 93 | // which `i` is less than, so the dereference is inside the allocation. |
| 94 | return UNSAFE_BUFFERS(buffer_[i]); |
| Chris Palmer | b586d770 | 2018-08-15 03:57:37 | [diff] [blame] | 95 | } |
| 96 | |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 97 | const T* data() const { return buffer_; } |
| 98 | T* data() { return buffer_; } |
| 99 | |
| Daniel Cheng | c882376 | 2019-08-20 00:14:09 | [diff] [blame] | 100 | T* begin() { return buffer_; } |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 101 | T* end() { |
| 102 | // SAFETY: `capacity_` is the size of the array pointed to by `buffer_`. |
| 103 | return UNSAFE_BUFFERS(buffer_ + capacity_); |
| 104 | } |
| 105 | |
| danakj | d5b80ea | 2024-07-11 22:40:43 | [diff] [blame] | 106 | span<T> as_span() { |
| 107 | // SAFETY: The `buffer_` array's size is `capacity_` so this gives the |
| 108 | // pointer to the start and one-past-the-end of the `buffer_`. |
| 109 | return UNSAFE_BUFFERS(span(buffer_, buffer_ + capacity_)); |
| 110 | } |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 111 | |
| danakj | d5b80ea | 2024-07-11 22:40:43 | [diff] [blame] | 112 | span<T> subspan(size_t index) { return as_span().subspan(index); } |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 113 | |
| 114 | span<T> subspan(size_t index, size_t size) { |
| danakj | d5b80ea | 2024-07-11 22:40:43 | [diff] [blame] | 115 | return as_span().subspan(index, size); |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 116 | } |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 117 | |
| danakj | 26ede22 | 2024-11-06 21:36:34 | [diff] [blame] | 118 | T* get_at(size_t index) { return as_span().get_at(index); } |
| 119 | |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 120 | // DestructRange ------------------------------------------------------------ |
| 121 | |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 122 | static void DestructRange(span<T> range) { |
| Daniel Cheng | bd6da000 | 2023-11-14 22:58:56 | [diff] [blame] | 123 | // Trivially destructible objects need not have their destructors called. |
| 124 | if constexpr (!std::is_trivially_destructible_v<T>) { |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 125 | for (T& t : range) { |
| 126 | t.~T(); |
| Daniel Cheng | bd6da000 | 2023-11-14 22:58:56 | [diff] [blame] | 127 | } |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 128 | } |
| 129 | } |
| 130 | |
| 131 | // MoveRange ---------------------------------------------------------------- |
| Adam Rice | fb288d0 | 2023-10-13 08:36:21 | [diff] [blame] | 132 | |
| 133 | template <typename T2> |
| 134 | static inline constexpr bool is_trivially_copyable_or_relocatable = |
| 135 | std::is_trivially_copyable_v<T2> || IS_TRIVIALLY_RELOCATABLE(T2); |
| 136 | |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 137 | // Moves or copies elements from the `from` span to the `to` span. Uses memcpy |
| 138 | // when possible. The memory in `to` must be uninitialized and the ranges must |
| 139 | // not overlap. |
| 140 | // |
| 141 | // The objects in `from` are destroyed afterward. |
| 142 | static void MoveConstructRange(span<T> from, span<T> to) { |
| 143 | CHECK(!RangesOverlap(from, to)); |
| 144 | CHECK_EQ(from.size(), to.size()); |
| Jan Wilken Dörrie | d6e18a4 | 2021-02-17 21:25:10 | [diff] [blame] | 145 | |
| Daniel Cheng | bd6da000 | 2023-11-14 22:58:56 | [diff] [blame] | 146 | if constexpr (is_trivially_copyable_or_relocatable<T>) { |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 147 | // We can't use span::copy_from() as it tries to call copy constructors, |
| 148 | // and fails to compile on move-only trivially-relocatable types. |
| Adrian Ratiu | e8fd45e9 | 2025-07-19 13:03:24 | [diff] [blame] | 149 | // TODO(https://crbug.com/432507886): find a way to remove the void* cast |
| 150 | memcpy(static_cast<void*>(to.data()), from.data(), to.size_bytes()); |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 151 | // Destructors are skipped because they are trivial or should be elided in |
| 152 | // trivial relocation (https://reviews.llvm.org/D114732). |
| Daniel Cheng | bd6da000 | 2023-11-14 22:58:56 | [diff] [blame] | 153 | } else { |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 154 | for (size_t i = 0; i < from.size(); ++i) { |
| 155 | T* to_pointer = to.subspan(i).data(); |
| Daniel Cheng | bd6da000 | 2023-11-14 22:58:56 | [diff] [blame] | 156 | if constexpr (std::move_constructible<T>) { |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 157 | new (to_pointer) T(std::move(from[i])); |
| Daniel Cheng | bd6da000 | 2023-11-14 22:58:56 | [diff] [blame] | 158 | } else { |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 159 | new (to_pointer) T(from[i]); |
| Daniel Cheng | bd6da000 | 2023-11-14 22:58:56 | [diff] [blame] | 160 | } |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 161 | from[i].~T(); |
| Daniel Cheng | bd6da000 | 2023-11-14 22:58:56 | [diff] [blame] | 162 | } |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 163 | } |
| 164 | } |
| 165 | |
| 166 | private: |
| danakj | 91c715bb | 2024-07-10 13:24:26 | [diff] [blame] | 167 | static bool RangesOverlap(span<T> a, span<T> b) { |
| 168 | const auto a_start = reinterpret_cast<uintptr_t>(a.data()); |
| 169 | const auto a_end = reinterpret_cast<uintptr_t>(a.data()) + a.size(); |
| 170 | const auto b_start = reinterpret_cast<uintptr_t>(b.data()); |
| 171 | const auto b_end = reinterpret_cast<uintptr_t>(b.data()) + b.size(); |
| 172 | return a_end > b_start && a_start < b_end; |
| Daniel Cheng | c882376 | 2019-08-20 00:14:09 | [diff] [blame] | 173 | } |
| 174 | |
| Lukasz Anforowicz | 877e622 | 2021-11-26 00:03:23 | [diff] [blame] | 175 | // `buffer_` is not a raw_ptr<...> for performance reasons (based on analysis |
| 176 | // of sampling profiler data and tab_search:top100:2020). |
| Keishi Hattori | 488b760 | 2022-05-02 13:09:31 | [diff] [blame] | 177 | RAW_PTR_EXCLUSION T* buffer_ = nullptr; |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 178 | size_t capacity_ = 0; |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 179 | }; |
| 180 | |
| Adam Rice | fb288d0 | 2023-10-13 08:36:21 | [diff] [blame] | 181 | } // namespace base::internal |
| Brett Wilson | 322a546 | 2017-08-03 23:59:12 | [diff] [blame] | 182 | |
| Lei Zhang | 6825df46 | 2020-06-23 16:17:43 | [diff] [blame] | 183 | #endif // BASE_CONTAINERS_VECTOR_BUFFER_H_ |