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