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);
265 for (auto it: *this)
266 *it = value;
267 }
268
269 darr_soa(size_type count) {
270 resize(count);
271 }
272
273 template <typename InputIt>
274 darr_soa(InputIt first, InputIt last) {
275 const auto count = (size_type)core::distance(first, last);
276 resize(count);
277
278 if constexpr (std::is_pointer_v<InputIt>) {
279 for (size_type i = 0; i < count; ++i)
280 operator[](i) = first[i];
281 } else if constexpr (std::is_same_v<typename InputIt::iterator_category, core::random_access_iterator_tag>) {
282 for (size_type i = 0; i < count; ++i)
283 operator[](i) = *(first[i]);
284 } else {
285 size_type i = 0;
286 for (auto it = first; it != last; ++it)
287 operator[](++i) = *it;
288 }
289 }
290
291 darr_soa(std::initializer_list<T> il): darr_soa(il.begin(), il.end()) {}
292
293 darr_soa(const darr_soa& other): darr_soa(other.begin(), other.end()) {}
294
295 darr_soa(darr_soa&& other) noexcept {
296 // This is a newly constructed object.
297 // It can't have any memory allocated, yet.
298 GAIA_ASSERT(m_pData == nullptr);
299
300 m_pData = other.m_pData;
301 m_cnt = other.m_cnt;
302 m_cap = other.m_cap;
303
304 other.m_cnt = size_type(0);
305 other.m_cap = size_type(0);
306 other.m_pData = nullptr;
307 }
308
309 darr_soa& operator=(std::initializer_list<T> il) {
310 *this = darr_soa(il.begin(), il.end());
311 return *this;
312 }
313
314 darr_soa& operator=(const darr_soa& other) {
315 GAIA_ASSERT(core::addressof(other) != this);
316
317 resize(other.size());
318 mem::copy_elements<T, true>(
319 (uint8_t*)m_pData, (const uint8_t*)other.m_pData, other.size(), 0, capacity(), other.capacity());
320
321 return *this;
322 }
323
324 darr_soa& operator=(darr_soa&& other) noexcept {
325 GAIA_ASSERT(core::addressof(other) != this);
326
327 // Release previously allocated memory if there was anything
328 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
329
330 m_pData = other.m_pData;
331 m_cnt = other.m_cnt;
332 m_cap = other.m_cap;
333
334 other.m_pData = nullptr;
335 other.m_cnt = size_type(0);
336 other.m_cap = size_type(0);
337
338 return *this;
339 }
340
341 ~darr_soa() {
342 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
343 }
344
345 GAIA_CLANG_WARNING_PUSH()
346 // Memory is aligned so we can silence this warning
347 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
348
349 GAIA_NODISCARD pointer data() noexcept {
350 return reinterpret_cast<pointer>(m_pData);
351 }
352
353 GAIA_NODISCARD const_pointer data() const noexcept {
354 return reinterpret_cast<const_pointer>(m_pData);
355 }
356
357 GAIA_NODISCARD decltype(auto) operator[](size_type pos) noexcept {
358 GAIA_ASSERT(pos < size());
359 return view_policy::set({(typename view_policy::TargetCastType)m_pData, capacity()}, pos);
360 }
361
362 GAIA_NODISCARD decltype(auto) operator[](size_type pos) const noexcept {
363 GAIA_ASSERT(pos < size());
364 return view_policy::get({(typename view_policy::TargetCastType)m_pData, capacity()}, pos);
365 }
366
367 GAIA_CLANG_WARNING_POP()
368
369 void reserve(size_type cap) {
370 if (cap <= m_cap)
371 return;
372
373 auto* pDataOld = m_pData;
374 m_pData = view_policy::template alloc<Allocator>(cap);
375
376 if (pDataOld != nullptr) {
377 view_policy::mem_add_block(m_pData, cap, m_cnt);
378 mem::move_elements<T, true>(m_pData, pDataOld, m_cnt, 0, cap, m_cap);
379 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
380 }
381
382 m_cap = cap;
383 }
384
385 void resize(size_type count) {
386 if (count == m_cnt)
387 return;
388
389 // Fresh allocation
390 if (m_pData == nullptr) {
391 if (count > 0) {
392 m_pData = view_policy::template alloc<Allocator>(count);
393 view_policy::mem_add_block(m_pData, count, count);
394 m_cap = count;
395 m_cnt = count;
396 }
397 return;
398 }
399
400 // Resizing to a smaller size
401 if (count < m_cnt) {
402 view_policy::mem_pop_block(m_pData, m_cap, m_cnt, m_cnt - count);
403
404 m_cnt = count;
405 return;
406 }
407
408 // Resizing to a bigger size but still within allocated capacity
409 if (count <= m_cap) {
410 view_policy::mem_pop_block(m_pData, m_cap, m_cnt, count - m_cnt);
411
412 m_cnt = count;
413 return;
414 }
415
416 auto* pDataOld = m_pData;
417 m_pData = view_policy::template alloc<Allocator>(count);
418 view_policy::mem_add_block(m_pData, count, count);
419 // Move old data to the new location
420 mem::move_elements<T, true>(m_pData, pDataOld, m_cnt, 0, count, m_cap);
421 // Release old memory
422 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
423
424 m_cap = count;
425 m_cnt = count;
426 }
427
428 void push_back(const T& arg) {
429 try_grow();
430
431 operator[](m_cnt++) = arg;
432 }
433
434 void push_back(T&& arg) {
435 try_grow();
436
437 view_policy::mem_push_block(m_pData, m_cap, m_cnt, 1);
438 operator[](m_cnt++) = GAIA_MOV(arg);
439 }
440
441 template <typename... Args>
442 decltype(auto) emplace_back(Args&&... args) {
443 try_grow();
444
445 view_policy::mem_push_block(m_pData, m_cap, m_cnt, 1);
446 operator[](m_cnt++) = T(GAIA_FWD(args)...);
447 }
448
449 void pop_back() noexcept {
450 GAIA_ASSERT(!empty());
451
452 view_policy::mem_pop_block(m_pData, m_cap, m_cnt, 1);
453
454 --m_cnt;
455 }
456
460 iterator insert(iterator pos, const T& arg) {
461 GAIA_ASSERT(pos >= data());
462 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
463
464 const auto idxSrc = (size_type)core::distance(begin(), pos);
465 try_grow();
466 const auto idxDst = (size_type)core::distance(begin(), end()) + 1;
467
468 view_policy::mem_push_block(m_pData, m_cap, m_cnt, 1);
469 mem::shift_elements_right<T, true>(m_pData, idxDst, idxSrc, m_cap);
470
471 operator[](idxSrc) = arg;
472
473 ++m_cnt;
474
475 return iterator(m_pData, capacity(), idxSrc);
476 }
477
481 iterator insert(iterator pos, T&& arg) {
482 GAIA_ASSERT(pos >= data());
483 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
484
485 const auto idxSrc = (size_type)core::distance(begin(), pos);
486 try_grow();
487 const auto idxDst = (size_type)core::distance(begin(), end());
488
489 view_policy::mem_push_block(m_pData, m_cap, m_cnt, 1);
490 mem::shift_elements_right<T, true>(m_pData, idxDst, idxSrc, m_cap);
491
492 operator[](idxSrc) = GAIA_MOV(arg);
493
494 ++m_cnt;
495
496 return iterator(m_pData, capacity(), idxSrc);
497 }
498
501 iterator erase(iterator pos) noexcept {
502 GAIA_ASSERT(pos >= data());
503 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
504
505 if (empty())
506 return end();
507
508 const auto idxSrc = (size_type)core::distance(begin(), pos);
509 const auto idxDst = (size_type)core::distance(begin(), end()) - 1;
510
511 mem::shift_elements_left<T, true>(m_pData, idxDst, idxSrc, m_cap);
512 view_policy::mem_pop_block(m_pData, m_cap, m_cnt, 1);
513
514 --m_cnt;
515
516 return iterator(m_pData, capacity(), idxSrc);
517 }
518
522 iterator erase(iterator first, iterator last) noexcept {
523 GAIA_ASSERT(first >= data())
524 GAIA_ASSERT(empty() || (first < iterator(data() + size())));
525 GAIA_ASSERT(last > first);
526 GAIA_ASSERT(last <= iterator(data() + size()));
527
528 if (empty())
529 return end();
530
531 const auto idxSrc = (size_type)core::distance(begin(), first);
532 const auto idxDst = size();
533 const auto cnt = (size_type)(last - first);
534
535 mem::shift_elements_left_fast<T, true>(m_pData, idxDst, idxSrc, cnt, m_cap);
536 view_policy::mem_pop_block(m_pData, m_cap, m_cnt, cnt);
537
538 m_cnt -= cnt;
539
540 return iterator(&data()[idxSrc]);
541 }
542
543 void clear() noexcept {
544 resize(0);
545 }
546
547 void shrink_to_fit() {
548 const auto cap = capacity();
549 const auto cnt = size();
550
551 if (cap == cnt)
552 return;
553
554 auto* pDataOld = m_pData;
555 m_pData = view_policy::template alloc<Allocator>(m_cap = cnt);
556 view_policy::mem_add_block(m_pData, m_cap, m_cnt);
557 mem::move_elements<T, true>(m_pData, pDataOld, cnt, 0);
558 view_policy::template free<Allocator>(pDataOld, cap, cnt);
559 }
560
564 template <typename Func>
565 auto retain(Func&& func) noexcept {
566 size_type erased = 0;
567 size_type idxDst = 0;
568 size_type idxSrc = 0;
569
570 while (idxSrc < m_cnt) {
571 if (func(operator[](idxSrc))) {
572 if (idxDst < idxSrc) {
573 mem::move_element<T, true>(m_pData, m_pData, idxDst, idxSrc, m_cap, m_cap);
574 auto* ptr = &data()[idxSrc];
575 core::call_dtor(ptr);
576 }
577 ++idxDst;
578 } else {
579 auto* ptr = &data()[idxSrc];
580 core::call_dtor(ptr);
581 ++erased;
582 }
583
584 ++idxSrc;
585 }
586
587 view_policy::mem_pop_block(m_pData, m_cap, m_cnt, erased);
588
589 m_cnt -= erased;
590 return idxDst;
591 }
592
593 GAIA_NODISCARD size_type size() const noexcept {
594 return m_cnt;
595 }
596
597 GAIA_NODISCARD bool empty() const noexcept {
598 return size() == 0;
599 }
600
601 GAIA_NODISCARD size_type capacity() const noexcept {
602 return m_cap;
603 }
604
605 GAIA_NODISCARD size_type max_size() const noexcept {
606 return static_cast<size_type>(-1);
607 }
608
609 GAIA_NODISCARD decltype(auto) front() noexcept {
610 GAIA_ASSERT(!empty());
611 return *begin();
612 }
613
614 GAIA_NODISCARD decltype(auto) front() const noexcept {
615 GAIA_ASSERT(!empty());
616 return *begin();
617 }
618
619 GAIA_NODISCARD decltype(auto) back() noexcept {
620 GAIA_ASSERT(!empty());
621 return operator[](m_cnt - 1);
622 }
623
624 GAIA_NODISCARD decltype(auto) back() const noexcept {
625 GAIA_ASSERT(!empty());
626 return operator[](m_cnt - 1);
627 }
628
629 GAIA_NODISCARD auto begin() noexcept {
630 return iterator(m_pData, capacity(), 0);
631 }
632
633 GAIA_NODISCARD auto begin() const noexcept {
634 return const_iterator(m_pData, capacity(), 0);
635 }
636
637 GAIA_NODISCARD auto cbegin() const noexcept {
638 return const_iterator(m_pData, capacity(), 0);
639 }
640
641 GAIA_NODISCARD auto rbegin() noexcept {
642 return iterator(m_pData, capacity(), size() - 1);
643 }
644
645 GAIA_NODISCARD auto rbegin() const noexcept {
646 return const_iterator(m_pData, capacity(), size() - 1);
647 }
648
649 GAIA_NODISCARD auto crbegin() const noexcept {
650 return const_iterator(m_pData, capacity(), size() - 1);
651 }
652
653 GAIA_NODISCARD auto end() noexcept {
654 return iterator(m_pData, capacity(), size());
655 }
656
657 GAIA_NODISCARD auto end() const noexcept {
658 return const_iterator(m_pData, capacity(), size());
659 }
660
661 GAIA_NODISCARD auto cend() const noexcept {
662 return const_iterator(m_pData, capacity(), size());
663 }
664
665 GAIA_NODISCARD auto rend() noexcept {
666 return iterator(m_pData, capacity(), -1);
667 }
668
669 GAIA_NODISCARD auto rend() const noexcept {
670 return const_iterator(m_pData, capacity(), -1);
671 }
672
673 GAIA_NODISCARD auto crend() const noexcept {
674 return const_iterator(m_pData, capacity(), -1);
675 }
676
677 GAIA_NODISCARD bool operator==(const darr_soa& other) const noexcept {
678 if (m_cnt != other.m_cnt)
679 return false;
680 const size_type n = size();
681 for (size_type i = 0; i < n; ++i)
682 if (!(operator[](i) == other[i]))
683 return false;
684 return true;
685 }
686
687 GAIA_NODISCARD constexpr bool operator!=(const darr_soa& other) const noexcept {
688 return !operator==(other);
689 }
690
691 template <size_t Item>
692 auto view_mut() noexcept {
693 return mem::data_view_policy<T::gaia_Data_Layout, T>::template set<Item>(
694 std::span<uint8_t>{GAIA_ACC((uint8_t*)m_pData), capacity()});
695 }
696
697 template <size_t Item>
698 auto view() const noexcept {
699 return mem::data_view_policy<T::gaia_Data_Layout, T>::template get<Item>(
700 std::span<const uint8_t>{GAIA_ACC((const uint8_t*)m_pData), capacity()});
701 }
702 };
703 } // namespace cnt
704
705} // 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:481
auto retain(Func &&func) noexcept
Removes all elements that fail the predicate.
Definition darray_soa_impl.h:565
iterator erase(iterator first, iterator last) noexcept
Removes the elements in the range [first, last)
Definition darray_soa_impl.h:522
iterator erase(iterator pos) noexcept
Removes the element at pos.
Definition darray_soa_impl.h:501
iterator insert(iterator pos, const T &arg)
Insert the element to the position given by iterator pos.
Definition darray_soa_impl.h:460
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