• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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