Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
darray_soa_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
15namespace gaia {
16 namespace cnt {
17 namespace darr_soa_detail {
18 using difference_type = uint32_t;
19 using size_type = uint32_t;
20 } // namespace darr_soa_detail
21
22 template <typename T>
24 using value_type = T;
25 // using pointer = T*; not supported
26 // using reference = T&; not supported
27 using difference_type = darr_soa_detail::difference_type;
28 using size_type = darr_soa_detail::size_type;
29
32
33 private:
34 uint8_t* m_ptr;
35 uint32_t m_cnt;
36 uint32_t m_idx;
37
38 public:
39 darr_soa_iterator(uint8_t* ptr, uint32_t cnt, uint32_t idx): m_ptr(ptr), m_cnt(cnt), m_idx(idx) {}
40
41 T operator*() const {
42 return mem::data_view_policy<T::gaia_Data_Layout, T>::get({m_ptr, m_cnt}, m_idx);
43 }
44 T operator->() const {
45 return mem::data_view_policy<T::gaia_Data_Layout, T>::get({m_ptr, m_cnt}, m_idx);
46 }
47 iterator operator[](size_type offset) const {
48 return iterator(m_ptr, m_cnt, m_idx + offset);
49 }
50
51 iterator& operator+=(size_type diff) {
52 m_idx += diff;
53 return *this;
54 }
55 iterator& operator-=(size_type diff) {
56 m_idx -= diff;
57 return *this;
58 }
59 iterator& operator++() {
60 ++m_idx;
61 return *this;
62 }
63 iterator operator++(int) {
64 iterator temp(*this);
65 ++*this;
66 return temp;
67 }
68 iterator& operator--() {
69 --m_idx;
70 return *this;
71 }
72 iterator operator--(int) {
73 iterator temp(*this);
74 --*this;
75 return temp;
76 }
77
78 iterator operator+(size_type offset) const {
79 return iterator(m_ptr, m_cnt, m_idx + offset);
80 }
81 iterator operator-(size_type offset) const {
82 return iterator(m_ptr, m_cnt, m_idx + offset);
83 }
84 difference_type operator-(const iterator& other) const {
85 GAIA_ASSERT(m_ptr == other.m_ptr);
86 return (difference_type)(m_idx - other.m_idx);
87 }
88
89 GAIA_NODISCARD bool operator==(const iterator& other) const {
90 GAIA_ASSERT(m_ptr == other.m_ptr);
91 return m_idx == other.m_idx;
92 }
93 GAIA_NODISCARD bool operator!=(const iterator& other) const {
94 GAIA_ASSERT(m_ptr == other.m_ptr);
95 return m_idx != other.m_idx;
96 }
97 GAIA_NODISCARD bool operator>(const iterator& other) const {
98 GAIA_ASSERT(m_ptr == other.m_ptr);
99 return m_idx > other.m_idx;
100 }
101 GAIA_NODISCARD bool operator>=(const iterator& other) const {
102 GAIA_ASSERT(m_ptr == other.m_ptr);
103 return m_idx >= other.m_idx;
104 }
105 GAIA_NODISCARD bool operator<(const iterator& other) const {
106 GAIA_ASSERT(m_ptr == other.m_ptr);
107 return m_idx < other.m_idx;
108 }
109 GAIA_NODISCARD bool operator<=(const iterator& other) const {
110 GAIA_ASSERT(m_ptr == other.m_ptr);
111 return m_idx <= other.m_idx;
112 }
113 };
114
115 template <typename T>
117 using value_type = T;
118 // using pointer = T*; not supported
119 // using reference = T&; not supported
120 using difference_type = darr_soa_detail::difference_type;
121 using size_type = darr_soa_detail::size_type;
122
125
126 private:
127 const uint8_t* m_ptr;
128 uint32_t m_cnt;
129 uint32_t m_idx;
130
131 public:
132 const_darr_soa_iterator(const uint8_t* ptr, uint32_t cnt, uint32_t idx): m_ptr(ptr), m_cnt(cnt), m_idx(idx) {}
133
134 T operator*() const {
135 return mem::data_view_policy<T::gaia_Data_Layout, T>::get({m_ptr, m_cnt}, m_idx);
136 }
137 T operator->() const {
138 return mem::data_view_policy<T::gaia_Data_Layout, T>::get({m_ptr, m_cnt}, m_idx);
139 }
140 iterator operator[](size_type offset) const {
141 return iterator(m_ptr, m_cnt, m_idx + offset);
142 }
143
144 iterator& operator+=(size_type diff) {
145 m_idx += diff;
146 return *this;
147 }
148 iterator& operator-=(size_type diff) {
149 m_idx -= diff;
150 return *this;
151 }
152 iterator& operator++() {
153 ++m_idx;
154 return *this;
155 }
156 iterator operator++(int) {
157 iterator temp(*this);
158 ++*this;
159 return temp;
160 }
161 iterator& operator--() {
162 --m_idx;
163 return *this;
164 }
165 iterator operator--(int) {
166 iterator temp(*this);
167 --*this;
168 return temp;
169 }
170
171 iterator operator+(size_type offset) const {
172 return iterator(m_ptr, m_cnt, m_idx + offset);
173 }
174 iterator operator-(size_type offset) const {
175 return iterator(m_ptr, m_cnt, m_idx + offset);
176 }
177 difference_type operator-(const iterator& other) const {
178 GAIA_ASSERT(m_ptr == other.m_ptr);
179 return (difference_type)(m_idx - other.m_idx);
180 }
181
182 GAIA_NODISCARD bool operator==(const iterator& other) const {
183 GAIA_ASSERT(m_ptr == other.m_ptr);
184 return m_idx == other.m_idx;
185 }
186 GAIA_NODISCARD bool operator!=(const iterator& other) const {
187 GAIA_ASSERT(m_ptr == other.m_ptr);
188 return m_idx != other.m_idx;
189 }
190 GAIA_NODISCARD bool operator>(const iterator& other) const {
191 GAIA_ASSERT(m_ptr == other.m_ptr);
192 return m_idx > other.m_idx;
193 }
194 GAIA_NODISCARD bool operator>=(const iterator& other) const {
195 GAIA_ASSERT(m_ptr == other.m_ptr);
196 return m_idx >= other.m_idx;
197 }
198 GAIA_NODISCARD bool operator<(const iterator& other) const {
199 GAIA_ASSERT(m_ptr == other.m_ptr);
200 return m_idx < other.m_idx;
201 }
202 GAIA_NODISCARD bool operator<=(const iterator& other) const {
203 GAIA_ASSERT(m_ptr == other.m_ptr);
204 return m_idx <= other.m_idx;
205 }
206 };
207
211 template <typename T, typename Allocator = mem::DefaultAllocatorAdaptor>
212 class darr_soa {
213 static_assert(mem::is_soa_layout_v<T>, "darr_soa can be used only with soa types");
214
215 public:
216 using value_type = T;
217 using reference = T&;
218 using const_reference = const T&;
219 using pointer = T*;
220 using const_pointer = const T*;
222 using difference_type = darr_soa_detail::difference_type;
223 using size_type = darr_soa_detail::size_type;
224
228
229 private:
230 uint8_t* m_pData = nullptr;
231 size_type m_cnt = size_type(0);
232 size_type m_cap = size_type(0);
233
234 void try_grow() {
235 const auto cnt = size();
236 const auto cap = capacity();
237
238 // Unless we reached the capacity don't do anything
239 if GAIA_LIKELY (cap != 0 && cnt < cap)
240 return;
241
242 // If no data is allocated go with at least 4 elements
243 if GAIA_UNLIKELY (m_pData == nullptr) {
244 m_pData = view_policy::template alloc<Allocator>(m_cap = 4);
245 return;
246 }
247
248 // We increase the capacity in multiples of 1.5 which is about the golden ratio (1.618).
249 // This effectively means we prefer more frequent allocations over memory fragmentation.
250 m_cap = (cap * 3 + 1) / 2;
251
252 auto* pDataOld = m_pData;
253 m_pData = view_policy::template alloc<Allocator>(m_cap);
254 view_policy::mem_add_block(m_pData, m_cap, cnt);
255 mem::move_elements<T, true>(m_pData, pDataOld, cnt, 0, m_cap, cap);
256 view_policy::template free<Allocator>(pDataOld, cap, cnt);
257 }
258
259 public:
260 darr_soa() noexcept = default;
261 darr_soa(core::zero_t) noexcept {}
262
263 darr_soa(size_type count, const_reference value) {
264 resize(count, value);
265 }
266
267 darr_soa(size_type count) {
268 resize(count);
269 }
270
271 template <typename InputIt>
272 darr_soa(InputIt first, InputIt last) {
273 const auto count = (size_type)core::distance(first, last);
274 resize(count);
275
276 if constexpr (std::is_pointer_v<InputIt>) {
277 for (size_type i = 0; i < count; ++i)
278 operator[](i) = first[i];
279 } else if constexpr (std::is_same_v<typename InputIt::iterator_category, core::random_access_iterator_tag>) {
280 for (size_type i = 0; i < count; ++i)
281 operator[](i) = *(first[i]);
282 } else {
283 size_type i = 0;
284 for (auto it = first; it != last; ++it)
285 operator[](++i) = *it;
286 }
287 }
288
289 darr_soa(std::initializer_list<T> il): darr_soa(il.begin(), il.end()) {}
290
291 darr_soa(const darr_soa& other): darr_soa(other.begin(), other.end()) {}
292
293 darr_soa(darr_soa&& other) noexcept {
294 // This is a newly constructed object.
295 // It can't have any memory allocated, yet.
296 GAIA_ASSERT(m_pData == nullptr);
297
298 m_pData = other.m_pData;
299 m_cnt = other.m_cnt;
300 m_cap = other.m_cap;
301
302 other.m_cnt = size_type(0);
303 other.m_cap = size_type(0);
304 other.m_pData = nullptr;
305 }
306
307 darr_soa& operator=(std::initializer_list<T> il) {
308 *this = darr_soa(il.begin(), il.end());
309 return *this;
310 }
311
312 darr_soa& operator=(const darr_soa& other) {
313 GAIA_ASSERT(core::addressof(other) != this);
314
315 resize(other.size());
316 mem::copy_elements<T, true>(
317 (uint8_t*)m_pData, (const uint8_t*)other.m_pData, other.size(), 0, capacity(), other.capacity());
318
319 return *this;
320 }
321
322 darr_soa& operator=(darr_soa&& other) noexcept {
323 GAIA_ASSERT(core::addressof(other) != this);
324
325 // Release previously allocated memory if there was anything
326 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
327
328 m_pData = other.m_pData;
329 m_cnt = other.m_cnt;
330 m_cap = other.m_cap;
331
332 other.m_pData = nullptr;
333 other.m_cnt = size_type(0);
334 other.m_cap = size_type(0);
335
336 return *this;
337 }
338
339 ~darr_soa() {
340 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
341 }
342
343 GAIA_CLANG_WARNING_PUSH()
344 // Memory is aligned so we can silence this warning
345 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
346
347 GAIA_NODISCARD pointer data() noexcept {
348 return reinterpret_cast<pointer>(m_pData);
349 }
350
351 GAIA_NODISCARD const_pointer data() const noexcept {
352 return reinterpret_cast<const_pointer>(m_pData);
353 }
354
355 GAIA_NODISCARD decltype(auto) operator[](size_type pos) noexcept {
356 GAIA_ASSERT(pos < size());
357 return view_policy::set({(typename view_policy::TargetCastType)m_pData, capacity()}, pos);
358 }
359
360 GAIA_NODISCARD decltype(auto) operator[](size_type pos) const noexcept {
361 GAIA_ASSERT(pos < size());
362 return view_policy::get({(typename view_policy::TargetCastType)m_pData, capacity()}, pos);
363 }
364
365 GAIA_CLANG_WARNING_POP()
366
367 void reserve(size_type cap) {
368 if (cap <= m_cap)
369 return;
370
371 auto* pDataOld = m_pData;
372 m_pData = view_policy::template alloc<Allocator>(cap);
373
374 if (pDataOld != nullptr) {
375 view_policy::mem_add_block(m_pData, cap, m_cnt);
376 mem::move_elements<T, true>(m_pData, pDataOld, m_cnt, 0, cap, m_cap);
377 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
378 }
379
380 m_cap = cap;
381 }
382
383 void resize(size_type count) {
384 if (count == m_cnt)
385 return;
386
387 // Fresh allocation
388 if (m_pData == nullptr) {
389 if (count > 0) {
390 m_pData = view_policy::template alloc<Allocator>(count);
391 view_policy::mem_add_block(m_pData, count, count);
392 m_cap = count;
393 m_cnt = count;
394 }
395 return;
396 }
397
398 // Resizing to a smaller size
399 if (count < m_cnt) {
400 view_policy::mem_pop_block(m_pData, m_cap, m_cnt, m_cnt - count);
401
402 m_cnt = count;
403 return;
404 }
405
406 // Resizing to a bigger size but still within allocated capacity
407 if (count <= m_cap) {
408 view_policy::mem_pop_block(m_pData, m_cap, m_cnt, count - m_cnt);
409
410 m_cnt = count;
411 return;
412 }
413
414 auto* pDataOld = m_pData;
415 m_pData = view_policy::template alloc<Allocator>(count);
416 view_policy::mem_add_block(m_pData, count, count);
417 // Move old data to the new location
418 mem::move_elements<T, true>(m_pData, pDataOld, m_cnt, 0, count, m_cap);
419 // Release old memory
420 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
421
422 m_cap = count;
423 m_cnt = count;
424 }
425
426 void resize(size_type count, const_reference value) {
427 const auto oldCount = m_cnt;
428 resize(count);
429
430 if constexpr (std::is_copy_constructible_v<value_type>) {
431 const value_type valueCopy = value;
432 for (size_type i = oldCount; i < m_cnt; ++i)
433 operator[](i) = valueCopy;
434 } else {
435 for (size_type i = oldCount; i < m_cnt; ++i)
436 operator[](i) = value;
437 }
438 }
439
440 void push_back(const T& arg) {
441 try_grow();
442
443 operator[](m_cnt++) = arg;
444 }
445
446 void push_back(T&& arg) {
447 try_grow();
448
449 view_policy::mem_push_block(m_pData, m_cap, m_cnt, 1);
450 operator[](m_cnt++) = GAIA_MOV(arg);
451 }
452
453 template <typename... Args>
454 decltype(auto) emplace_back(Args&&... args) {
455 try_grow();
456
457 view_policy::mem_push_block(m_pData, m_cap, m_cnt, 1);
458 operator[](m_cnt++) = T(GAIA_FWD(args)...);
459 }
460
461 void pop_back() noexcept {
462 GAIA_ASSERT(!empty());
463
464 view_policy::mem_pop_block(m_pData, m_cap, m_cnt, 1);
465
466 --m_cnt;
467 }
468
472 iterator insert(iterator pos, const T& arg) {
473 GAIA_ASSERT(pos >= data());
474 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
475
476 const auto idxSrc = (size_type)core::distance(begin(), pos);
477 try_grow();
478 const auto idxDst = (size_type)core::distance(begin(), end()) + 1;
479
480 view_policy::mem_push_block(m_pData, m_cap, m_cnt, 1);
481 mem::shift_elements_right<T, true>(m_pData, idxDst, idxSrc, m_cap);
482
483 operator[](idxSrc) = arg;
484
485 ++m_cnt;
486
487 return iterator(m_pData, capacity(), idxSrc);
488 }
489
493 iterator insert(iterator pos, T&& arg) {
494 GAIA_ASSERT(pos >= data());
495 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
496
497 const auto idxSrc = (size_type)core::distance(begin(), pos);
498 try_grow();
499 const auto idxDst = (size_type)core::distance(begin(), end());
500
501 view_policy::mem_push_block(m_pData, m_cap, m_cnt, 1);
502 mem::shift_elements_right<T, true>(m_pData, idxDst, idxSrc, m_cap);
503
504 operator[](idxSrc) = GAIA_MOV(arg);
505
506 ++m_cnt;
507
508 return iterator(m_pData, capacity(), idxSrc);
509 }
510
513 iterator erase(iterator pos) noexcept {
514 GAIA_ASSERT(pos >= data());
515 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
516
517 if (empty())
518 return end();
519
520 const auto idxSrc = (size_type)core::distance(begin(), pos);
521 const auto idxDst = (size_type)core::distance(begin(), end()) - 1;
522
523 mem::shift_elements_left<T, true>(m_pData, idxDst, idxSrc, m_cap);
524 view_policy::mem_pop_block(m_pData, m_cap, m_cnt, 1);
525
526 --m_cnt;
527
528 return iterator(m_pData, capacity(), idxSrc);
529 }
530
534 iterator erase(iterator first, iterator last) noexcept {
535 GAIA_ASSERT(first >= data())
536 GAIA_ASSERT(empty() || (first < iterator(data() + size())));
537 GAIA_ASSERT(last > first);
538 GAIA_ASSERT(last <= iterator(data() + size()));
539
540 if (empty())
541 return end();
542
543 const auto idxSrc = (size_type)core::distance(begin(), first);
544 const auto idxDst = size();
545 const auto cnt = (size_type)(last - first);
546
547 mem::shift_elements_left_fast<T, true>(m_pData, idxDst, idxSrc, cnt, m_cap);
548 view_policy::mem_pop_block(m_pData, m_cap, m_cnt, cnt);
549
550 m_cnt -= cnt;
551
552 return iterator(&data()[idxSrc]);
553 }
554
555 void clear() noexcept {
556 resize(0);
557 }
558
559 void shrink_to_fit() {
560 const auto cap = capacity();
561 const auto cnt = size();
562
563 if (cap == cnt)
564 return;
565
566 auto* pDataOld = m_pData;
567 m_pData = view_policy::template alloc<Allocator>(m_cap = cnt);
568 view_policy::mem_add_block(m_pData, m_cap, m_cnt);
569 mem::move_elements<T, true>(m_pData, pDataOld, cnt, 0);
570 view_policy::template free<Allocator>(pDataOld, cap, cnt);
571 }
572
576 template <typename Func>
577 auto retain(Func&& func) noexcept {
578 size_type erased = 0;
579 size_type idxDst = 0;
580 size_type idxSrc = 0;
581
582 while (idxSrc < m_cnt) {
583 if (func(operator[](idxSrc))) {
584 if (idxDst < idxSrc) {
585 mem::move_element<T, true>(m_pData, m_pData, idxDst, idxSrc, m_cap, m_cap);
586 auto* ptr = &data()[idxSrc];
587 core::call_dtor(ptr);
588 }
589 ++idxDst;
590 } else {
591 auto* ptr = &data()[idxSrc];
592 core::call_dtor(ptr);
593 ++erased;
594 }
595
596 ++idxSrc;
597 }
598
599 view_policy::mem_pop_block(m_pData, m_cap, m_cnt, erased);
600
601 m_cnt -= erased;
602 return idxDst;
603 }
604
605 GAIA_NODISCARD size_type size() const noexcept {
606 return m_cnt;
607 }
608
609 GAIA_NODISCARD bool empty() const noexcept {
610 return size() == 0;
611 }
612
613 GAIA_NODISCARD size_type capacity() const noexcept {
614 return m_cap;
615 }
616
617 GAIA_NODISCARD size_type max_size() const noexcept {
618 return static_cast<size_type>(-1);
619 }
620
621 GAIA_NODISCARD decltype(auto) front() noexcept {
622 GAIA_ASSERT(!empty());
623 return *begin();
624 }
625
626 GAIA_NODISCARD decltype(auto) front() const noexcept {
627 GAIA_ASSERT(!empty());
628 return *begin();
629 }
630
631 GAIA_NODISCARD decltype(auto) back() noexcept {
632 GAIA_ASSERT(!empty());
633 return operator[](m_cnt - 1);
634 }
635
636 GAIA_NODISCARD decltype(auto) back() const noexcept {
637 GAIA_ASSERT(!empty());
638 return operator[](m_cnt - 1);
639 }
640
641 GAIA_NODISCARD auto begin() noexcept {
642 return iterator(m_pData, capacity(), 0);
643 }
644
645 GAIA_NODISCARD auto begin() const noexcept {
646 return const_iterator(m_pData, capacity(), 0);
647 }
648
649 GAIA_NODISCARD auto cbegin() const noexcept {
650 return const_iterator(m_pData, capacity(), 0);
651 }
652
653 GAIA_NODISCARD auto rbegin() noexcept {
654 return iterator(m_pData, capacity(), size() - 1);
655 }
656
657 GAIA_NODISCARD auto rbegin() const noexcept {
658 return const_iterator(m_pData, capacity(), size() - 1);
659 }
660
661 GAIA_NODISCARD auto crbegin() const noexcept {
662 return const_iterator(m_pData, capacity(), size() - 1);
663 }
664
665 GAIA_NODISCARD auto end() noexcept {
666 return iterator(m_pData, capacity(), size());
667 }
668
669 GAIA_NODISCARD auto end() const noexcept {
670 return const_iterator(m_pData, capacity(), size());
671 }
672
673 GAIA_NODISCARD auto cend() const noexcept {
674 return const_iterator(m_pData, capacity(), size());
675 }
676
677 GAIA_NODISCARD auto rend() noexcept {
678 return iterator(m_pData, capacity(), -1);
679 }
680
681 GAIA_NODISCARD auto rend() const noexcept {
682 return const_iterator(m_pData, capacity(), -1);
683 }
684
685 GAIA_NODISCARD auto crend() const noexcept {
686 return const_iterator(m_pData, capacity(), -1);
687 }
688
689 GAIA_NODISCARD bool operator==(const darr_soa& other) const noexcept {
690 if (m_cnt != other.m_cnt)
691 return false;
692 const size_type n = size();
693 for (size_type i = 0; i < n; ++i)
694 if (!(operator[](i) == other[i]))
695 return false;
696 return true;
697 }
698
699 GAIA_NODISCARD constexpr bool operator!=(const darr_soa& other) const noexcept {
700 return !operator==(other);
701 }
702
703 template <size_t Item>
704 auto view_mut() noexcept {
705 return mem::data_view_policy<T::gaia_Data_Layout, T>::template set<Item>(
706 std::span<uint8_t>{GAIA_ACC((uint8_t*)m_pData), capacity()});
707 }
708
709 template <size_t Item>
710 auto view() const noexcept {
711 return mem::data_view_policy<T::gaia_Data_Layout, T>::template get<Item>(
712 std::span<const uint8_t>{GAIA_ACC((const uint8_t*)m_pData), capacity()});
713 }
714 };
715 } // namespace cnt
716
717} // namespace gaia
Array with variable size of elements of type.
Definition darray_soa_impl.h:212
iterator insert(iterator pos, T &&arg)
Insert the element to the position given by iterator pos.
Definition darray_soa_impl.h:493
auto retain(Func &&func) noexcept
Removes all elements that fail the predicate.
Definition darray_soa_impl.h:577
iterator erase(iterator first, iterator last) noexcept
Removes the elements in the range [first, last)
Definition darray_soa_impl.h:534
iterator erase(iterator pos) noexcept
Removes the element at pos.
Definition darray_soa_impl.h:513
iterator insert(iterator pos, const T &arg)
Insert the element to the position given by iterator pos.
Definition darray_soa_impl.h:472
Definition span_impl.h:99
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition darray_soa_impl.h:116
Definition darray_soa_impl.h:23
Definition utility.h:87
View policy for accessing and storing data in the SoA way. Good for SIMD processing.
Definition data_layout_policy.h:243
Definition data_layout_policy.h:87