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