1 // Copyright 2024 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_HEAP_ARRAY_H_ 6 #define BASE_CONTAINERS_HEAP_ARRAY_H_ 7 8 #include <stddef.h> 9 10 #include <memory> 11 #include <type_traits> 12 #include <utility> 13 14 #include "base/compiler_specific.h" 15 #include "base/containers/span.h" 16 17 namespace base { 18 19 // HeapArray<T> is a replacement for std::unique_ptr<T[]> that keeps track 20 // of its size. It is intended to provide easy conversion to span<T> for most 21 // usage, but it also provides bounds-checked indexing. 22 // 23 // By default, elements in the array are either value-initialized (i.e. zeroed 24 // for primitive types) when the array is created using the WithSize() 25 // static method, or uninitialized when the array is created via the Uninit() 26 // static method. 27 template <typename T, typename Deleter = void> 28 class TRIVIAL_ABI GSL_OWNER HeapArray { 29 public: 30 static_assert(!std::is_const_v<T>, "HeapArray cannot hold const types"); 31 static_assert(!std::is_reference_v<T>, 32 "HeapArray cannot hold reference types"); 33 34 using iterator = base::span<T>::iterator; 35 using const_iterator = base::span<const T>::iterator; 36 // We don't put this default value in the template parameter list to allow the 37 // static_assert on is_reference_v to give a nicer error message. 38 using deleter_type = std:: 39 conditional_t<std::is_void_v<Deleter>, std::default_delete<T[]>, Deleter>; 40 41 // Allocates initialized memory capable of holding `size` elements. No memory 42 // is allocated for zero-sized arrays. WithSize(size_t size)43 static HeapArray WithSize(size_t size) 44 requires(std::constructible_from<T>) 45 { 46 if (!size) { 47 return HeapArray(); 48 } 49 return HeapArray(std::unique_ptr<T[], deleter_type>(new T[size]()), size); 50 } 51 52 // Allocates uninitialized memory capable of holding `size` elements. T must 53 // be trivially constructible and destructible. No memory is allocated for 54 // zero-sized arrays. Uninit(size_t size)55 static HeapArray Uninit(size_t size) 56 requires(std::is_trivially_constructible_v<T> && 57 std::is_trivially_destructible_v<T>) 58 { 59 if (!size) { 60 return HeapArray(); 61 } 62 return HeapArray(std::unique_ptr<T[], deleter_type>(new T[size]), size); 63 } 64 CopiedFrom(base::span<const T> that)65 static HeapArray CopiedFrom(base::span<const T> that) { 66 auto result = HeapArray::Uninit(that.size()); 67 result.copy_from(that); 68 return result; 69 } 70 71 // Constructs a HeapArray from an existing pointer, taking ownership of the 72 // pointer. 73 // 74 // # Safety 75 // The pointer must be correctly aligned for type `T` and able to be deleted 76 // through the `deleter_type`, which defaults to the `delete[]` operation. The 77 // `ptr` must point to an array of at least `size` many elements. If these are 78 // not met, then Undefined Behaviour can result. 79 // 80 // # Checks 81 // When the `size` is zero, the `ptr` must be null. FromOwningPointer(T * ptr,size_t size)82 UNSAFE_BUFFER_USAGE static HeapArray FromOwningPointer(T* ptr, size_t size) { 83 if (!size) { 84 CHECK_EQ(ptr, nullptr); 85 return HeapArray(); 86 } 87 return HeapArray(std::unique_ptr<T[], deleter_type>(ptr), size); 88 } 89 90 // Constructs an empty array and does not allocate any memory. 91 HeapArray() 92 requires(std::constructible_from<T>) 93 = default; 94 95 // Move-only type since the memory is owned. 96 HeapArray(const HeapArray&) = delete; 97 HeapArray& operator=(const HeapArray&) = delete; 98 99 // Move-construction leaves the moved-from object empty and containing 100 // no allocated memory. HeapArray(HeapArray && that)101 HeapArray(HeapArray&& that) 102 : data_(std::move(that.data_)), size_(std::exchange(that.size_, 0u)) {} 103 104 // Move-assigment leaves the moved-from object empty and containing 105 // no allocated memory. 106 HeapArray& operator=(HeapArray&& that) { 107 data_ = std::move(that.data_); 108 size_ = std::exchange(that.size_, 0u); 109 return *this; 110 } 111 ~HeapArray() = default; 112 empty()113 bool empty() const { return size_ == 0u; } size()114 size_t size() const { return size_; } 115 116 // Prefer span-based methods below over data() where possible. The data() 117 // method exists primarily to allow implicit constructions of spans. 118 // Returns nullptr for a zero-sized (or moved-from) array. data()119 T* data() LIFETIME_BOUND { return data_.get(); } data()120 const T* data() const LIFETIME_BOUND { return data_.get(); } 121 begin()122 iterator begin() LIFETIME_BOUND { return as_span().begin(); } begin()123 const_iterator begin() const LIFETIME_BOUND { return as_span().begin(); } 124 end()125 iterator end() LIFETIME_BOUND { return as_span().end(); } end()126 const_iterator end() const LIFETIME_BOUND { return as_span().end(); } 127 128 T& operator[](size_t idx) LIFETIME_BOUND { return as_span()[idx]; } 129 const T& operator[](size_t idx) const LIFETIME_BOUND { 130 return as_span()[idx]; 131 } 132 133 // Access the HeapArray via spans. Note that span<T> is implicilty 134 // constructible from HeapArray<T>, so an explicit call to .as_span() is 135 // most useful, say, when the compiler can't deduce a template 136 // argument type. as_span()137 base::span<T> as_span() LIFETIME_BOUND { 138 // SAFETY: `size_` is the number of elements in the `data_` allocation` at 139 // all times. 140 return UNSAFE_BUFFERS(base::span<T>(data_.get(), size_)); 141 } as_span()142 base::span<const T> as_span() const LIFETIME_BOUND { 143 // SAFETY: `size_` is the number of elements in the `data_` allocation` at 144 // all times. 145 return UNSAFE_BUFFERS(base::span<const T>(data_.get(), size_)); 146 } 147 148 // Convenience method to copy the contents of a span<> into the entire array. 149 // Hard CHECK occurs in span<>::copy_from() if the HeapArray and the span 150 // have different sizes. copy_from(base::span<const T> other)151 void copy_from(base::span<const T> other) { as_span().copy_from(other); } 152 153 // Convenience method to copy the contents of a span<> into the start of the 154 // array. Hard CHECK occurs in span<>::copy_prefix_from() if the HeapArray 155 // isn't large enough to contain the entire span. copy_prefix_from(base::span<const T> other)156 void copy_prefix_from(base::span<const T> other) { 157 as_span().copy_prefix_from(other); 158 } 159 160 // Convenience methods to slice the vector into spans. 161 // Returns a span over the HeapArray starting at `offset` of `count` elements. 162 // If `count` is unspecified, all remaining elements are included. A CHECK() 163 // occurs if any of the parameters results in an out-of-range position in 164 // the HeapArray. 165 base::span<T> subspan(size_t offset, 166 size_t count = base::dynamic_extent) LIFETIME_BOUND { 167 return as_span().subspan(offset, count); 168 } 169 base::span<const T> subspan(size_t offset, 170 size_t count = base::dynamic_extent) const 171 LIFETIME_BOUND { 172 return as_span().subspan(offset, count); 173 } 174 175 // Returns a span over the first `count` elements of the HeapArray. A CHECK() 176 // occurs if the `count` is larger than size of the HeapArray. first(size_t count)177 base::span<T> first(size_t count) LIFETIME_BOUND { 178 return as_span().first(count); 179 } first(size_t count)180 base::span<const T> first(size_t count) const LIFETIME_BOUND { 181 return as_span().first(count); 182 } 183 184 // Returns a span over the last `count` elements of the HeapArray. A CHECK() 185 // occurs if the `count` is larger than size of the HeapArray. last(size_t count)186 base::span<T> last(size_t count) LIFETIME_BOUND { 187 return as_span().last(count); 188 } last(size_t count)189 base::span<const T> last(size_t count) const LIFETIME_BOUND { 190 return as_span().last(count); 191 } 192 193 // Leaks the memory in the HeapArray so that it will never be freed, and 194 // consumes the HeapArray, returning an unowning span that points to the 195 // memory. leak()196 base::span<T> leak() && { 197 HeapArray<T> dropped = std::move(*this); 198 T* leaked = dropped.data_.release(); 199 // SAFETY: The `size_` is the number of elements in the allocation in 200 // `data_` at all times, which is renamed as `leaked` here. 201 return UNSAFE_BUFFERS(span(leaked, dropped.size_)); 202 } 203 204 // Allows construction of a smaller HeapArray from an existing HeapArray w/o 205 // reallocations or copies. Note: The original allocation is preserved, so 206 // CopiedFrom() should be preferred for significant size reductions. take_first(size_t reduced_size)207 base::HeapArray<T> take_first(size_t reduced_size) && { 208 CHECK_LE(reduced_size, size_); 209 size_ = 0u; 210 if (!reduced_size) { 211 data_.reset(); 212 } 213 return base::HeapArray(std::move(data_), reduced_size); 214 } 215 216 // Delete the memory previously obtained from leak(). Argument is a pointer 217 // rather than a span to facilitate use by callers that have lost track of 218 // size information, as may happen when being passed through a C-style 219 // function callback. The void* argument type makes its signature compatible 220 // with typical void (*cb)(void*) C-style deletion callback. DeleteLeakedData(void * ptr)221 static void DeleteLeakedData(void* ptr) { 222 // Memory is freed by unique ptr going out of scope. 223 std::unique_ptr<T[], deleter_type> deleter(static_cast<T*>(ptr)); 224 } 225 226 private: HeapArray(std::unique_ptr<T[],deleter_type> data,size_t size)227 HeapArray(std::unique_ptr<T[], deleter_type> data, size_t size) 228 : data_(std::move(data)), size_(size) {} 229 230 std::unique_ptr<T[], deleter_type> data_; 231 size_t size_ = 0u; 232 }; 233 234 } // namespace base 235 236 #endif // BASE_CONTAINERS_HEAP_ARRAY_H_ 237