Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
darray_ext_impl.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstddef>
5#include <initializer_list>
6#include <type_traits>
7#include <utility>
8
9#include "gaia/core/iterator.h"
10#include "gaia/core/utility.h"
11#include "gaia/mem/data_layout_policy.h"
12#include "gaia/mem/mem_sani.h"
13#include "gaia/mem/mem_utils.h"
14#include "gaia/mem/raw_data_holder.h"
15
16namespace gaia {
17 namespace cnt {
18 namespace darr_ext_detail {
19 using difference_type = uint32_t;
20 using size_type = uint32_t;
21 } // namespace darr_ext_detail
22
27 template <typename T, darr_ext_detail::size_type N, typename Allocator = mem::DefaultAllocatorAdaptor>
28 class darr_ext {
29 public:
30 static_assert(N > 0);
31
32 using value_type = T;
33 using reference = T&;
34 using const_reference = const T&;
35 using pointer = T*;
36 using const_pointer = const T*;
38 using difference_type = darr_ext_detail::difference_type;
39 using size_type = darr_ext_detail::size_type;
40
41 using iterator = pointer;
42 using const_iterator = const_pointer;
44
45 static constexpr size_t value_size = sizeof(T);
46 static constexpr size_type extent = N;
47 static constexpr uint32_t allocated_bytes = view_policy::get_min_byte_size(0, N);
48
49 private:
53 uint8_t* m_pDataHeap = nullptr;
55 uint8_t* m_pData = m_data;
57 size_type m_cnt = size_type(0);
59 size_type m_cap = extent;
60
61 void try_grow() {
62 const auto cnt = size();
63 const auto cap = capacity();
64
65 // Unless we reached the capacity don't do anything
66 if GAIA_LIKELY (cnt < cap)
67 return;
68
69 // We increase the capacity in multiples of 1.5 which is about the golden ratio (1.618).
70 // This means we prefer more frequent allocations over memory fragmentation.
71 m_cap = (cap * 3 + 1) / 2;
72
73 if GAIA_UNLIKELY (m_pDataHeap == nullptr) {
74 // If no heap memory is allocated yet we need to allocate it and move the old stack elements to it
75 m_pDataHeap = view_policy::template alloc<Allocator>(m_cap);
76 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pDataHeap, m_cap, cnt);
77 mem::move_elements<T, false>(m_pDataHeap, m_data, cnt, 0, m_cap, cap);
78 } else {
79 // Move items from the old heap array to the new one. Delete the old
80 auto* pDataOld = m_pDataHeap;
81 m_pDataHeap = view_policy::template alloc<Allocator>(m_cap);
82 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pDataHeap, m_cap, cnt);
83 mem::move_elements<T, false>(m_pDataHeap, pDataOld, cnt, 0, m_cap, cap);
84 view_policy::template free<Allocator>(pDataOld, cap, cnt);
85 }
86
87 m_pData = m_pDataHeap;
88 }
89
90 public:
91 darr_ext() noexcept = default;
92 darr_ext(core::zero_t) noexcept {}
93
94 darr_ext(size_type count, const_reference value) {
95 resize(count, value);
96 }
97
98 darr_ext(size_type count) {
99 resize(count);
100 }
101
102 template <typename InputIt>
103 darr_ext(InputIt first, InputIt last) {
104 const auto count = (size_type)core::distance(first, last);
105 resize(count);
106
107 if constexpr (std::is_pointer_v<InputIt>) {
108 for (size_type i = 0; i < count; ++i)
109 operator[](i) = first[i];
110 } else if constexpr (std::is_same_v<typename InputIt::iterator_category, core::random_access_iterator_tag>) {
111 for (size_type i = 0; i < count; ++i)
112 operator[](i) = *(first[i]);
113 } else {
114 size_type i = 0;
115 for (auto it = first; it != last; ++it)
116 operator[](++i) = *it;
117 }
118 }
119
120 darr_ext(std::initializer_list<T> il): darr_ext(il.begin(), il.end()) {}
121
122 darr_ext(const darr_ext& other): darr_ext(other.begin(), other.end()) {}
123
124 darr_ext(darr_ext&& other) noexcept {
125 GAIA_ASSERT(core::addressof(other) != this);
126
127 // Moving from stack-allocated source
128 if (other.m_pDataHeap == nullptr) {
129 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_data, extent, other.size());
130 mem::move_elements<T, false>(m_data, other.m_data, other.size(), 0, extent, other.extent);
131 GAIA_MEM_SANI_DEL_BLOCK(value_size, other.m_data, extent, other.size());
132 m_pDataHeap = nullptr;
133 m_pData = m_data;
134 } else {
135 m_pDataHeap = other.m_pDataHeap;
136 m_pData = m_pDataHeap;
137 }
138
139 m_cnt = other.m_cnt;
140 m_cap = other.m_cap;
141
142 other.m_pDataHeap = nullptr;
143 other.m_pData = other.m_data;
144 other.m_cnt = size_type(0);
145 other.m_cap = extent;
146 }
147
148 darr_ext& operator=(std::initializer_list<T> il) {
149 *this = darr_ext(il.begin(), il.end());
150 return *this;
151 }
152
153 darr_ext& operator=(const darr_ext& other) {
154 GAIA_ASSERT(core::addressof(other) != this);
155
156 resize(other.size());
157 mem::copy_elements<T, false>(
158 m_pData, (const uint8_t*)other.m_pData, other.size(), 0, capacity(), other.capacity());
159
160 return *this;
161 }
162
163 darr_ext& operator=(darr_ext&& other) noexcept {
164 GAIA_ASSERT(core::addressof(other) != this);
165
166 // Release previously allocated memory if there was anything
167 view_policy::template free<Allocator>(m_pDataHeap, m_cap, m_cnt);
168
169 // Moving from stack-allocated source
170 if (other.m_pDataHeap == nullptr) {
171 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_data, extent, other.size());
172 mem::move_elements<T, false>(m_data, other.m_data, other.size(), 0, extent, other.extent);
173 GAIA_MEM_SANI_DEL_BLOCK(value_size, other.m_data, extent, other.size());
174 m_pDataHeap = nullptr;
175 m_pData = m_data;
176 } else {
177 m_pDataHeap = other.m_pDataHeap;
178 m_pData = m_pDataHeap;
179 }
180
181 m_cnt = other.m_cnt;
182 m_cap = other.m_cap;
183
184 other.m_pDataHeap = nullptr;
185 other.m_pData = other.m_data;
186 other.m_cnt = size_type(0);
187 other.m_cap = extent;
188
189 return *this;
190 }
191
192 ~darr_ext() {
193 view_policy::template free<Allocator>(m_pDataHeap, m_cap, m_cnt);
194 }
195
196 GAIA_CLANG_WARNING_PUSH()
197 // Memory is aligned so we can silence this warning
198 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
199
200 GAIA_NODISCARD pointer data() noexcept {
201 return reinterpret_cast<pointer>(m_pData);
202 }
203
204 GAIA_NODISCARD const_pointer data() const noexcept {
205 return reinterpret_cast<const_pointer>(m_pData);
206 }
207
208 GAIA_NODISCARD decltype(auto) operator[](size_type pos) noexcept {
209 GAIA_ASSERT(pos < size());
210 return view_policy::set({(typename view_policy::TargetCastType)m_pData, size()}, pos);
211 }
212
213 GAIA_NODISCARD decltype(auto) operator[](size_type pos) const noexcept {
214 GAIA_ASSERT(pos < size());
215 return view_policy::get({(typename view_policy::TargetCastType)m_pData, size()}, pos);
216 }
217
218 GAIA_CLANG_WARNING_POP()
219
220 void reserve(size_type cap) {
221 if (cap <= m_cap)
222 return;
223
224 auto* pDataOld = m_pDataHeap;
225 m_pDataHeap = view_policy::template alloc<Allocator>(cap);
226 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pDataHeap, cap, m_cnt);
227 if (pDataOld != nullptr) {
228 mem::move_elements<T, false>(m_pDataHeap, pDataOld, m_cnt, 0, cap, m_cap);
229 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
230 } else {
231 mem::move_elements<T, false>(m_pDataHeap, m_data, m_cnt, 0, cap, m_cap);
232 GAIA_MEM_SANI_DEL_BLOCK(value_size, m_data, m_cap, m_cnt);
233 }
234
235 m_cap = cap;
236 m_pData = m_pDataHeap;
237 }
238
239 void resize(size_type count) {
240 if (count == m_cnt)
241 return;
242
243 // Resizing to a smaller size
244 if (count < m_cnt) {
245 // Destroy elements at the end
246 core::call_dtor_n(&data()[count], m_cnt - count);
247 GAIA_MEM_SANI_POP_N(value_size, data(), m_cap, m_cnt, m_cnt - count);
248
249 m_cnt = count;
250 return;
251 }
252
253 // Resizing to a bigger size but still within allocated capacity
254 if (count <= m_cap) {
255 // Construct new elements
256 GAIA_MEM_SANI_PUSH_N(value_size, data(), m_cap, m_cnt, count - m_cnt);
257 core::call_ctor_n(&data()[m_cnt], count - m_cnt);
258
259 m_cnt = count;
260 return;
261 }
262
263 auto* pDataOld = m_pDataHeap;
264 m_pDataHeap = view_policy::template alloc<Allocator>(count);
265 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pDataHeap, count, count);
266 auto* pDataNew = reinterpret_cast<pointer>(m_pDataHeap);
267 if (pDataOld != nullptr) {
268 mem::move_elements<T, false>(m_pDataHeap, pDataOld, m_cnt, 0, count, m_cap);
269 core::call_ctor_n(&pDataNew[m_cnt], count - m_cnt);
270 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
271 } else {
272 mem::move_elements<T, false>(m_pDataHeap, m_data, m_cnt, 0, count, m_cap);
273 GAIA_MEM_SANI_DEL_BLOCK(value_size, m_data, m_cap, m_cnt);
274 }
275
276 m_cap = count;
277 m_cnt = count;
278 m_pData = m_pDataHeap;
279 }
280
281 void resize(size_type count, const_reference value) {
282 const auto oldCount = m_cnt;
283 resize(count);
284
285 if constexpr (std::is_copy_constructible_v<value_type>) {
286 const value_type valueCopy = value;
287 for (size_type i = oldCount; i < m_cnt; ++i)
288 operator[](i) = valueCopy;
289 } else {
290 for (size_type i = oldCount; i < m_cnt; ++i)
291 operator[](i) = value;
292 }
293 }
294
295 void push_back(const T& arg) {
296 try_grow();
297
298 GAIA_MEM_SANI_PUSH(value_size, data(), m_cap, m_cnt);
299 auto* ptr = &data()[m_cnt++];
300 core::call_ctor(ptr, arg);
301 }
302
303 void push_back(T&& arg) {
304 try_grow();
305
306 GAIA_MEM_SANI_PUSH(value_size, data(), m_cap, m_cnt);
307 auto* ptr = &data()[m_cnt++];
308 core::call_ctor(ptr, GAIA_MOV(arg));
309 }
310
311 template <typename... Args>
312 decltype(auto) emplace_back(Args&&... args) {
313 try_grow();
314
315 GAIA_MEM_SANI_PUSH(value_size, data(), m_cap, m_cnt);
316 auto* ptr = &data()[m_cnt++];
317 core::call_ctor(ptr, GAIA_FWD(args)...);
318 return (reference)*ptr;
319 }
320
321 void pop_back() noexcept {
322 GAIA_ASSERT(!empty());
323
324 auto* ptr = &data()[m_cnt - 1];
325 core::call_dtor(ptr);
326 GAIA_MEM_SANI_POP(value_size, data(), m_cap, m_cnt);
327
328 --m_cnt;
329 }
330
334 iterator insert(iterator pos, const T& arg) {
335 GAIA_ASSERT(pos >= data());
336 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
337
338 const auto idxSrc = (size_type)core::distance(begin(), pos);
339 try_grow();
340 const auto idxDst = (size_type)core::distance(begin(), end()) + 1;
341
342 GAIA_MEM_SANI_PUSH(value_size, data(), m_cap, m_cnt);
343 mem::shift_elements_right<T, false>(m_pData, idxDst, idxSrc, m_cap);
344 auto* ptr = &data()[idxSrc];
345 core::call_ctor(ptr, arg);
346
347 ++m_cnt;
348
349 return iterator(ptr);
350 }
351
355 iterator insert(iterator pos, T&& arg) {
356 GAIA_ASSERT(pos >= data());
357 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
358
359 const auto idxSrc = (size_type)core::distance(begin(), pos);
360 try_grow();
361 const auto idxDst = (size_type)core::distance(begin(), end());
362
363 GAIA_MEM_SANI_PUSH(value_size, data(), m_cap, m_cnt);
364 mem::shift_elements_right<T, false>(m_pData, idxDst, idxSrc, m_cap);
365 auto* ptr = &data()[idxSrc];
366 core::call_ctor(ptr, GAIA_MOV(arg));
367
368 ++m_cnt;
369
370 return iterator(ptr);
371 }
372
375 iterator erase(iterator pos) noexcept {
376 GAIA_ASSERT(pos >= data());
377 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
378
379 if (empty())
380 return end();
381
382 const auto idxSrc = (size_type)core::distance(begin(), pos);
383 const auto idxDst = (size_type)core::distance(begin(), end()) - 1;
384
385 mem::shift_elements_left<T, false>(m_pData, idxDst, idxSrc, m_cap);
386 // Destroy if it's the last element
387 auto* ptr = &data()[m_cnt - 1];
388 core::call_dtor(ptr);
389 GAIA_MEM_SANI_POP(value_size, data(), m_cap, m_cnt);
390
391 --m_cnt;
392
393 return iterator(&data()[idxSrc]);
394 }
395
399 iterator erase(iterator first, iterator last) noexcept {
400 GAIA_ASSERT(first >= data())
401 GAIA_ASSERT(empty() || (first < iterator(data() + size())));
402 GAIA_ASSERT(last > first);
403 GAIA_ASSERT(last <= iterator(data() + size()));
404
405 if (empty())
406 return end();
407
408 const auto idxSrc = (size_type)core::distance(begin(), first);
409 const auto idxDst = size();
410 const auto cnt = (size_type)(last - first);
411
412 mem::shift_elements_left_fast<T, false>(m_pData, idxDst, idxSrc, cnt, m_cap);
413 // Destroy if it's the last element
414 core::call_dtor_n(&data()[m_cnt - cnt], cnt);
415 GAIA_MEM_SANI_POP_N(value_size, data(), m_cap, m_cnt, cnt);
416
417 m_cnt -= cnt;
418
419 return iterator(&data()[idxSrc]);
420 }
421
422 void clear() noexcept {
423 resize(0);
424 }
425
426 void shrink_to_fit() {
427 const auto cap = capacity();
428 const auto cnt = size();
429
430 if (cap == cnt)
431 return;
432
433 if (m_pDataHeap != nullptr) {
434 auto* pDataOld = m_pDataHeap;
435
436 if (cnt < extent) {
437 mem::move_elements<T, false>(m_data, pDataOld, cnt, 0);
438 m_pData = m_data;
439 m_cap = extent;
440 } else {
441 m_pDataHeap = view_policy::template alloc<Allocator>(m_cap = cnt);
442 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pDataHeap, m_cap, m_cnt);
443 mem::move_elements<T, false>(m_pDataHeap, pDataOld, cnt, 0);
444 m_pData = m_pDataHeap;
445 }
446
447 GAIA_MEM_SANI_DEL_BLOCK(value_size, pDataOld, cap, cnt);
448 view_policy::template free<Allocator>(pDataOld);
449 } else
450 resize(cnt);
451 }
452
456 template <typename Func>
457 auto retain(Func&& func) noexcept {
458 size_type erased = 0;
459 size_type idxDst = 0;
460 size_type idxSrc = 0;
461
462 while (idxSrc < m_cnt) {
463 if (func(operator[](idxSrc))) {
464 if (idxDst < idxSrc) {
465 auto* ptr = (uint8_t*)data();
466 mem::move_element<T, false>(ptr, ptr, idxDst, idxSrc, m_cap, m_cap);
467 auto* ptr2 = &data()[idxSrc];
468 core::call_dtor(ptr2);
469 }
470 ++idxDst;
471 } else {
472 auto* ptr = &data()[idxSrc];
473 core::call_dtor(ptr);
474 ++erased;
475 }
476
477 ++idxSrc;
478 }
479
480 GAIA_MEM_SANI_POP_N(value_size, data(), m_cap, m_cnt, erased);
481
482 m_cnt -= erased;
483 return idxDst;
484 }
485
486 GAIA_NODISCARD size_type size() const noexcept {
487 return m_cnt;
488 }
489
490 GAIA_NODISCARD bool empty() const noexcept {
491 return size() == 0;
492 }
493
494 GAIA_NODISCARD size_type capacity() const noexcept {
495 return m_cap;
496 }
497
498 GAIA_NODISCARD size_type max_size() const noexcept {
499 return N;
500 }
501
502 GAIA_NODISCARD decltype(auto) front() noexcept {
503 GAIA_ASSERT(!empty());
504 return (reference)*begin();
505 }
506
507 GAIA_NODISCARD decltype(auto) front() const noexcept {
508 GAIA_ASSERT(!empty());
509 return (const_reference)*begin();
510 }
511
512 GAIA_NODISCARD decltype(auto) back() noexcept {
513 GAIA_ASSERT(!empty());
514 return (reference) operator[](m_cnt - 1);
515 }
516
517 GAIA_NODISCARD decltype(auto) back() const noexcept {
518 GAIA_ASSERT(!empty());
519 return (const_reference) operator[](m_cnt - 1);
520 }
521
522 GAIA_NODISCARD auto begin() noexcept {
523 return iterator(data());
524 }
525
526 GAIA_NODISCARD auto begin() const noexcept {
527 return const_iterator(data());
528 }
529
530 GAIA_NODISCARD auto cbegin() const noexcept {
531 return const_iterator(data());
532 }
533
534 GAIA_NODISCARD auto rbegin() noexcept {
535 return iterator((pointer)&back());
536 }
537
538 GAIA_NODISCARD auto rbegin() const noexcept {
539 return const_iterator((const_pointer)&back());
540 }
541
542 GAIA_NODISCARD auto crbegin() const noexcept {
543 return const_iterator((const_pointer)&back());
544 }
545
546 GAIA_NODISCARD auto end() noexcept {
547 return iterator(data() + size());
548 }
549
550 GAIA_NODISCARD auto end() const noexcept {
551 return const_iterator(data() + size());
552 }
553
554 GAIA_NODISCARD auto cend() const noexcept {
555 return const_iterator(data() + size());
556 }
557
558 GAIA_NODISCARD auto rend() noexcept {
559 return iterator(data() - 1);
560 }
561
562 GAIA_NODISCARD auto rend() const noexcept {
563 return const_iterator(data() - 1);
564 }
565
566 GAIA_NODISCARD auto crend() const noexcept {
567 return const_iterator(data() - 1);
568 }
569
570 GAIA_NODISCARD bool operator==(const darr_ext& other) const noexcept {
571 if (m_cnt != other.m_cnt)
572 return false;
573 const size_type n = size();
574 for (size_type i = 0; i < n; ++i)
575 if (!(operator[](i) == other[i]))
576 return false;
577 return true;
578 }
579
580 GAIA_NODISCARD constexpr bool operator!=(const darr_ext& other) const noexcept {
581 return !operator==(other);
582 }
583 };
584
585 namespace detail {
586 template <typename T, uint32_t N, uint32_t... I>
587 darr_ext<std::remove_cv_t<T>, N> to_sarray_impl(T (&a)[N], std::index_sequence<I...> /*no_name*/) {
588 return {{a[I]...}};
589 }
590 } // namespace detail
591
592 template <typename T, uint32_t N>
593 darr_ext<std::remove_cv_t<T>, N> to_sarray(T (&a)[N]) {
594 return detail::to_sarray_impl(a, std::make_index_sequence<N>{});
595 }
596
597 } // namespace cnt
598
599} // namespace gaia
Array of elements of type.
Definition darray_ext_impl.h:28
iterator insert(iterator pos, T &&arg)
Insert the element to the position given by iterator pos.
Definition darray_ext_impl.h:355
iterator insert(iterator pos, const T &arg)
Insert the element to the position given by iterator pos.
Definition darray_ext_impl.h:334
iterator erase(iterator first, iterator last) noexcept
Removes the elements in the range [first, last)
Definition darray_ext_impl.h:399
auto retain(Func &&func) noexcept
Removes all elements that fail the predicate.
Definition darray_ext_impl.h:457
iterator erase(iterator pos) noexcept
Removes the element at pos.
Definition darray_ext_impl.h:375
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition utility.h:87
View policy for accessing and storing data in the AoS way. Good for random access and when accessing ...
Definition data_layout_policy.h:112
Definition raw_data_holder.h:12