Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/common/heap_array.h
4212 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#pragma once
5
6
#include <algorithm>
7
#include <cassert>
8
#include <cstdlib>
9
#include <cstring>
10
#include <span>
11
#include <type_traits>
12
13
template<typename T, std::size_t SIZE, std::size_t ALIGNMENT = 0>
14
class FixedHeapArray
15
{
16
public:
17
using value_type = T;
18
using size_type = std::size_t;
19
using difference_type = std::ptrdiff_t;
20
using reference = T&;
21
using const_reference = const T&;
22
using pointer = T*;
23
using const_pointer = const T*;
24
using this_type = FixedHeapArray<T, SIZE, ALIGNMENT>;
25
26
FixedHeapArray() { allocate(); }
27
28
FixedHeapArray(const this_type& copy)
29
{
30
allocate();
31
std::copy(copy.cbegin(), copy.cend(), begin());
32
}
33
34
FixedHeapArray(this_type&& move)
35
{
36
m_data = move.m_data;
37
move.m_data = nullptr;
38
}
39
40
~FixedHeapArray() { deallocate(); }
41
42
size_type size() const { return SIZE; }
43
size_type capacity() const { return SIZE; }
44
bool empty() const { return false; }
45
46
pointer begin() { return m_data; }
47
pointer end() { return m_data + SIZE; }
48
49
const_pointer data() const { return m_data; }
50
pointer data() { return m_data; }
51
52
const_pointer cbegin() const { return m_data; }
53
const_pointer cend() const { return m_data + SIZE; }
54
55
const_reference operator[](size_type index) const
56
{
57
assert(index < SIZE);
58
return m_data[index];
59
}
60
reference operator[](size_type index)
61
{
62
assert(index < SIZE);
63
return m_data[index];
64
}
65
66
const_reference front() const { return m_data[0]; }
67
const_reference back() const { return m_data[SIZE - 1]; }
68
reference front() { return m_data[0]; }
69
reference back() { return m_data[SIZE - 1]; }
70
71
void fill(const_reference value) { std::fill(begin(), end(), value); }
72
73
void swap(this_type& move) { std::swap(m_data, move.m_data); }
74
75
std::span<T, SIZE> span() { return std::span<T, SIZE>(m_data); }
76
std::span<const T, SIZE> cspan() const { return std::span<const T, SIZE>(m_data); }
77
78
this_type& operator=(const this_type& rhs)
79
{
80
std::copy(begin(), end(), rhs.cbegin());
81
return *this;
82
}
83
84
this_type& operator=(this_type&& move)
85
{
86
deallocate();
87
m_data = move.m_data;
88
move.m_data = nullptr;
89
return *this;
90
}
91
92
#define RELATIONAL_OPERATOR(op) \
93
bool operator op(const this_type& rhs) const \
94
{ \
95
for (size_type i = 0; i < SIZE; i++) \
96
{ \
97
if (!(m_data[i] op rhs.m_data[i])) \
98
return false; \
99
} \
100
}
101
102
RELATIONAL_OPERATOR(==);
103
RELATIONAL_OPERATOR(!=);
104
RELATIONAL_OPERATOR(<);
105
RELATIONAL_OPERATOR(<=);
106
RELATIONAL_OPERATOR(>);
107
RELATIONAL_OPERATOR(>=);
108
109
#undef RELATIONAL_OPERATOR
110
111
private:
112
void allocate()
113
{
114
if constexpr (ALIGNMENT > 0)
115
{
116
#ifdef _MSC_VER
117
m_data = static_cast<T*>(_aligned_malloc(SIZE * sizeof(T), ALIGNMENT));
118
assert(m_data);
119
if (!m_data) [[unlikely]]
120
std::abort();
121
#else
122
if (posix_memalign(reinterpret_cast<void**>(&m_data), ALIGNMENT, SIZE * sizeof(T)) != 0) [[unlikely]]
123
std::abort();
124
#endif
125
}
126
else
127
{
128
m_data = static_cast<T*>(std::malloc(SIZE * sizeof(T)));
129
assert(m_data);
130
if (!m_data) [[unlikely]]
131
std::abort();
132
}
133
}
134
void deallocate()
135
{
136
if constexpr (ALIGNMENT > 0)
137
{
138
#ifdef _MSC_VER
139
_aligned_free(m_data);
140
#else
141
std::free(m_data);
142
#endif
143
}
144
else
145
{
146
std::free(m_data);
147
}
148
}
149
150
T* m_data;
151
};
152
153
template<typename T, size_t alignment = 0>
154
class DynamicHeapArray
155
{
156
static_assert(std::is_trivially_copyable_v<T>, "T is trivially copyable");
157
static_assert(std::is_standard_layout_v<T>, "T is standard layout");
158
159
public:
160
using value_type = T;
161
using size_type = std::size_t;
162
using difference_type = std::ptrdiff_t;
163
using reference = T&;
164
using const_reference = const T&;
165
using pointer = T*;
166
using const_pointer = const T*;
167
using this_type = DynamicHeapArray<T, alignment>;
168
169
DynamicHeapArray() : m_data(nullptr), m_size(0) {}
170
explicit DynamicHeapArray(size_t size) { internal_resize(size, nullptr, 0); }
171
DynamicHeapArray(const T* begin, const T* end)
172
{
173
const size_t size = reinterpret_cast<const char*>(end) - reinterpret_cast<const char*>(begin);
174
if (size > 0)
175
{
176
internal_resize(size / sizeof(T), nullptr, 0);
177
std::memcpy(m_data, begin, size);
178
}
179
else
180
{
181
m_data = nullptr;
182
m_size = 0;
183
}
184
}
185
DynamicHeapArray(const T* begin, size_t count)
186
{
187
if (count > 0)
188
{
189
internal_resize(count, nullptr, 0);
190
std::memcpy(m_data, begin, sizeof(T) * count);
191
}
192
else
193
{
194
m_data = nullptr;
195
m_size = 0;
196
}
197
}
198
explicit DynamicHeapArray(const std::span<const T> data)
199
{
200
if (!data.empty())
201
{
202
internal_resize(data.size(), nullptr, 0);
203
std::memcpy(m_data, data.data(), sizeof(T) * data.size());
204
}
205
else
206
{
207
m_data = nullptr;
208
m_size = 0;
209
}
210
}
211
212
DynamicHeapArray(const this_type& copy)
213
{
214
if (copy.m_size > 0)
215
{
216
internal_resize(copy.m_size, nullptr, 0);
217
std::memcpy(m_data, copy.m_data, sizeof(T) * copy.m_size);
218
}
219
else
220
{
221
m_data = nullptr;
222
m_size = 0;
223
}
224
}
225
226
DynamicHeapArray(this_type&& move) noexcept
227
{
228
m_data = move.m_data;
229
m_size = move.m_size;
230
move.m_data = nullptr;
231
move.m_size = 0;
232
}
233
234
~DynamicHeapArray() { internal_deallocate(); }
235
236
size_type size() const { return m_size; }
237
size_type capacity() const { return m_size; }
238
bool empty() const { return (m_size == 0); }
239
240
pointer begin() { return m_data; }
241
pointer end() { return m_data + m_size; }
242
243
const_pointer data() const { return m_data; }
244
pointer data() { return m_data; }
245
246
const_pointer cbegin() const { return m_data; }
247
const_pointer cend() const { return m_data + m_size; }
248
249
const_reference operator[](size_type index) const
250
{
251
assert(index < m_size);
252
return m_data[index];
253
}
254
reference operator[](size_type index)
255
{
256
assert(index < m_size);
257
return m_data[index];
258
}
259
260
const_reference front() const { return m_data[0]; }
261
const_reference back() const { return m_data[m_size - 1]; }
262
reference front() { return m_data[0]; }
263
reference back() { return m_data[m_size - 1]; }
264
265
void fill(const_reference value) { std::fill(begin(), end(), value); }
266
267
void swap(this_type& rhs)
268
{
269
std::swap(m_data, rhs.m_data);
270
std::swap(m_size, rhs.m_size);
271
}
272
273
void resize(size_t new_size) { internal_resize(new_size, m_data, m_size); }
274
275
void deallocate()
276
{
277
internal_deallocate();
278
m_data = nullptr;
279
m_size = 0;
280
}
281
282
void assign(const std::span<const T> data) { assign(data.data(), data.size()); }
283
284
void assign(const T* begin, const T* end)
285
{
286
const size_t size = reinterpret_cast<const char*>(end) - reinterpret_cast<const char*>(begin);
287
const size_t count = size / sizeof(T);
288
if (count > 0)
289
{
290
if (m_size != count)
291
{
292
internal_deallocate();
293
internal_resize(count, nullptr, 0);
294
}
295
296
std::memcpy(m_data, begin, size);
297
}
298
else
299
{
300
internal_deallocate();
301
302
m_data = nullptr;
303
m_size = 0;
304
}
305
}
306
void assign(const T* begin, size_t count)
307
{
308
if (count > 0)
309
{
310
if (m_size != count)
311
{
312
internal_deallocate();
313
internal_resize(count, nullptr, 0);
314
}
315
316
std::memcpy(m_data, begin, sizeof(T) * count);
317
}
318
else
319
{
320
internal_deallocate();
321
322
m_data = nullptr;
323
m_size = 0;
324
}
325
}
326
void assign(const this_type& copy) { assign(copy.m_data, copy.m_size); }
327
void assign(this_type&& move)
328
{
329
internal_deallocate();
330
m_data = move.m_data;
331
m_size = move.m_size;
332
move.m_data = nullptr;
333
move.m_size = 0;
334
}
335
336
std::span<T> span() { return std::span<T>(m_data, m_size); }
337
std::span<const T> cspan() const { return std::span<const T>(m_data, m_size); }
338
339
std::span<T> span(size_t offset, size_t size = static_cast<size_t>(-1))
340
{
341
std::span<T> ret;
342
if (offset < m_size) [[likely]]
343
ret = std::span<T>(m_data + offset, std::min(m_size - offset, size));
344
return ret;
345
}
346
347
std::span<const T> cspan(size_t offset, size_t size = static_cast<size_t>(-1)) const
348
{
349
std::span<const T> ret;
350
if (offset < m_size) [[likely]]
351
ret = std::span<const T>(m_data + offset, std::min(m_size - offset, size));
352
return ret;
353
}
354
355
this_type& operator=(const this_type& rhs)
356
{
357
assign(rhs);
358
return *this;
359
}
360
361
this_type& operator=(this_type&& move) noexcept
362
{
363
assign(std::move(move));
364
return *this;
365
}
366
367
#define RELATIONAL_OPERATOR(op, size_op) \
368
bool operator op(const this_type& rhs) const \
369
{ \
370
if (m_size != rhs.m_size) \
371
return m_size size_op rhs.m_size; \
372
for (size_type i = 0; i < m_size; i++) \
373
{ \
374
if (!(m_data[i] op rhs.m_data[i])) \
375
return false; \
376
} \
377
}
378
379
RELATIONAL_OPERATOR(==, !=);
380
RELATIONAL_OPERATOR(!=, ==);
381
RELATIONAL_OPERATOR(<, <);
382
RELATIONAL_OPERATOR(<=, <=);
383
RELATIONAL_OPERATOR(>, >);
384
RELATIONAL_OPERATOR(>=, >=);
385
386
#undef RELATIONAL_OPERATOR
387
388
private:
389
void internal_resize(size_t size, T* prev_ptr, [[maybe_unused]] size_t prev_size)
390
{
391
if constexpr (alignment > 0)
392
{
393
#ifdef _MSC_VER
394
m_data = static_cast<T*>(_aligned_realloc(prev_ptr, size * sizeof(T), alignment));
395
assert(m_data);
396
if (!m_data) [[unlikely]]
397
std::abort();
398
#else
399
if (posix_memalign(reinterpret_cast<void**>(&m_data), alignment, size * sizeof(T)) != 0) [[unlikely]]
400
std::abort();
401
402
if (prev_ptr)
403
{
404
std::memcpy(m_data, prev_ptr, prev_size * sizeof(T));
405
std::free(prev_ptr);
406
}
407
#endif
408
}
409
else
410
{
411
m_data = static_cast<T*>(std::realloc(prev_ptr, size * sizeof(T)));
412
assert(m_data);
413
if (!m_data) [[unlikely]]
414
std::abort();
415
}
416
417
m_size = size;
418
}
419
void internal_deallocate()
420
{
421
if constexpr (alignment > 0)
422
{
423
#ifdef _MSC_VER
424
_aligned_free(m_data);
425
#else
426
std::free(m_data);
427
#endif
428
}
429
else
430
{
431
std::free(m_data);
432
}
433
}
434
435
T* m_data;
436
size_t m_size;
437
};
438
439