Grindstone Game Engine v0.2.0
An open source game engine and toolkit.
Loading...
Searching...
No Matches
DynamicArray.hpp
1#pragma once
2
3#include <type_traits>
4
5#include "../Memory/Allocators/AllocatorConcept.hpp"
6#include "../Assert.hpp"
7#include "Iterators.hpp"
8#include "Span.hpp"
9
10namespace Grindstone::Containers {
11 template<typename T, Allocator AllocatorType>
12 class DynamicArray {
13 public:
14 using Iterator = ArrayIterator<T>;
15 using ConstIterator = ConstArrayIterator<T>;
16 using ReverseIterator = ReverseArrayIterator<T>;
17 using ConstReverseIterator = ConstArrayIterator<T>;
18
19 DynamicArray() = default;
20
21 DynamicArray(AllocatorType* allocator, std::initializer_list<T> list) :
22 allocator(allocator),
23 capacity(GetStaticRequestCapacity(list.size())),
24 contents(Allocate(sizeof(T) * capacity)),
25 size(list.size()) {
26 size_t i = 0;
27 for (auto val : list) {
28 new (&contents[i++]) T(val);
29 }
30 }
31
32 DynamicArray(const DynamicArray& other) :
33 allocator(other.allocator),
34 size(other.size),
35 capacity(other.capacity),
36 contents(nullptr) {
37 if (other.contents != nullptr) {
38 contents = Allocate(sizeof(T) * capacity);
39 for (size_t i = 0; i < size; ++i) {
40 new (&contents[i]) T(other.contents[i]);
41 }
42 GS_ASSERT_ENGINE_WITH_MESSAGE(contents != nullptr, "Unable to allocate memory for DynamicArray.");
43 }
44 }
45
46 DynamicArray(DynamicArray&& other) noexcept : allocator(other.allocator), size(other.size), capacity(other.capacity), contents(other.contents) {
47 size = other.size;
48 capacity = other.capacity;
49 contents = other.contents;
50
51 other.size = 0;
52 other.capacity = 0;
53 other.contents = nullptr;
54 }
55
56 DynamicArray& operator=(const DynamicArray& other) {
57 if (contents != nullptr) {
58 for (size_t i = 0; i < size; ++i) {
59 contents[i].~T();
60 }
61
62 allocator->Free(contents);
63 contents = nullptr;
64 }
65
66 allocator = other.allocator;
67 size = other.size;
68 capacity = other.capacity;
69 if (other.contents == nullptr) {
70 contents = nullptr;
71 }
72 else {
73 contents = Allocate(sizeof(T) * capacity);
74 for (size_t i = 0; i < size; ++i) {
75 new (&contents[i]) T(std::move(other.contents[i]));
76 }
77 GS_ASSERT_ENGINE_WITH_MESSAGE(contents != nullptr, "Unable to allocate memory for DynamicArray.");
78 }
79 }
80
81 DynamicArray& operator=(DynamicArray&& other) {
82 if (contents != nullptr) {
83 for (size_t i = 0; i < size; ++i) {
84 contents[i].~T();
85 }
86
87 allocator->Free(contents);
88 contents = nullptr;
89 }
90
91 allocator = other.allocator;
92 size = other.size;
93 capacity = other.capacity;
94 contents = other.contents;
95
96 other.size = 0;
97 other.capacity = 0;
98 other.contents = nullptr;
99 }
100
102 return Grindstone::Containers::Span<T>{ contents, size };
103 }
104
105 Grindstone::Containers::Span<T> GetSpan(size_t index, size_t size) {
106 return Grindstone::Containers::Span<T>{ &contents[index], size };
107 }
108
109 ~DynamicArray() {
110 if (contents != nullptr) {
111 for (size_t i = 0; i < size; ++i) {
112 contents[i].~T();
113 }
114
115 allocator->Free(contents);
116 contents = nullptr;
117 }
118
119 capacity = 0;
120 size = 0;
121 }
122
123 static DynamicArray CreateExactReserved(size_t initialCapacity) {
124 return std::move(DynamicArray(initialCapacity, 0));
125 }
126
127 static DynamicArray CreateExactInitialized(size_t initialSize) {
128 return std::move(DynamicArray(initialSize, initialSize));
129 }
130
131 static DynamicArray CreateReservedWithAtLeast(size_t minimumInitialCapacity) {
132 return std::move(DynamicArray(GetStaticRequestCapacity(minimumInitialCapacity), 0));
133 }
134
135 static DynamicArray CreateInitializedWithAtLeast(size_t initialSize) {
136 return std::move(DynamicArray(GetStaticRequestCapacity(initialSize), initialSize));
137 }
138
139 void Resize(size_t newSize) {
140 if (newSize < size) {
141 for (size_t i = newSize; i < size; ++i) {
142 contents[i].~T();
143 }
144 size = newSize;
145 return;
146 }
147
148 if (newSize > capacity) {
149 size_t newCapacity = GetRequestCapacity(newSize);
150 ResizeBuffer(newCapacity);
151 }
152
153 for (size_t i = size; i < newSize; ++i) {
154 new (&contents[i]) T();
155 }
156 size = newSize;
157 }
158
159 void ReserveToExact(size_t newCapacity) {
160 if (newCapacity > capacity) {
161 ResizeBuffer(newCapacity);
162 }
163 }
164
165 void ReserveToAtLeast(size_t newCapacity) {
166 if (newCapacity > capacity) {
167 ResizeBuffer(GetRequestCapacity(newCapacity));
168 }
169 }
170
171 void ReserveMoreExact(size_t addedCapacity) {
172 ResizeBuffer(capacity + addedCapacity);
173 }
174
175 void ReserveMoreAtLeast(size_t addedCapacity) {
176 ResizeBuffer(GetRequestCapacity(capacity + addedCapacity));
177 }
178
179 T& PushBack(T&& val) {
180 IncrementAndResize();
181 T& newValue = contents[size];
182 newValue = std::move(val);
183 ++size;
184 return newValue;
185 }
186
187 T& PushBack(const T& val) {
188 IncrementAndResize();
189 T& newValue = contents[size];
190 new(&newValue) T(val);
191 ++size;
192 return newValue;
193 }
194
195 template<typename... Args>
196 T& EmplaceBack(Args&&... args) {
197 IncrementAndResize();
198 T& newValue = contents[size];
199 ++size;
200 new (&newValue) T(std::forward<Args>(args)...);
201 return newValue;
202 }
203
204 void AppendList(std::initializer_list<T> list, size_t offset) {
205 size_t currentSize = size;
206 size_t countToAdd = list.size();
207 size += countToAdd;
208 if (size >= capacity) {
209 size_t newCapacity = GetRequestCapacity(size);
210 ResizeBuffer(newCapacity);
211 }
212
213 for (size_t index = currentSize - 1; index >= offset; --index) {
214 new (&contents[index + countToAdd]) T(std::move(contents[index]));
215 }
216
217 size_t i = offset;
218 for (auto& val : list) {
219 new (&contents[i++]) T(std::move(val));
220 }
221 }
222
223 void AppendListBack(std::initializer_list<T> list) {
224 size_t currentSize = size;
225 size += list.size();
226 size_t newCapacity = GetRequestCapacity(size);
227 ResizeBuffer(newCapacity);
228
229 size_t i = currentSize;
230 for (auto val : list) {
231 new (&contents[i++]) T(val);
232 }
233 }
234
235 [[nodiscard]] const T& GetBegin() const {
236 return contents[0];
237 }
238
239 [[nodiscard]] T& GetBegin() {
240 return contents[0];
241 }
242
243 [[nodiscard]] const T& GetEnd() const {
244 return contents[size];
245 }
246
247 [[nodiscard]] T& GetEnd() {
248 return contents[size];
249 }
250
251 void Remove(T& first, T& last) {
252 size_t firstIndex = (&first - contents);
253 size_t lastIndex = (&last - contents);
254 for (size_t index = firstIndex; index <= lastIndex; ++index) {
255 contents[index].~T();
256 }
257
258 for (size_t index = 0; index < size - lastIndex - 1; ++index) {
259 contents[firstIndex + index] = std::move(contents[lastIndex + index + 1]);
260 }
261
262 size -= 1 + lastIndex - firstIndex;
263 }
264
265 void Remove(T& first) {
266 Remove(first, first);
267 }
268
269 void Remove(size_t index) {
270 Remove(contents[index], contents[index]);
271 }
272
273 bool TryGet(T& outValue, size_t index) {
274 if (index < size) {
275 contents[index];
276 return true;
277 }
278
279 return false;
280 }
281
282 size_t GetSize() {
283 return size;
284 }
285
286 size_t GetCapacity() {
287 return capacity;
288 }
289
290 T& operator[](size_t index) {
291 GS_ASSERT_ENGINE_WITH_MESSAGE(index < size, "Array index is invalid.");
292 return contents[index];
293 }
294
295 const T& operator[](size_t index) const {
296 GS_ASSERT_ENGINE_WITH_MESSAGE(index < size, "Array index is invalid.");
297 return contents[index];
298 }
299
300 [[nodiscard]] constexpr Iterator begin() noexcept {
301 return Iterator(contents);
302 }
303
304 [[nodiscard]] constexpr ConstIterator begin() const noexcept {
305 return ConstIterator(contents);
306 }
307
308 [[nodiscard]] constexpr Iterator end() noexcept {
309 return Iterator(&contents[size]);
310 }
311
312 [[nodiscard]] constexpr ConstIterator end() const noexcept {
313 return ConstIterator(&contents[size - 1]);
314 }
315
316 [[nodiscard]] constexpr ReverseIterator rbegin() noexcept {
317 return ReverseIterator(&contents[size - 1]);
318 }
319
320 [[nodiscard]] constexpr ConstReverseIterator rbegin() const noexcept {
321 return ConstReverseIterator(&contents[size - 1]);
322 }
323
324 [[nodiscard]] constexpr ReverseIterator rend() noexcept {
325 return ReverseIterator(contents - 1);
326 }
327
328 [[nodiscard]] constexpr ConstReverseIterator rend() const noexcept {
329 return ConstReverseIterator(contents - 1);
330 }
331
332 [[nodiscard]] constexpr ConstIterator cbegin() const noexcept {
333 return ConstIterator(contents);
334 }
335
336 [[nodiscard]] constexpr ConstIterator cend() const noexcept {
337 return ConstIterator(&contents[size]);
338 }
339
340 [[nodiscard]] constexpr ConstReverseIterator crbegin() const noexcept {
341 return ConstReverseIterator(&contents[size - 1]);
342 }
343
344 [[nodiscard]] constexpr ConstReverseIterator crend() const noexcept {
345 return ConstReverseIterator(contents - 1);
346 }
347 protected:
348 DynamicArray(size_t initialCapacity, size_t initialSize) :
349 capacity(initialCapacity),
350 size(initialSize),
351 contents(Allocate(sizeof(T) * initialCapacity)) {
352 for (size_t i = 0; i < initialSize; ++i) {
353 new (&contents[i]) T();
354 }
355 }
356
357 void IncrementAndResize() {
358 if (size + 1 > capacity) {
359 ResizeBuffer(GetPushOneCapacity());
360 }
361 }
362
363 void ResizeBuffer(size_t newCapacity) {
364 if (capacity == 0) {
365 contents = Allocate(sizeof(T) * newCapacity);
366 GS_ASSERT_ENGINE_WITH_MESSAGE(contents != nullptr, "Unable to allocate memory for DynamicArray.");
367 capacity = newCapacity;
368 }
369 else {
370 // TODO: Switch to realloc
371 void* newContents = allocator->Reallocate(contents, sizeof(T) * newCapacity, alignof(T), "DynamicArray<>");
372 GS_ASSERT_ENGINE_WITH_MESSAGE(newContents != nullptr, "Unable to allocate memory for DynamicArray.");
373 contents = reinterpret_cast<T*>(newContents);
374 capacity = newCapacity;
375 }
376 }
377
378 size_t GetPushOneCapacity() const {
379 if (capacity == 0) {
380 return (64 / sizeof(T) > 1) ? (64 / sizeof(T)) : 1;
381 }
382
383 if (capacity > 4096 * 32 / sizeof(T)) {
384 return capacity * 2;
385 }
386
387 return (capacity * 3 + 1) / 2;
388 }
389
390 size_t GetRequestCapacity(size_t requestedCapacity) const {
391 size_t testCapacity = capacity;
392
393 if (testCapacity == 0) {
394 testCapacity = (64 / sizeof(T) > 1) ? (64 / sizeof(T)) : 1;
395 }
396
397 while (testCapacity < requestedCapacity) {
398 if (testCapacity > 4096 * 32 / sizeof(T)) {
399 testCapacity = testCapacity * 2;
400 }
401 else {
402 testCapacity = (testCapacity * 3 + 1) / 2;
403 }
404 }
405
406 return testCapacity;
407 }
408
409 static size_t GetStaticRequestCapacity(size_t requestedCapacity) {
410 size_t testCapacity = (64 / sizeof(T) > 1) ? (64 / sizeof(T)) : 1;
411
412 while (testCapacity < requestedCapacity) {
413 if (testCapacity > 4096 * 32 / sizeof(T)) {
414 testCapacity = testCapacity * 2;
415 }
416 else {
417 testCapacity = (testCapacity * 3 + 1) / 2;
418 }
419 }
420
421 return testCapacity;
422 }
423
424 inline T* Allocate(size_t cap) {
425 return reinterpret_cast<T*>(allocator->AllocateRaw(sizeof(T) * capacity, alignof(T), "DynamicArray<>"));
426 }
427
428 AllocatorType* allocator = nullptr;
429 size_t capacity = 0;
430 size_t size = 0;
431 T* contents = nullptr;
432 };
433}
Definition Span.hpp:12
Definition Iterators.hpp:29