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