Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
paged_storage.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstddef>
5#include <cstdint>
6#include <initializer_list>
7#include <type_traits>
8#include <utility>
9
10#include "gaia/cnt/bitset.h"
11#include "gaia/cnt/darray.h"
12#include "gaia/core/iterator.h"
13#include "gaia/core/utility.h"
14#include "gaia/mem/data_layout_policy.h"
15#include "gaia/mem/mem_alloc.h"
16#include "gaia/mem/mem_utils.h"
17#include "gaia/mem/paged_allocator.h"
18#include "gaia/mem/raw_data_holder.h"
19
20namespace gaia {
21 namespace cnt {
23 using page_storage_id = uint32_t;
24
26 namespace detail {
27 using difference_type = uint32_t;
28 using size_type = uint32_t;
29
30 constexpr static page_storage_id InvalidPageStorageId = (page_storage_id)-1;
31
32 template <typename T>
33 struct mem_page_data;
34 template <typename T, typename Allocator>
35 class mem_page;
36 } // namespace detail
38
42 template <typename T>
47 static page_storage_id get(const T& item) noexcept {
48 (void)item;
49 static_assert(
50 std::is_empty_v<T>,
51 "Sparse_storage items require a conversion function to be defined in gaia::cnt namespace");
52 return detail::InvalidPageStorageId;
53 }
54 };
55
57 namespace detail {
58 template <typename T, typename Allocator, bool IsFwd>
59 struct mem_page_iterator {
60 using value_type = T;
61 using pointer = T*;
62 using reference = T&;
63 using difference_type = detail::difference_type;
64 using size_type = detail::size_type;
65 using iterator = mem_page_iterator;
66 using iterator_category = core::bidirectional_iterator_tag;
67
68 private:
69 using page_data_type = detail::mem_page_data<T>;
70 using page_type = detail::mem_page<T, Allocator>;
71 using bit_set_iter_type = std::conditional_t<
72 IsFwd, typename page_data_type::bit_set::iter, typename page_data_type::bit_set::iter_rev>;
73
74 page_type* m_pPage = nullptr;
75 bit_set_iter_type m_it;
76
77 public:
78 mem_page_iterator() = default;
79 mem_page_iterator(page_type* pPage): m_pPage(pPage) {}
80 mem_page_iterator(page_type* pPage, bit_set_iter_type it): m_pPage(pPage), m_it(it) {}
81
82 reference operator*() const {
83 return m_pPage->set_data(*m_it);
84 }
85 pointer operator->() const {
86 return &m_pPage->set_data(*m_it);
87 }
88
89 iterator& operator++() {
90 ++m_it;
91 return *this;
92 }
93 iterator operator++(int) {
94 iterator temp(*this);
95 ++*this;
96 return temp;
97 }
98
99 GAIA_NODISCARD bool operator==(const iterator& other) const {
100 return m_pPage == other.m_pPage && m_it == other.m_it;
101 }
102 GAIA_NODISCARD bool operator!=(const iterator& other) const {
103 return !(*this == other);
104 }
105 };
106
107 template <typename T, typename Allocator, bool IsFwd>
108 struct mem_page_iterator_soa {
109 using value_type = T;
110 // using pointer = T*; not supported
111 // using reference = T&; not supported
112 using difference_type = detail::difference_type;
113 using size_type = detail::size_type;
114 using iterator = mem_page_iterator_soa;
115 using iterator_category = core::bidirectional_iterator_tag;
116
117 private:
118 using page_data_type = detail::mem_page_data<T>;
119 using page_type = detail::mem_page<T, Allocator>;
120 using bit_set_iter_type = std::conditional_t<
121 IsFwd, typename page_data_type::bit_set::iter, typename page_data_type::bit_set::iter_rev>;
122
123 page_type* m_pPage = nullptr;
124 bit_set_iter_type m_it;
125
126 public:
128 mem_page_iterator_soa() = default;
129
133 mem_page_iterator_soa(page_type* pPage, bit_set_iter_type it): m_pPage(pPage), m_it(it) {}
134
135 value_type operator*() const {
136 return m_pPage->set_data(*m_it);
137 }
138 value_type operator->() const {
139 return m_pPage->set_data(*m_it);
140 }
141
142 iterator& operator++() {
143 ++m_it;
144 return *this;
145 }
146 iterator operator++(int) {
147 iterator temp(*this);
148 ++*this;
149 return temp;
150 }
151 iterator& operator--() {
152 --m_it;
153 return *this;
154 }
155 iterator operator--(int) {
156 iterator temp(*this);
157 --*this;
158 return temp;
159 }
160
161 GAIA_NODISCARD bool operator==(const iterator& other) const {
162 return m_pPage == other.m_pPage && m_it == other.m_it;
163 }
164 GAIA_NODISCARD bool operator!=(const iterator& other) const {
165 return !(*this == other);
166 }
167 };
168
169 template <typename T>
170 struct mem_page_data {
171 static constexpr uint32_t PageCapacity = 4096;
172
173 using view_policy = mem::auto_view_policy<T>;
174 using bit_set = cnt::bitset<PageCapacity>;
175 static constexpr uint32_t AllocatedBytes = view_policy::get_min_byte_size(0, PageCapacity);
176
177 struct PageHeader {
179 size_type cnt = size_type(0);
181 bit_set mask;
182 };
183
185 PageHeader header;
187 mem::raw_data_holder<T, AllocatedBytes> data;
188 };
189
190 template <typename T, typename Allocator>
191 class mem_page {
192 static_assert(!std::is_empty_v<T>, "It only makes sense to use page storage for data types with non-zero size");
193
194 public:
195 using value_type = T;
196 using reference = T&;
197 using const_reference = const T&;
198 using pointer = T*;
199 using const_pointer = const T*;
200 using view_policy = mem::auto_view_policy<T>;
201 using difference_type = detail::difference_type;
202 using size_type = detail::size_type;
203
204 template <bool IsFwd>
205 using iterator_base = mem_page_iterator<T, Allocator, IsFwd>;
206 using iterator = iterator_base<true>;
207 using iterator_reverse = iterator_base<false>;
208
209 template <bool IsFwd>
210 using iterator_soa_base = mem_page_iterator_soa<T, Allocator, IsFwd>;
211 using iterator_soa = iterator_soa_base<true>;
212 using iterator_soa_reverse = iterator_soa_base<false>;
213
214 using PageData = mem_page_data<T>;
215 static constexpr uint32_t PageCapacity = PageData::PageCapacity;
216
217 private:
219 PageData* m_pData = nullptr;
220
221 void ensure() {
222 if GAIA_LIKELY (m_pData != nullptr)
223 return;
224
225 // Allocate memory for data
226 m_pData = mem::AllocHelper::alloc<PageData, Allocator>(1);
227 (void)new (m_pData) PageData{};
228 // Prepare the item data region
229 core::call_ctor_raw_n(data(), PageCapacity);
230 }
231
232 void dtr_data_inter(uint32_t idx) noexcept {
233 GAIA_ASSERT(!empty());
234
235 if constexpr (!mem::is_soa_layout_v<T>) {
236 auto* ptr = &data()[idx];
237 core::call_dtor(ptr);
238 }
239
240 m_pData->header.mask.set(idx, false);
241 --m_pData->header.cnt;
242 }
243
244 void dtr_active_data() noexcept {
245 if constexpr (!mem::is_soa_layout_v<T>) {
246 for (auto i: m_pData->header.mask) {
247 auto* ptr = &data()[i];
248 core::call_dtor(ptr);
249 }
250 }
251 }
252
253 void invalidate() {
254 if (m_pData == nullptr)
255 return;
256
257 // Destruct active items
258 dtr_active_data();
259
260 // Release allocated memory
261 m_pData->~PageData();
262 mem::AllocHelper::free<Allocator>(m_pData);
263 m_pData = nullptr;
264 }
265
266 public:
267 mem_page() = default;
268
269 mem_page(const mem_page& other) {
270 // Copy new items over
271 if (other.m_pData == nullptr) {
272 invalidate();
273 } else {
274 ensure();
275
276 m_pData->header.mask = other.m_pData->header.mask;
277 m_pData->header.cnt = other.m_pData->header.cnt;
278
279 // Copy construct data
280 for (auto i: other.m_pData->header.mask)
281 add_data(i, other.get_data(i));
282 }
283 }
284
285 mem_page& operator=(const mem_page& other) {
286 GAIA_ASSERT(core::addressof(other) != this);
287
288 // Copy new items over if there are any
289 if (other.m_pData == nullptr) {
290 invalidate();
291 } else {
292 ensure();
293
294 // Remove current active items
295 if (m_pData != nullptr)
296 dtr_active_data();
297
298 m_pData->header.mask = other.m_pData->header.mask;
299 m_pData->header.cnt = other.m_pData->header.cnt;
300
301 // Copy data
302 for (auto i: other.m_pData->header.mask)
303 add_data(i, other.get_data(i));
304 }
305
306 return *this;
307 }
308
309 mem_page(mem_page&& other) noexcept {
310 m_pData = other.m_pData;
311 other.m_pData = nullptr;
312 }
313
314 mem_page& operator=(mem_page&& other) noexcept {
315 GAIA_ASSERT(core::addressof(other) != this);
316
317 invalidate();
318
319 m_pData = other.m_pData;
320 other.m_pData = nullptr;
321
322 return *this;
323 }
324
325 ~mem_page() {
326 invalidate();
327 }
328
329 GAIA_CLANG_WARNING_PUSH()
330 // Memory is aligned so we can silence this warning
331 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
332
333 GAIA_NODISCARD pointer data() noexcept {
334 return GAIA_ACC((pointer)&m_pData->data[0]);
335 }
336
337 GAIA_NODISCARD const_pointer data() const noexcept {
338 return GAIA_ACC((const_pointer)&m_pData->data[0]);
339 }
340
341 GAIA_NODISCARD decltype(auto) set_data(size_type pos) noexcept {
342 GAIA_ASSERT(m_pData->header.mask.test(pos));
343 return view_policy::set(
344 {GAIA_ACC((typename view_policy::TargetCastType) & m_pData->data[0]), PageCapacity}, pos);
345 }
346
347 GAIA_NODISCARD decltype(auto) operator[](size_type pos) noexcept {
348 GAIA_ASSERT(m_pData->header.mask.test(pos));
349 return view_policy::set(
350 {GAIA_ACC((typename view_policy::TargetCastType) & m_pData->data[0]), PageCapacity}, pos);
351 }
352
353 GAIA_NODISCARD decltype(auto) get_data(size_type pos) const noexcept {
354 GAIA_ASSERT(m_pData->header.mask.test(pos));
355 return view_policy::get(
356 {GAIA_ACC((typename view_policy::TargetCastType) & m_pData->data[0]), PageCapacity}, pos);
357 }
358
359 GAIA_NODISCARD decltype(auto) operator[](size_type pos) const noexcept {
360 GAIA_ASSERT(m_pData->header.mask.test(pos));
361 return view_policy::get(
362 {GAIA_ACC((typename view_policy::TargetCastType) & m_pData->data[0]), PageCapacity}, pos);
363 }
364
365 GAIA_CLANG_WARNING_POP()
366
367 void add() {
368 ensure();
369 ++m_pData->header.cnt;
370 }
371
372 decltype(auto) add_data(uint32_t idx, const T& arg) {
373 m_pData->header.mask.set(idx);
374
375 if constexpr (mem::is_soa_layout_v<T>) {
376 set_data(idx) = arg;
377 } else {
378 auto* ptr = &set_data(idx);
379 core::call_ctor(ptr, arg);
380 return (reference)(*ptr);
381 }
382 }
383
384 decltype(auto) add_data(uint32_t idx, T&& arg) {
385 m_pData->header.mask.set(idx);
386
387 if constexpr (mem::is_soa_layout_v<T>) {
388 set_data(idx) = GAIA_MOV(arg);
389 } else {
390 auto* ptr = &set_data(idx);
391 core::call_ctor(ptr, GAIA_MOV(arg));
392 return (reference)(*ptr);
393 }
394 }
395
396 template <typename... Args>
397 decltype(auto) emplace_data(uint32_t idx, Args&&... args) {
398 m_pData->header.used.set(idx);
399
400 if constexpr (mem::is_soa_layout_v<T>) {
401 set_data(idx) = T(GAIA_FWD(args)...);
402 } else {
403 auto* ptr = &set_data(idx);
404 core::call_ctor(ptr, GAIA_FWD(args)...);
405 return (reference)(*ptr);
406 }
407 }
408
409 void del_data(uint32_t idx) noexcept {
410 dtr_data_inter(idx);
411
412 // If there is no more data, release the memory allocated by the page
413 if (m_pData->header.cnt == 0)
414 invalidate();
415 }
416
417 GAIA_NODISCARD bool has_data(uint32_t idx) const noexcept {
418 return m_pData ? m_pData->header.mask.test(idx) : false;
419 }
420
421 GAIA_NODISCARD size_type size() const noexcept {
422 return m_pData ? m_pData->header.cnt : 0;
423 }
424
425 GAIA_NODISCARD bool empty() const noexcept {
426 return size() == 0;
427 }
428
429 GAIA_NODISCARD decltype(auto) front() noexcept {
430 GAIA_ASSERT(!empty());
431 if constexpr (mem::is_soa_layout_v<T>)
432 return *begin();
433 else
434 return (reference)*begin();
435 }
436
437 GAIA_NODISCARD decltype(auto) front() const noexcept {
438 GAIA_ASSERT(!empty());
439 if constexpr (mem::is_soa_layout_v<T>)
440 return *begin();
441 else
442 return (const_reference)*begin();
443 }
444
445 GAIA_NODISCARD decltype(auto) back() noexcept {
446 GAIA_ASSERT(!empty());
447 const auto idx = *m_pData->header.mask.rbegin();
448 if constexpr (mem::is_soa_layout_v<T>)
449 return set_data(idx);
450 else
451 return (reference)(set_data(idx));
452 }
453
454 GAIA_NODISCARD decltype(auto) back() const noexcept {
455 GAIA_ASSERT(!empty());
456 const auto idx = *m_pData->header.mask.rbegin();
457 if constexpr (mem::is_soa_layout_v<T>)
458 return set_data(idx);
459 else
460 return (const_reference)set_data(idx);
461 }
462
463 static constexpr typename PageData::bit_set s_dummyBitSet{};
464
465 GAIA_NODISCARD auto begin() const noexcept {
466 if constexpr (mem::is_soa_layout_v<T>)
467 return iterator_soa((mem_page*)this, m_pData ? m_pData->header.mask.begin() : s_dummyBitSet.begin());
468 else
469 return iterator((mem_page*)this, m_pData ? m_pData->header.mask.begin() : s_dummyBitSet.begin());
470 }
471
472 GAIA_NODISCARD auto end() const noexcept {
473 if constexpr (mem::is_soa_layout_v<T>)
474 return iterator_soa((mem_page*)this, m_pData ? m_pData->header.mask.end() : s_dummyBitSet.end());
475 else
476 return iterator((mem_page*)this, m_pData ? m_pData->header.mask.end() : s_dummyBitSet.end());
477 }
478
479 GAIA_NODISCARD auto rbegin() const noexcept {
480 if constexpr (mem::is_soa_layout_v<T>)
481 return iterator_soa_reverse(
482 (mem_page*)this, m_pData ? m_pData->header.mask.rbegin() : s_dummyBitSet.rbegin());
483 else
484 return iterator_reverse((mem_page*)this, m_pData ? m_pData->header.mask.rbegin() : s_dummyBitSet.rbegin());
485 }
486
487 GAIA_NODISCARD auto rend() const noexcept {
488 if constexpr (mem::is_soa_layout_v<T>)
489 return iterator_soa_reverse((mem_page*)this, m_pData ? m_pData->header.mask.rend() : s_dummyBitSet.rend());
490 else
491 return iterator_reverse((mem_page*)this, m_pData ? m_pData->header.mask.rend() : s_dummyBitSet.rend());
492 }
493
494 GAIA_NODISCARD bool operator==(const mem_page& other) const noexcept {
495 // We expect to compare only valid pages
496 GAIA_ASSERT(m_pData != nullptr);
497 GAIA_ASSERT(other.m_pData != nullptr);
498
499 if (m_pData->header.cnt != other.m_pData->header.cnt)
500 return false;
501 if (m_pData->header.mask != other.m_pData->header.mask)
502 return false;
503 for (auto i: m_pData->header.mask)
504 if (!(get_data(i) == other[i]))
505 return false;
506 return true;
507 }
508
509 GAIA_NODISCARD bool operator!=(const mem_page& other) const noexcept {
510 return !operator==(other);
511 }
512 };
513 } // namespace detail
515
520 template <typename T, typename Allocator, bool IsFwd>
523 using value_type = T;
525 using pointer = T*;
527 using reference = T&;
529 using difference_type = detail::difference_type;
531 using size_type = detail::size_type;
536
537 private:
538 using page_type = detail::mem_page<T, Allocator>;
539
540 page_type* m_pPage;
541 page_type* m_pPageLast;
542 typename page_type::template iterator_base<IsFwd> m_it;
543
544 public:
547 page_iterator(page_type* pPage): m_pPage(pPage), m_pPageLast(pPage) {}
548
552 page_iterator(page_type* pPage, page_type* pPageLast): m_pPage(pPage), m_pPageLast(pPageLast) {
553 // Find first page with data
554 if constexpr (!IsFwd) {
555 m_it = m_pPage->rbegin();
556 while (m_it == m_pPage->rend()) {
557 if (m_pPage == m_pPageLast) {
558 m_it = {};
559 break;
560 }
561 --m_pPage;
562 m_it = m_pPage->rbegin();
563 }
564 } else {
565 m_it = m_pPage->begin();
566 while (m_it == m_pPage->end()) {
567 ++m_pPage;
568 if (m_pPage == m_pPageLast) {
569 m_it = {};
570 break;
571 }
572 m_it = m_pPage->begin();
573 }
574 }
575 }
576
580 return m_it.operator*();
581 }
585 return m_it.operator->();
586 }
587
591 if constexpr (!IsFwd) {
592 ++m_it;
593 if (m_it == m_pPage->rend()) {
594 do {
595 if (m_pPage == m_pPageLast) {
596 m_it = {};
597 return *this;
598 }
599 --m_pPage;
600 m_it = m_pPage->rbegin();
601 } while (m_it == m_pPage->rend());
602 }
603 } else {
604 ++m_it;
605 if (m_it == m_pPage->end()) {
606 do {
607 ++m_pPage;
608 if (m_pPage == m_pPageLast) {
609 m_it = {};
610 return *this;
611 }
612 m_it = m_pPage->begin();
613 } while (m_it == m_pPage->end());
614 }
615 }
616 return *this;
617 }
621 iterator temp(*this);
622 ++*this;
623 return temp;
624 }
625
629 GAIA_NODISCARD bool operator==(const iterator& other) const {
630 return m_pPage == other.m_pPage && m_it == other.m_it;
631 }
635 GAIA_NODISCARD bool operator!=(const iterator& other) const {
636 return m_pPage != other.m_pPage || m_it != other.m_it;
637 }
638 };
639
644 template <typename T, typename Allocator, bool IsFwd>
647 using value_type = T;
649 using pointer = const T*;
651 using reference = const T&;
653 using difference_type = detail::difference_type;
655 using size_type = detail::size_type;
660
661 private:
662 using page_type = detail::mem_page<T, Allocator>;
663
664 const page_type* m_pPage;
665 const page_type* m_pPageLast;
666 typename page_type::template iterator_base<IsFwd> m_it;
667
668 public:
671 const_page_iterator(const page_type* pPage): m_pPage(pPage), m_pPageLast(pPage) {}
672
676 const_page_iterator(const page_type* pPage, const page_type* pPageLast): m_pPage(pPage), m_pPageLast(pPageLast) {
677 // Find first page with data
678 if constexpr (!IsFwd) {
679 m_it = m_pPage->rbegin();
680 while (m_it == m_pPage->rend()) {
681 if (m_pPage == m_pPageLast) {
682 m_it = {};
683 break;
684 }
685 --m_pPage;
686 m_it = m_pPage->rbegin();
687 }
688 } else {
689 m_it = m_pPage->begin();
690 while (m_it == m_pPage->end()) {
691 ++m_pPage;
692 if (m_pPage == m_pPageLast) {
693 m_it = {};
694 break;
695 }
696 m_it = m_pPage->begin();
697 }
698 }
699 }
700
704 return m_it.operator*();
705 }
709 return m_it.operator->();
710 }
711
715 if constexpr (!IsFwd) {
716 ++m_it;
717 if (m_it == m_pPage->rend()) {
718 do {
719 if (m_pPage == m_pPageLast) {
720 m_it = {};
721 return *this;
722 }
723 --m_pPage;
724 m_it = m_pPage->rbegin();
725 } while (m_it == m_pPage->rend());
726 }
727 } else {
728 ++m_it;
729 if (m_it == m_pPage->end()) {
730 do {
731 ++m_pPage;
732 if (m_pPage == m_pPageLast) {
733 m_it = {};
734 return *this;
735 }
736 m_it = m_pPage->begin();
737 } while (m_it == m_pPage->end());
738 }
739 }
740 return *this;
741 }
745 iterator temp(*this);
746 ++*this;
747 return temp;
748 }
749
753 GAIA_NODISCARD bool operator==(const iterator& other) const {
754 return m_pPage == other.m_pPage && m_it == other.m_it;
755 }
759 GAIA_NODISCARD bool operator!=(const iterator& other) const {
760 return m_pPage != other.m_pPage || m_it != other.m_it;
761 }
762 };
763
771 template <typename T, typename Allocator, bool IsFwd>
774 using value_type = T;
775 // using pointer = T*;
776 // using reference = T&;
778 using difference_type = detail::difference_type;
780 using size_type = detail::size_type;
785
786 private:
787 using page_type = detail::mem_page<T, Allocator>;
788
789 page_type* m_pPage;
790 page_type* m_pPageLast;
791 typename page_type::template iterator_soa_base<IsFwd> m_it;
792
793 public:
796 page_iterator_soa(page_type* pPage): m_pPage(pPage), m_pPageLast(pPage) {}
797
801 page_iterator_soa(page_type* pPage, page_type* pPageLast): m_pPage(pPage), m_pPageLast(pPageLast) {
802 // Find first page with data
803 if constexpr (!IsFwd) {
804 m_it = m_pPage->rbegin();
805 while (m_it == m_pPage->rend()) {
806 if (m_pPage == m_pPageLast) {
807 m_it = {};
808 break;
809 }
810 --m_pPage;
811 m_it = m_pPage->rbegin();
812 }
813 } else {
814 m_it = m_pPage->begin();
815 while (m_it == m_pPage->end()) {
816 ++m_pPage;
817 if (m_pPage == m_pPageLast) {
818 m_it = {};
819 break;
820 }
821 m_it = m_pPage->begin();
822 }
823 }
824 }
825
829 return m_it.operator*();
830 }
834 return m_it.operator->();
835 }
836
840 if constexpr (!IsFwd) {
841 ++m_it;
842 if (m_it == m_pPage->rend()) {
843 do {
844 if (m_pPage == m_pPageLast) {
845 m_it = {};
846 return *this;
847 }
848 --m_pPage;
849 m_it = m_pPage->rbegin();
850 } while (m_it == m_pPage->rend());
851 }
852 } else {
853 ++m_it;
854 if (m_it == m_pPage->end()) {
855 do {
856 ++m_pPage;
857 if (m_pPage == m_pPageLast) {
858 m_it = {};
859 return *this;
860 }
861 m_it = m_pPage->begin();
862 } while (m_it == m_pPage->end());
863 }
864 }
865 return *this;
866 }
870 iterator temp(*this);
871 ++*this;
872 return temp;
873 }
874
878 GAIA_NODISCARD bool operator==(const iterator& other) const {
879 return m_pPage == other.m_pPage && m_it == other.m_it;
880 }
884 GAIA_NODISCARD bool operator!=(const iterator& other) const {
885 return m_pPage != other.m_pPage || m_it != other.m_it;
886 }
887 };
888
896 template <typename T, typename Allocator, bool IsFwd>
899 using value_type = T;
900 // using pointer = T*;
901 // using reference = T&;
903 using difference_type = detail::difference_type;
905 using size_type = detail::size_type;
910
911 private:
912 using page_type = detail::mem_page<T, Allocator>;
913
914 const page_type* m_pPage;
915 const page_type* m_pPageLast;
916 typename page_type::template iterator_soa_base<IsFwd> m_it;
917
918 public:
921 const_page_iterator_soa(const page_type* pPage): m_pPage(pPage), m_pPageLast(pPage) {}
922
926 const_page_iterator_soa(const page_type* pPage, const page_type* pPageLast):
927 m_pPage(pPage), m_pPageLast(pPageLast) {
928 // Find first page with data
929 if constexpr (!IsFwd) {
930 m_it = m_pPage->rbegin();
931 while (m_it == m_pPage->rend()) {
932 if (m_pPage == m_pPageLast) {
933 m_it = {};
934 break;
935 }
936 --m_pPage;
937 m_it = m_pPage->rbegin();
938 }
939 } else {
940 m_it = m_pPage->begin();
941 while (m_it == m_pPage->end()) {
942 ++m_pPage;
943 if (m_pPage == m_pPageLast) {
944 m_it = {};
945 break;
946 }
947 m_it = m_pPage->begin();
948 }
949 }
950 }
951
955 return m_it.operator*();
956 }
960 return m_it.operator->();
961 }
962
966 if constexpr (!IsFwd) {
967 ++m_it;
968 if (m_it == m_pPage->rend()) {
969 do {
970 if (m_pPage == m_pPageLast) {
971 m_it = {};
972 return *this;
973 }
974 --m_pPage;
975 m_it = m_pPage->rbegin();
976 } while (m_it == m_pPage->rend());
977 }
978 } else {
979 ++m_it;
980 if (m_it == m_pPage->end()) {
981 do {
982 ++m_pPage;
983 if (m_pPage == m_pPageLast) {
984 m_it = {};
985 return *this;
986 }
987 m_it = m_pPage->begin();
988 } while (m_it == m_pPage->end());
989 }
990 }
991 return *this;
992 }
996 iterator temp(*this);
997 ++*this;
998 return temp;
999 }
1000
1004 GAIA_NODISCARD bool operator==(const iterator& other) const {
1005 return m_pPage == other.m_pPage && m_it == other.m_it;
1006 }
1010 GAIA_NODISCARD bool operator!=(const iterator& other) const {
1011 return m_pPage != other.m_pPage || m_it != other.m_it;
1012 }
1013 };
1014
1017 template <typename T>
1019 public:
1021 using value_type = T;
1023 using reference = T&;
1025 using const_reference = const T&;
1027 using pointer = T*;
1029 using const_pointer = const T*;
1033 using difference_type = detail::difference_type;
1035 using size_type = detail::size_type;
1036
1038 static constexpr uint32_t AllocatorBlockSize = (uint32_t)sizeof(detail::mem_page_data<T>);
1041
1043 using page_data_type = detail::mem_page_data<T>;
1045 using page_type = detail::mem_page<T, Allocator>;
1047 static constexpr uint32_t PageCapacity = page_type::PageCapacity;
1048
1066 using iterator = std::conditional_t<mem::is_soa_layout_v<T>, iterator_soa, iterator_aos>;
1069 std::conditional_t<mem::is_soa_layout_v<T>, iterator_soa_reverse, iterator_aos_reverse>;
1071 using const_iterator = std::conditional_t<mem::is_soa_layout_v<T>, const_iterator_soa, const_iterator_aos>;
1074 std::conditional_t<mem::is_soa_layout_v<T>, const_iterator_soa_reverse, const_iterator_aos_reverse>;
1077
1078 private:
1079 constexpr static uint32_t PageMask = PageCapacity - 1;
1080 constexpr static uint32_t ToPageIndex = core::count_bits(PageMask);
1081
1083 cnt::darray<page_type> m_pages;
1085 uint32_t m_itemCnt = 0;
1086
1087 void try_grow(uint32_t pid) {
1088 // The page array has to be able to take any page index
1089 if (pid >= m_pages.size())
1090 m_pages.resize(pid + 1, ResizePolicy::Scale);
1091
1092 m_pages[pid].add();
1093 ++m_itemCnt;
1094 }
1095
1096 public:
1097 constexpr page_storage() noexcept = default;
1098
1102 m_pages = other.m_pages;
1103 m_itemCnt = other.m_itemCnt;
1104 }
1105
1110 GAIA_ASSERT(core::addressof(other) != this);
1111
1112 m_pages = other.m_pages;
1113 m_itemCnt = other.m_itemCnt;
1114 return *this;
1115 }
1116
1120 m_pages = GAIA_MOV(other.m_pages);
1121 m_itemCnt = other.m_itemCnt;
1122
1123 other.m_pages = {};
1124 }
1125
1130 GAIA_ASSERT(core::addressof(other) != this);
1131
1132 m_pages = GAIA_MOV(other.m_pages);
1133 m_itemCnt = other.m_itemCnt;
1134
1135 other.m_pages = {};
1136
1137 return *this;
1138 }
1139
1140 ~page_storage() = default;
1141
1142 GAIA_CLANG_WARNING_PUSH()
1143 // Memory is aligned so we can silence this warning
1144 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
1145
1150 GAIA_ASSERT(has(id));
1151 const auto pid = size_type(id >> ToPageIndex);
1152 const auto did = size_type(id & PageMask);
1153 auto& page = m_pages[pid];
1154 return view_policy::set({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
1155 }
1156
1160 GAIA_NODISCARD decltype(auto) operator[](page_storage_id id) const noexcept {
1161 GAIA_ASSERT(has(id));
1162 const auto pid = size_type(id >> ToPageIndex);
1163 const auto did = size_type(id & PageMask);
1164 auto& page = m_pages[pid];
1165 return view_policy::get({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
1166 }
1167
1168 GAIA_CLANG_WARNING_POP()
1169
1170
1173 GAIA_NODISCARD bool has(page_storage_id id) const noexcept {
1174 const auto pid = size_type(id >> ToPageIndex);
1175 if (pid >= m_pages.size())
1176 return false;
1177
1178 const auto did = size_type(id & PageMask);
1179 const auto val = page_data_type::bit_set::BitCount;
1180 return did < val && m_pages[pid].has_data(did);
1181 }
1182
1186 GAIA_NODISCARD bool has(const T& arg) const noexcept {
1187 const auto id = to_page_storage_id<T>::get(arg);
1188 GAIA_ASSERT(id != detail::InvalidPageStorageId);
1189 return has(id);
1190 }
1191
1196 template <typename TType>
1197 decltype(auto) add(TType&& arg) {
1198 const auto id = to_page_storage_id<T>::get(arg);
1199 if (has(id)) {
1200 if constexpr (mem::is_soa_layout_v<T>)
1201 return;
1202 else {
1203 const auto pid = size_type(id >> ToPageIndex);
1204 const auto did = size_type(id & PageMask);
1205 auto& page = m_pages[pid];
1206 return page.set_data(did);
1207 }
1208 }
1209
1210 const auto pid = size_type(id >> ToPageIndex);
1211 const auto did = size_type(id & PageMask);
1212
1213 try_grow(pid);
1214
1215 auto& page = m_pages[pid];
1216 if constexpr (mem::is_soa_layout_v<T>)
1217 page.add_data(did, GAIA_FWD(arg));
1218 else
1219 return page.add_data(did, GAIA_FWD(arg));
1220 }
1221
1225 decltype(auto) set(page_storage_id id) {
1226 GAIA_ASSERT(has(id));
1227
1228 const auto pid = uint32_t(id >> ToPageIndex);
1229 const auto did = uint32_t(id & PageMask);
1230
1231 auto& page = m_pages[pid];
1232 return page.set_data(did);
1233 }
1234
1237 void del(page_storage_id id) noexcept {
1238 GAIA_ASSERT(!empty());
1239 GAIA_ASSERT(id != detail::InvalidPageStorageId);
1240
1241 if (!has(id))
1242 return;
1243
1244 const auto pid = uint32_t(id >> ToPageIndex);
1245 const auto did = uint32_t(id & PageMask);
1246
1247 auto& page = m_pages[pid];
1248 page.del_data(did);
1249 --m_itemCnt;
1250 }
1251
1254 void del(const T& arg) noexcept {
1255 const auto id = to_page_storage_id<T>::get(arg);
1256 return del(id);
1257 }
1258
1260 void clear() {
1261 m_pages.resize(0);
1262 m_itemCnt = 0;
1263 }
1264
1267 GAIA_NODISCARD size_type size() const noexcept {
1268 return m_itemCnt;
1269 }
1270
1273 GAIA_NODISCARD bool empty() const noexcept {
1274 return m_itemCnt == 0;
1275 }
1276
1279 GAIA_NODISCARD decltype(auto) front() noexcept {
1280 GAIA_ASSERT(!empty());
1281 return *begin();
1282 }
1283
1286 GAIA_NODISCARD decltype(auto) front() const noexcept {
1287 GAIA_ASSERT(!empty());
1288 return *begin();
1289 }
1290
1293 GAIA_NODISCARD decltype(auto) back() noexcept {
1294 GAIA_ASSERT(!empty());
1295 return *rbegin();
1296 }
1297
1300 GAIA_NODISCARD decltype(auto) back() const noexcept {
1301 GAIA_ASSERT(!empty());
1302 return *rbegin();
1303 }
1304
1307 GAIA_NODISCARD auto begin() noexcept {
1308 GAIA_ASSERT(!empty());
1309 return iterator(m_pages.data(), m_pages.data() + m_pages.size());
1310 }
1311
1314 GAIA_NODISCARD auto begin() const noexcept {
1315 GAIA_ASSERT(!empty());
1316 return const_iterator(m_pages.data(), m_pages.data() + m_pages.size());
1317 }
1318
1321 GAIA_NODISCARD auto cbegin() const noexcept {
1322 GAIA_ASSERT(!empty());
1323 return const_iterator(m_pages.data(), m_pages.data() + m_pages.size());
1324 }
1325
1328 GAIA_NODISCARD auto end() noexcept {
1329 GAIA_ASSERT(!empty());
1330 return iterator(m_pages.data() + m_pages.size());
1331 }
1332
1335 GAIA_NODISCARD auto end() const noexcept {
1336 GAIA_ASSERT(!empty());
1337 return const_iterator(m_pages.data() + m_pages.size());
1338 }
1339
1342 GAIA_NODISCARD auto cend() const noexcept {
1343 GAIA_ASSERT(!empty());
1344 return const_iterator(m_pages.data() + m_pages.size());
1345 }
1346
1349 GAIA_NODISCARD auto rbegin() noexcept {
1350 GAIA_ASSERT(!empty());
1351 return iterator_reverse(m_pages.data() + m_pages.size() - 1, m_pages.data());
1352 }
1353
1356 GAIA_NODISCARD auto rbegin() const noexcept {
1357 GAIA_ASSERT(!empty());
1358 return const_iterator_reverse(m_pages.data() + m_pages.size() - 1, m_pages.data());
1359 }
1360
1363 GAIA_NODISCARD auto crbegin() const noexcept {
1364 GAIA_ASSERT(!empty());
1365 return const_iterator_reverse(m_pages.data() + m_pages.size() - 1, m_pages.data());
1366 }
1367
1370 GAIA_NODISCARD auto rend() noexcept {
1371 GAIA_ASSERT(!empty());
1372 return iterator_reverse(m_pages.data());
1373 }
1374
1377 GAIA_NODISCARD auto rend() const noexcept {
1378 GAIA_ASSERT(!empty());
1379 return const_iterator_reverse(m_pages.data());
1380 }
1381
1384 GAIA_NODISCARD auto crend() const noexcept {
1385 GAIA_ASSERT(!empty());
1386 return const_iterator_reverse(m_pages.data());
1387 }
1388
1392 GAIA_NODISCARD bool operator==(const page_storage& other) const noexcept {
1393 return m_pages == other.m_pages;
1394 }
1395
1399 GAIA_NODISCARD bool operator!=(const page_storage& other) const noexcept {
1400 return !operator==(other);
1401 }
1402 };
1403 } // namespace cnt
1404
1405} // namespace gaia
Array of elements of type.
Definition darray_ext_impl.h:31
GAIA_NODISCARD size_type size() const noexcept
Returns the number of elements.
Definition darray_ext_impl.h:561
GAIA_NODISCARD auto rbegin() noexcept
Returns a reverse traversal iterator to the last element.
Definition darray_ext_impl.h:631
GAIA_NODISCARD auto begin() noexcept
Returns an iterator to the first element.
Definition darray_ext_impl.h:613
void resize(size_type count, ResizePolicy policy=ResizePolicy::Exact)
Changes the number of elements.
Definition darray_ext_impl.h:289
GAIA_NODISCARD pointer data() noexcept
Returns a pointer to the element storage.
Definition darray_ext_impl.h:236
Heap-allocated paged storage for elements of type T.
Definition paged_storage.h:1018
GAIA_NODISCARD bool operator==(const page_storage &other) const noexcept
Checks whether two storages contain equal elements.
Definition paged_storage.h:1392
detail::mem_page< T, Allocator > page_type
Page type used by the storage.
Definition paged_storage.h:1045
GAIA_NODISCARD auto end() noexcept
Returns an iterator past the last stored element.
Definition paged_storage.h:1328
GAIA_NODISCARD auto rbegin() const noexcept
Returns a reverse iterator to the last stored element.
Definition paged_storage.h:1356
GAIA_NODISCARD decltype(auto) front() const noexcept
Accesses the first stored element.
Definition paged_storage.h:1286
GAIA_NODISCARD auto crend() const noexcept
Returns a read-only reverse iterator before the first stored element.
Definition paged_storage.h:1384
GAIA_NODISCARD auto cbegin() const noexcept
Returns a read-only iterator to the first stored element.
Definition paged_storage.h:1321
decltype(auto) add(TType &&arg)
Inserts the item arg into the storage.
Definition paged_storage.h:1197
GAIA_NODISCARD decltype(auto) back() const noexcept
Accesses the last stored element.
Definition paged_storage.h:1300
void clear()
Clears the storage.
Definition paged_storage.h:1260
void del(const T &arg) noexcept
Removes the item arg from the storage.
Definition paged_storage.h:1254
static constexpr uint32_t AllocatorBlockSize
Size in bytes of one allocator block.
Definition paged_storage.h:1038
GAIA_NODISCARD auto rend() noexcept
Returns a reverse iterator before the first stored element.
Definition paged_storage.h:1370
decltype(auto) set(page_storage_id id)
Accesses the record at the index id for update.
Definition paged_storage.h:1225
page_storage & operator=(page_storage &&other) noexcept
Move-assigns a storage.
Definition paged_storage.h:1129
page_iterator_soa< T, Allocator, false > iterator_soa_reverse
Reverse mutable iterator for SoA elements.
Definition paged_storage.h:1056
GAIA_NODISCARD size_type size() const noexcept
Returns the number of items inserted into the storage.
Definition paged_storage.h:1267
GAIA_NODISCARD auto begin() noexcept
Returns an iterator to the first stored element.
Definition paged_storage.h:1307
detail::difference_type difference_type
Type used for iterator distances.
Definition paged_storage.h:1033
static constexpr uint32_t PageCapacity
Maximum number of elements addressable in one page.
Definition paged_storage.h:1047
GAIA_NODISCARD auto rend() const noexcept
Returns a reverse iterator before the first stored element.
Definition paged_storage.h:1377
GAIA_NODISCARD auto rbegin() noexcept
Returns a reverse iterator to the last stored element.
Definition paged_storage.h:1349
detail::mem_page_data< T > page_data_type
Raw page-data type.
Definition paged_storage.h:1043
GAIA_NODISCARD bool empty() const noexcept
Checks if the storage is empty (no items inserted).
Definition paged_storage.h:1273
const_page_iterator_soa< T, Allocator, false > const_iterator_soa_reverse
Reverse read-only iterator for SoA elements.
Definition paged_storage.h:1064
std::conditional_t< mem::is_soa_layout_v< T >, const_iterator_soa, const_iterator_aos > const_iterator
Forward read-only iterator matching the element data layout.
Definition paged_storage.h:1071
std::conditional_t< mem::is_soa_layout_v< T >, iterator_soa_reverse, iterator_aos_reverse > iterator_reverse
Reverse mutable iterator matching the element data layout.
Definition paged_storage.h:1069
std::conditional_t< mem::is_soa_layout_v< T >, iterator_soa, iterator_aos > iterator
Forward mutable iterator matching the element data layout.
Definition paged_storage.h:1066
GAIA_NODISCARD bool has(const T &arg) const noexcept
Checks if an item arg exists within the storage.
Definition paged_storage.h:1186
GAIA_NODISCARD auto begin() const noexcept
Returns an iterator to the first stored element.
Definition paged_storage.h:1314
GAIA_NODISCARD decltype(auto) front() noexcept
Accesses the first stored element.
Definition paged_storage.h:1279
void del(page_storage_id id) noexcept
Removes the item at the index id from the storage.
Definition paged_storage.h:1237
std::conditional_t< mem::is_soa_layout_v< T >, const_iterator_soa_reverse, const_iterator_aos_reverse > const_iterator_reverse
Reverse read-only iterator matching the element data layout.
Definition paged_storage.h:1074
GAIA_NODISCARD auto cend() const noexcept
Returns a read-only iterator past the last stored element.
Definition paged_storage.h:1342
GAIA_NODISCARD auto end() const noexcept
Returns an iterator past the last stored element.
Definition paged_storage.h:1335
GAIA_NODISCARD bool operator!=(const page_storage &other) const noexcept
Checks whether two storages differ.
Definition paged_storage.h:1399
detail::size_type size_type
Type used for sizes and indices.
Definition paged_storage.h:1035
GAIA_NODISCARD auto crbegin() const noexcept
Returns a read-only reverse iterator to the last stored element.
Definition paged_storage.h:1363
page_iterator_soa< T, Allocator, true > iterator_soa
Forward mutable iterator for SoA elements.
Definition paged_storage.h:1054
GAIA_NODISCARD decltype(auto) back() noexcept
Accesses the last stored element.
Definition paged_storage.h:1293
page_storage & operator=(const page_storage &other)
Copy-assigns a storage.
Definition paged_storage.h:1109
const_page_iterator_soa< T, Allocator, true > const_iterator_soa
Forward read-only iterator for SoA elements.
Definition paged_storage.h:1062
page_storage(page_storage &&other) noexcept
Move-constructs a storage.
Definition paged_storage.h:1119
GAIA_NODISCARD bool has(page_storage_id id) const noexcept
Checks if an item with a given page id exists.
Definition paged_storage.h:1173
Gaia-ECS is a header-only library which means we want to avoid using global static variables because ...
Definition dyn_singleton.h:29
Read-only iterator over SoA elements in paged storage. SoA pages keep each member in a separate array...
Definition paged_storage.h:897
const_page_iterator_soa(const page_type *pPage)
Constructs an end iterator at a page boundary.
Definition paged_storage.h:921
iterator operator++(int)
Advances to the next element.
Definition paged_storage.h:995
detail::difference_type difference_type
Type used for iterator distances.
Definition paged_storage.h:903
detail::size_type size_type
Type used for sizes and indices.
Definition paged_storage.h:905
iterator & operator++()
Advances to the next element.
Definition paged_storage.h:965
GAIA_NODISCARD bool operator==(const iterator &other) const
Checks whether two iterators have the same position.
Definition paged_storage.h:1004
value_type operator*() const
Accesses the current element view.
Definition paged_storage.h:954
value_type operator->() const
Accesses the current element view through arrow syntax.
Definition paged_storage.h:959
const_page_iterator_soa(const page_type *pPage, const page_type *pPageLast)
Constructs a read-only iterator over a page range.
Definition paged_storage.h:926
GAIA_NODISCARD bool operator!=(const iterator &other) const
Checks whether two iterators have different positions.
Definition paged_storage.h:1010
Read-only iterator over elements in paged storage.
Definition paged_storage.h:645
iterator operator++(int)
Advances to the next element.
Definition paged_storage.h:744
reference operator*() const
Accesses the current element.
Definition paged_storage.h:703
GAIA_NODISCARD bool operator==(const iterator &other) const
Checks whether two iterators have the same position.
Definition paged_storage.h:753
GAIA_NODISCARD bool operator!=(const iterator &other) const
Checks whether two iterators have different positions.
Definition paged_storage.h:759
detail::difference_type difference_type
Type used for iterator distances.
Definition paged_storage.h:653
const_page_iterator(const page_type *pPage)
Constructs an end iterator at a page boundary.
Definition paged_storage.h:671
detail::size_type size_type
Type used for sizes and indices.
Definition paged_storage.h:655
const_page_iterator(const page_type *pPage, const page_type *pPageLast)
Constructs a read-only iterator over a page range.
Definition paged_storage.h:676
pointer operator->() const
Accesses the current element through a pointer.
Definition paged_storage.h:708
iterator & operator++()
Advances to the next element.
Definition paged_storage.h:714
Iterator over SoA elements in paged storage. SoA pages keep each member in a separate array,...
Definition paged_storage.h:772
iterator & operator++()
Advances to the next element.
Definition paged_storage.h:839
detail::size_type size_type
Type used for sizes and indices.
Definition paged_storage.h:780
detail::difference_type difference_type
Type used for iterator distances.
Definition paged_storage.h:778
iterator operator++(int)
Advances to the next element.
Definition paged_storage.h:869
GAIA_NODISCARD bool operator!=(const iterator &other) const
Checks whether two iterators have different positions.
Definition paged_storage.h:884
value_type operator*() const
Accesses the current element view.
Definition paged_storage.h:828
GAIA_NODISCARD bool operator==(const iterator &other) const
Checks whether two iterators have the same position.
Definition paged_storage.h:878
value_type operator->() const
Accesses the current element view through arrow syntax.
Definition paged_storage.h:833
page_iterator_soa(page_type *pPage)
Constructs an end iterator at a page boundary.
Definition paged_storage.h:796
page_iterator_soa(page_type *pPage, page_type *pPageLast)
Constructs an iterator over a page range.
Definition paged_storage.h:801
Iterator over elements in paged storage.
Definition paged_storage.h:521
detail::size_type size_type
Type used for sizes and indices.
Definition paged_storage.h:531
detail::difference_type difference_type
Type used for iterator distances.
Definition paged_storage.h:529
iterator & operator++()
Advances to the next element.
Definition paged_storage.h:590
GAIA_NODISCARD bool operator==(const iterator &other) const
Checks whether two iterators have the same position.
Definition paged_storage.h:629
iterator operator++(int)
Advances to the next element.
Definition paged_storage.h:620
page_iterator(page_type *pPage)
Constructs an end iterator at a page boundary.
Definition paged_storage.h:547
pointer operator->() const
Accesses the current element through a pointer.
Definition paged_storage.h:584
GAIA_NODISCARD bool operator!=(const iterator &other) const
Checks whether two iterators have different positions.
Definition paged_storage.h:635
reference operator*() const
Accesses the current element.
Definition paged_storage.h:579
page_iterator(page_type *pPage, page_type *pPageLast)
Constructs an iterator over a page range.
Definition paged_storage.h:552
Customization point for converting a value to its paged-storage identifier. The default implementatio...
Definition paged_storage.h:43
static page_storage_id get(const T &item) noexcept
Applies the default conversion for an item.
Definition paged_storage.h:47
Storage policy for a selected layout and item type.
Definition data_layout_policy.h:121