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 m_pPage != other.m_pPage && m_it != other.m_it;
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;
124 bit_set_iter_type m_it;
125
126 public:
127 mem_page_iterator_soa(page_type* pPage, bit_set_iter_type it): m_pPage(pPage), m_it(it) {}
128
129 value_type operator*() const {
130 return m_pPage->set_data(*m_it);
131 }
132 value_type operator->() const {
133 return &m_pPage->set_data(*m_it);
134 }
135
136 iterator& operator++() {
137 ++m_it;
138 return *this;
139 }
140 iterator operator++(int) {
141 iterator temp(*this);
142 ++*this;
143 return temp;
144 }
145 iterator& operator--() {
146 --m_it;
147 return *this;
148 }
149 iterator operator--(int) {
150 iterator temp(*this);
151 --*this;
152 return temp;
153 }
154
155 GAIA_NODISCARD bool operator==(const iterator& other) const {
156 return m_pPage == other.m_pPage && m_it == other.m_it;
157 }
158 GAIA_NODISCARD bool operator!=(const iterator& other) const {
159 return m_pPage != other.m_pPage && m_it != other.m_it;
160 }
161 };
162
163 template <typename T>
164 struct mem_page_data {
165 static constexpr uint32_t PageCapacity = 4096;
166
167 using view_policy = mem::auto_view_policy<T>;
168 using bit_set = cnt::bitset<PageCapacity>;
169 static constexpr uint32_t AllocatedBytes = view_policy::get_min_byte_size(0, PageCapacity);
170
171 struct PageHeader {
173 size_type cnt = size_type(0);
175 bit_set mask;
176 };
177
179 PageHeader header;
181 mem::raw_data_holder<T, AllocatedBytes> data;
182 };
183
184 template <typename T, typename Allocator>
185 class mem_page {
186 static_assert(!std::is_empty_v<T>, "It only makes sense to use page storage for data types with non-zero size");
187
188 public:
189 using value_type = T;
190 using reference = T&;
191 using const_reference = const T&;
192 using pointer = T*;
193 using const_pointer = const T*;
194 using view_policy = mem::auto_view_policy<T>;
195 using difference_type = detail::difference_type;
196 using size_type = detail::size_type;
197
198 template <bool IsFwd>
199 using iterator_base = mem_page_iterator<T, Allocator, IsFwd>;
200 using iterator = iterator_base<true>;
201 using iterator_reverse = iterator_base<false>;
202
203 template <bool IsFwd>
204 using iterator_soa_base = mem_page_iterator_soa<T, Allocator, IsFwd>;
205 using iterator_soa = iterator_soa_base<true>;
206 using iterator_soa_reverse = iterator_soa_base<false>;
207
208 using PageData = mem_page_data<T>;
209 static constexpr uint32_t PageCapacity = PageData::PageCapacity;
210
211 private:
213 PageData* m_pData = nullptr;
214
215 void ensure() {
216 if GAIA_LIKELY (m_pData != nullptr)
217 return;
218
219 // Allocate memory for data
220 m_pData = mem::AllocHelper::alloc<PageData, Allocator>(1);
221 (void)new (m_pData) PageData{};
222 // Prepare the item data region
223 core::call_ctor_raw_n(data(), PageCapacity);
224 }
225
226 void dtr_data_inter(uint32_t idx) noexcept {
227 GAIA_ASSERT(!empty());
228
229 if constexpr (!mem::is_soa_layout_v<T>) {
230 auto* ptr = &data()[idx];
231 core::call_dtor(ptr);
232 }
233
234 m_pData->header.mask.set(idx, false);
235 --m_pData->header.cnt;
236 }
237
238 void dtr_active_data() noexcept {
239 if constexpr (!mem::is_soa_layout_v<T>) {
240 for (auto i: m_pData->header.mask) {
241 auto* ptr = &data()[i];
242 core::call_dtor(ptr);
243 }
244 }
245 }
246
247 void invalidate() {
248 if (m_pData == nullptr)
249 return;
250
251 // Destruct active items
252 dtr_active_data();
253
254 // Release allocated memory
255 m_pData->~PageData();
256 mem::AllocHelper::free<Allocator>(m_pData);
257 m_pData = nullptr;
258 }
259
260 public:
261 mem_page() = default;
262
263 mem_page(const mem_page& other) {
264 // Copy new items over
265 if (other.m_pData == nullptr) {
266 invalidate();
267 } else {
268 ensure();
269
270 m_pData->header.mask = other.m_pData->header.mask;
271 m_pData->header.cnt = other.m_pData->header.cnt;
272
273 // Copy construct data
274 for (auto i: other.m_pData->header.mask)
275 add_data(i, other.get_data(i));
276 }
277 }
278
279 mem_page& operator=(const mem_page& other) {
280 GAIA_ASSERT(core::addressof(other) != this);
281
282 // Copy new items over if there are any
283 if (other.m_pData == nullptr) {
284 invalidate();
285 } else {
286 ensure();
287
288 // Remove current active items
289 if (m_pData != nullptr)
290 dtr_active_data();
291
292 m_pData->header.mask = other.m_pData->header.mask;
293 m_pData->header.cnt = other.m_pData->header.cnt;
294
295 // Copy data
296 for (auto i: other.m_pData->header.mask)
297 add_data(i, other.get_data(i));
298 }
299
300 return *this;
301 }
302
303 mem_page(mem_page&& other) noexcept {
304 m_pData = other.m_pData;
305 other.m_pData = nullptr;
306 }
307
308 mem_page& operator=(mem_page&& other) noexcept {
309 GAIA_ASSERT(core::addressof(other) != this);
310
311 invalidate();
312
313 m_pData = other.m_pData;
314 other.m_pData = nullptr;
315
316 return *this;
317 }
318
319 ~mem_page() {
320 invalidate();
321 }
322
323 GAIA_CLANG_WARNING_PUSH()
324 // Memory is aligned so we can silence this warning
325 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
326
327 GAIA_NODISCARD pointer data() noexcept {
328 return GAIA_ACC((pointer)&m_pData->data[0]);
329 }
330
331 GAIA_NODISCARD const_pointer data() const noexcept {
332 return GAIA_ACC((const_pointer)&m_pData->data[0]);
333 }
334
335 GAIA_NODISCARD decltype(auto) set_data(size_type pos) noexcept {
336 GAIA_ASSERT(m_pData->header.mask.test(pos));
337 return view_policy::set(
338 {GAIA_ACC((typename view_policy::TargetCastType) & m_pData->data[0]), PageCapacity}, pos);
339 }
340
341 GAIA_NODISCARD decltype(auto) operator[](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) get_data(size_type pos) const noexcept {
348 GAIA_ASSERT(m_pData->header.mask.test(pos));
349 return view_policy::get(
350 {GAIA_ACC((typename view_policy::TargetCastType) & m_pData->data[0]), PageCapacity}, pos);
351 }
352
353 GAIA_NODISCARD decltype(auto) operator[](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_CLANG_WARNING_POP()
360
361 void add() {
362 ensure();
363 ++m_pData->header.cnt;
364 }
365
366 decltype(auto) add_data(uint32_t idx, const T& arg) {
367 m_pData->header.mask.set(idx);
368
369 if constexpr (mem::is_soa_layout_v<T>) {
370 set_data(idx) = arg;
371 } else {
372 auto* ptr = &set_data(idx);
373 core::call_ctor(ptr, arg);
374 return (reference)(*ptr);
375 }
376 }
377
378 decltype(auto) add_data(uint32_t idx, T&& arg) {
379 m_pData->header.mask.set(idx);
380
381 if constexpr (mem::is_soa_layout_v<T>) {
382 set_data(idx) = GAIA_MOV(arg);
383 } else {
384 auto* ptr = &set_data(idx);
385 core::call_ctor(ptr, GAIA_MOV(arg));
386 return (reference)(*ptr);
387 }
388 }
389
390 template <typename... Args>
391 decltype(auto) emplace_data(uint32_t idx, Args&&... args) {
392 m_pData->header.used.set(idx);
393
394 if constexpr (mem::is_soa_layout_v<T>) {
395 set_data(idx) = T(GAIA_FWD(args)...);
396 } else {
397 auto* ptr = &set_data(idx);
398 core::call_ctor(ptr, GAIA_FWD(args)...);
399 return (reference)(*ptr);
400 }
401 }
402
403 void del_data(uint32_t idx) noexcept {
404 dtr_data_inter(idx);
405
406 // If there is no more data, release the memory allocated by the page
407 if (m_pData->header.cnt == 0)
408 invalidate();
409 }
410
411 GAIA_NODISCARD bool has_data(uint32_t idx) const noexcept {
412 return m_pData ? m_pData->header.mask.test(idx) : false;
413 }
414
415 GAIA_NODISCARD size_type size() const noexcept {
416 return m_pData ? m_pData->header.cnt : 0;
417 }
418
419 GAIA_NODISCARD bool empty() const noexcept {
420 return size() == 0;
421 }
422
423 GAIA_NODISCARD decltype(auto) front() noexcept {
424 GAIA_ASSERT(!empty());
425 if constexpr (mem::is_soa_layout_v<T>)
426 return *begin();
427 else
428 return (reference)*begin();
429 }
430
431 GAIA_NODISCARD decltype(auto) front() const noexcept {
432 GAIA_ASSERT(!empty());
433 if constexpr (mem::is_soa_layout_v<T>)
434 return *begin();
435 else
436 return (const_reference)*begin();
437 }
438
439 GAIA_NODISCARD decltype(auto) back() noexcept {
440 GAIA_ASSERT(!empty());
441 const auto idx = *m_pData->header.mask.rbegin();
442 if constexpr (mem::is_soa_layout_v<T>)
443 return set_data(idx);
444 else
445 return (reference)(set_data(idx));
446 }
447
448 GAIA_NODISCARD decltype(auto) back() const noexcept {
449 GAIA_ASSERT(!empty());
450 const auto idx = *m_pData->header.mask.rbegin();
451 if constexpr (mem::is_soa_layout_v<T>)
452 return set_data(idx);
453 else
454 return (const_reference)set_data(idx);
455 }
456
457 static constexpr typename PageData::bit_set s_dummyBitSet{};
458
459 GAIA_NODISCARD auto begin() const noexcept {
460 if constexpr (mem::is_soa_layout_v<T>)
461 return iterator_soa((mem_page*)this, m_pData ? m_pData->header.mask.begin() : s_dummyBitSet.begin());
462 else
463 return iterator((mem_page*)this, m_pData ? m_pData->header.mask.begin() : s_dummyBitSet.begin());
464 }
465
466 GAIA_NODISCARD auto end() const noexcept {
467 if constexpr (mem::is_soa_layout_v<T>)
468 return iterator_soa((mem_page*)this, m_pData ? m_pData->header.mask.end() : s_dummyBitSet.end());
469 else
470 return iterator((mem_page*)this, m_pData ? m_pData->header.mask.end() : s_dummyBitSet.end());
471 }
472
473 GAIA_NODISCARD auto rbegin() const noexcept {
474 if constexpr (mem::is_soa_layout_v<T>)
475 return iterator_soa_reverse(
476 (mem_page*)this, m_pData ? m_pData->header.mask.rbegin() : s_dummyBitSet.rbegin());
477 else
478 return iterator_reverse((mem_page*)this, m_pData ? m_pData->header.mask.rbegin() : s_dummyBitSet.rbegin());
479 }
480
481 GAIA_NODISCARD auto rend() const noexcept {
482 if constexpr (mem::is_soa_layout_v<T>)
483 return iterator_soa_reverse((mem_page*)this, m_pData ? m_pData->header.mask.rend() : s_dummyBitSet.rend());
484 else
485 return iterator_reverse((mem_page*)this, m_pData ? m_pData->header.mask.rend() : s_dummyBitSet.rend());
486 }
487
488 GAIA_NODISCARD bool operator==(const mem_page& other) const noexcept {
489 // We expect to compare only valid pages
490 GAIA_ASSERT(m_pData != nullptr);
491 GAIA_ASSERT(other.m_pData != nullptr);
492
493 if (m_pData->header.cnt != other.m_pData->header.cnt)
494 return false;
495 if (m_pData->header.mask != other.m_pData->header.mask)
496 return false;
497 for (auto i: m_pData->header.mask)
498 if (!(get_data(i) == other[i]))
499 return false;
500 return true;
501 }
502
503 GAIA_NODISCARD bool operator!=(const mem_page& other) const noexcept {
504 return !operator==(other);
505 }
506 };
507 } // namespace detail
509
514 template <typename T, typename Allocator, bool IsFwd>
517 using value_type = T;
519 using pointer = T*;
521 using reference = T&;
523 using difference_type = detail::difference_type;
525 using size_type = detail::size_type;
530
531 private:
532 using page_type = detail::mem_page<T, Allocator>;
533
534 page_type* m_pPage;
535 page_type* m_pPageLast;
536 typename page_type::template iterator_base<IsFwd> m_it;
537
538 public:
541 page_iterator(page_type* pPage): m_pPage(pPage), m_pPageLast(pPage) {}
542
546 page_iterator(page_type* pPage, page_type* pPageLast): m_pPage(pPage), m_pPageLast(pPageLast) {
547 // Find first page with data
548 if constexpr (!IsFwd) {
549 m_it = m_pPage->rbegin();
550 while (m_it == m_pPage->rend()) {
551 --m_pPage;
552 if (m_pPage == m_pPageLast) {
553 m_it = {};
554 break;
555 }
556 m_it = m_pPage->rbegin();
557 }
558 } else {
559 m_it = m_pPage->begin();
560 while (m_it == m_pPage->end()) {
561 ++m_pPage;
562 if (m_pPage == m_pPageLast) {
563 m_it = {};
564 break;
565 }
566 m_it = m_pPage->begin();
567 }
568 }
569 }
570
574 return m_it.operator*();
575 }
579 return m_it.operator->();
580 }
581
585 if constexpr (!IsFwd) {
586 ++m_it;
587 if (m_it == m_pPage->rend()) {
588 --m_pPage;
589 if (m_pPage == m_pPageLast) {
590 m_it = {};
591 return *this;
592 }
593 m_it = m_pPage->rbegin();
594 }
595 } else {
596 ++m_it;
597 if (m_it == m_pPage->end()) {
598 ++m_pPage;
599 if (m_pPage == m_pPageLast) {
600 m_it = {};
601 return *this;
602 }
603 m_it = m_pPage->begin();
604 }
605 }
606 return *this;
607 }
611 iterator temp(*this);
612 ++*this;
613 return temp;
614 }
615
619 GAIA_NODISCARD bool operator==(const iterator& other) const {
620 return m_pPage == other.m_pPage && m_it == other.m_it;
621 }
625 GAIA_NODISCARD bool operator!=(const iterator& other) const {
626 return m_pPage != other.m_pPage || m_it != other.m_it;
627 }
628 };
629
634 template <typename T, typename Allocator, bool IsFwd>
637 using value_type = T;
639 using pointer = const T*;
641 using reference = const T&;
643 using difference_type = detail::difference_type;
645 using size_type = detail::size_type;
650
651 private:
652 using page_type = detail::mem_page<T, Allocator>;
653
654 const page_type* m_pPage;
655 const page_type* m_pPageLast;
656 typename page_type::template iterator_base<IsFwd> m_it;
657
658 public:
661 const_page_iterator(const page_type* pPage): m_pPage(pPage), m_pPageLast(pPage) {}
662
666 const_page_iterator(const page_type* pPage, const page_type* pPageLast): m_pPage(pPage), m_pPageLast(pPageLast) {
667 // Find first page with data
668 if constexpr (!IsFwd) {
669 m_it = m_pPage->rbegin();
670 while (m_it == m_pPage->rend()) {
671 --m_pPage;
672 if (m_pPage == m_pPageLast) {
673 m_it = {};
674 break;
675 }
676 m_it = m_pPage->rbegin();
677 }
678 } else {
679 m_it = m_pPage->begin();
680 while (m_it == m_pPage->end()) {
681 ++m_pPage;
682 if (m_pPage == m_pPageLast) {
683 m_it = {};
684 break;
685 }
686 m_it = m_pPage->begin();
687 }
688 }
689 }
690
694 return m_it.operator*();
695 }
699 return m_it.operator->();
700 }
701
705 if constexpr (!IsFwd) {
706 ++m_it;
707 if (m_it == m_pPage->rend()) {
708 --m_pPage;
709 if (m_pPage == m_pPageLast) {
710 m_it = {};
711 return *this;
712 }
713 m_it = m_pPage->rbegin();
714 }
715 } else {
716 ++m_it;
717 if (m_it == m_pPage->end()) {
718 ++m_pPage;
719 if (m_pPage == m_pPageLast) {
720 m_it = {};
721 return *this;
722 }
723 m_it = m_pPage->begin();
724 }
725 }
726 return *this;
727 }
731 iterator temp(*this);
732 ++*this;
733 return temp;
734 }
735
739 GAIA_NODISCARD bool operator==(const iterator& other) const {
740 return m_pPage == other.m_pPage && m_it == other.m_it;
741 }
745 GAIA_NODISCARD bool operator!=(const iterator& other) const {
746 return m_pPage != other.m_pPage || m_it != other.m_it;
747 }
748 };
749
754 template <typename T, typename Allocator, bool IsFwd>
757 using value_type = T;
758 // using pointer = T*;
759 // using reference = T&;
761 using difference_type = detail::difference_type;
763 using size_type = detail::size_type;
768
769 private:
770 using page_type = detail::mem_page<T, Allocator>;
771
772 page_type* m_pPage;
773 page_type* m_pPageLast;
774 typename page_type::template iterator_soa_base<IsFwd> m_it;
775
776 public:
779 page_iterator_soa(page_type* pPage): m_pPage(pPage), m_pPageLast(pPage) {}
780
784 page_iterator_soa(page_type* pPage, page_type* pPageLast): m_pPage(pPage), m_pPageLast(pPageLast) {
785 // Find first page with data
786 if constexpr (!IsFwd) {
787 m_it = m_pPage->rbegin();
788 while (m_it == m_pPage->rend()) {
789 if (m_pPage == m_pPageLast)
790 break;
791 --m_pPage;
792 m_it = m_pPage->rbegin();
793 }
794 } else {
795 m_it = m_pPage->begin();
796 while (m_it == m_pPage->end()) {
797 if (m_pPage == m_pPageLast)
798 break;
799 ++m_pPage;
800 m_it = m_pPage->begin();
801 }
802 }
803 }
804
808 return m_it.operator*();
809 }
813 return m_it.operator->();
814 }
815
819 if constexpr (!IsFwd) {
820 ++m_it;
821 while (m_it == m_pPage->rend()) {
822 --m_pPage;
823 m_it = m_pPage->rbegin();
824 }
825 } else {
826 ++m_it;
827 while (m_it == m_pPage->end()) {
828 ++m_pPage;
829 m_it = m_pPage->begin();
830 }
831 }
832 return *this;
833 }
837 iterator temp(*this);
838 ++*this;
839 return temp;
840 }
841
845 GAIA_NODISCARD bool operator==(const iterator& other) const {
846 return m_pPage == other.m_pPage && m_it == other.m_it;
847 }
851 GAIA_NODISCARD bool operator!=(const iterator& other) const {
852 return m_pPage != other.m_pPage || m_it != other.m_it;
853 }
854 };
855
860 template <typename T, typename Allocator, bool IsFwd>
863 using value_type = T;
864 // using pointer = T*;
865 // using reference = T&;
867 using difference_type = detail::difference_type;
869 using size_type = detail::size_type;
874
875 private:
876 using page_type = detail::mem_page<T, Allocator>;
877
878 const page_type* m_pPage;
879 const page_type* m_pPageLast;
880 typename page_type::template iterator_soa_base<IsFwd> m_it;
881
882 public:
885 const_page_iterator_soa(const page_type* pPage): m_pPage(pPage), m_pPageLast(pPage) {}
886
890 const_page_iterator_soa(const page_type* pPage, const page_type* pPageLast):
891 m_pPage(pPage), m_pPageLast(pPageLast) {
892 // Find first page with data
893 if constexpr (!IsFwd) {
894 m_it = m_pPage->rbegin();
895 while (m_it == m_pPage->rend()) {
896 if (m_pPage == m_pPageLast)
897 break;
898 --m_pPage;
899 m_it = m_pPage->rbegin();
900 }
901 } else {
902 m_it = m_pPage->begin();
903 while (m_it == m_pPage->end()) {
904 if (m_pPage == m_pPageLast)
905 break;
906 ++m_pPage;
907 m_it = m_pPage->begin();
908 }
909 }
910 }
911
915 return m_it.operator*();
916 }
920 return m_it.operator->();
921 }
922
926 if constexpr (!IsFwd) {
927 ++m_it;
928 while (m_it == m_pPage->rend()) {
929 --m_pPage;
930 m_it = m_pPage->rbegin();
931 }
932 } else {
933 ++m_it;
934 while (m_it == m_pPage->end()) {
935 ++m_pPage;
936 m_it = m_pPage->begin();
937 }
938 }
939 return *this;
940 }
944 iterator temp(*this);
945 ++*this;
946 return temp;
947 }
948
952 GAIA_NODISCARD bool operator==(const iterator& other) const {
953 return m_pPage == other.m_pPage && m_it == other.m_it;
954 }
958 GAIA_NODISCARD bool operator!=(const iterator& other) const {
959 return m_pPage != other.m_pPage || m_it != other.m_it;
960 }
961 };
962
965 template <typename T>
967 public:
969 using value_type = T;
971 using reference = T&;
973 using const_reference = const T&;
975 using pointer = T*;
977 using const_pointer = const T*;
981 using difference_type = detail::difference_type;
983 using size_type = detail::size_type;
984
986 static constexpr uint32_t AllocatorBlockSize = (uint32_t)sizeof(detail::mem_page_data<T>);
989
991 using page_data_type = detail::mem_page_data<T>;
993 using page_type = detail::mem_page<T, Allocator>;
995 static constexpr uint32_t PageCapacity = page_type::PageCapacity;
996
1015
1016 private:
1017 constexpr static uint32_t PageMask = PageCapacity - 1;
1018 constexpr static uint32_t ToPageIndex = core::count_bits(PageMask);
1019
1021 cnt::darray<page_type> m_pages;
1023 uint32_t m_itemCnt = 0;
1024
1025 void try_grow(uint32_t pid) {
1026 // The page array has to be able to take any page index
1027 if (pid >= m_pages.size())
1028 m_pages.resize(pid + 1);
1029
1030 m_pages[pid].add();
1031 ++m_itemCnt;
1032 }
1033
1034 public:
1035 constexpr page_storage() noexcept = default;
1036
1040 m_pages = other.m_pages;
1041 m_itemCnt = other.m_itemCnt;
1042 }
1043
1048 GAIA_ASSERT(core::addressof(other) != this);
1049
1050 m_pages = other.m_pages;
1051 m_itemCnt = other.m_itemCnt;
1052 return *this;
1053 }
1054
1057 page_storage(page_storage&& other) noexcept {
1058 m_pages = GAIA_MOV(other.m_pages);
1059 m_itemCnt = other.m_itemCnt;
1060
1061 other.m_pages = {};
1062 }
1063
1068 GAIA_ASSERT(core::addressof(other) != this);
1069
1070 m_pages = GAIA_MOV(other.m_pages);
1071 m_itemCnt = other.m_itemCnt;
1072
1073 other.m_pages = {};
1074
1075 return *this;
1076 }
1077
1078 ~page_storage() = default;
1079
1080 GAIA_CLANG_WARNING_PUSH()
1081 // Memory is aligned so we can silence this warning
1082 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
1083
1088 GAIA_ASSERT(has(id));
1089 const auto pid = size_type(id >> ToPageIndex);
1090 const auto did = size_type(id & PageMask);
1091 auto& page = m_pages[pid];
1092 return view_policy::set({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
1093 }
1094
1098 GAIA_NODISCARD decltype(auto) operator[](page_storage_id id) const noexcept {
1099 GAIA_ASSERT(has(id));
1100 const auto pid = size_type(id >> ToPageIndex);
1101 const auto did = size_type(id & PageMask);
1102 auto& page = m_pages[pid];
1103 return view_policy::get({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
1104 }
1105
1106 GAIA_CLANG_WARNING_POP()
1107
1108
1111 GAIA_NODISCARD bool has(page_storage_id id) const noexcept {
1112 const auto pid = size_type(id >> ToPageIndex);
1113 if (pid >= m_pages.size())
1114 return false;
1115
1116 const auto did = size_type(id & PageMask);
1117 const auto val = page_data_type::bit_set::BitCount;
1118 return did < val && m_pages[pid].has_data(did);
1119 }
1120
1124 GAIA_NODISCARD bool has(const T& arg) const noexcept {
1125 const auto id = to_page_storage_id<T>::get(arg);
1126 GAIA_ASSERT(id != detail::InvalidPageStorageId);
1127 return has(id);
1128 }
1129
1134 template <typename TType>
1135 decltype(auto) add(TType&& arg) {
1136 const auto id = to_page_storage_id<T>::get(arg);
1137 if (has(id)) {
1138 if constexpr (mem::is_soa_layout_v<TType>)
1139 return;
1140 else {
1141 const auto pid = size_type(id >> ToPageIndex);
1142 const auto did = size_type(id & PageMask);
1143 auto& page = m_pages[pid];
1144 return page.set_data(did);
1145 }
1146 }
1147
1148 const auto pid = size_type(id >> ToPageIndex);
1149 const auto did = size_type(id & PageMask);
1150
1151 try_grow(pid);
1152
1153 auto& page = m_pages[pid];
1154 if constexpr (mem::is_soa_layout_v<TType>)
1155 page.add_data(did, GAIA_FWD(arg));
1156 else
1157 return page.add_data(did, GAIA_FWD(arg));
1158 }
1159
1163 decltype(auto) set(page_storage_id id) {
1164 GAIA_ASSERT(has(id));
1165
1166 const auto pid = uint32_t(id >> ToPageIndex);
1167 const auto did = uint32_t(id & PageMask);
1168
1169 auto& page = m_pages[pid];
1170 return page.set_data(did);
1171 }
1172
1175 void del(page_storage_id id) noexcept {
1176 GAIA_ASSERT(!empty());
1177 GAIA_ASSERT(id != detail::InvalidPageStorageId);
1178
1179 if (!has(id))
1180 return;
1181
1182 const auto pid = uint32_t(id >> ToPageIndex);
1183 const auto did = uint32_t(id & PageMask);
1184
1185 auto& page = m_pages[pid];
1186 page.del_data(did);
1187 --m_itemCnt;
1188 }
1189
1192 void del(const T& arg) noexcept {
1193 const auto id = to_page_storage_id<T>::get(arg);
1194 return del(id);
1195 }
1196
1198 void clear() {
1199 m_pages.resize(0);
1200 m_itemCnt = 0;
1201 }
1202
1205 GAIA_NODISCARD size_type size() const noexcept {
1206 return m_itemCnt;
1207 }
1208
1211 GAIA_NODISCARD bool empty() const noexcept {
1212 return m_itemCnt == 0;
1213 }
1214
1217 GAIA_NODISCARD decltype(auto) front() noexcept {
1218 GAIA_ASSERT(!empty());
1219 return (reference)*begin();
1220 }
1221
1224 GAIA_NODISCARD decltype(auto) front() const noexcept {
1225 GAIA_ASSERT(!empty());
1226 return (const_reference)*begin();
1227 }
1228
1231 GAIA_NODISCARD decltype(auto) back() noexcept {
1232 GAIA_ASSERT(!empty());
1233 return (reference)*rbegin();
1234 }
1235
1238 GAIA_NODISCARD decltype(auto) back() const noexcept {
1239 GAIA_ASSERT(!empty());
1240 return (const_reference)*rbegin();
1241 }
1242
1245 GAIA_NODISCARD auto begin() noexcept {
1246 GAIA_ASSERT(!empty());
1247 return iterator(m_pages.data(), m_pages.data() + m_pages.size());
1248 }
1249
1252 GAIA_NODISCARD auto begin() const noexcept {
1253 GAIA_ASSERT(!empty());
1254 return const_iterator(m_pages.data(), m_pages.data() + m_pages.size());
1255 }
1256
1259 GAIA_NODISCARD auto cbegin() const noexcept {
1260 GAIA_ASSERT(!empty());
1261 return const_iterator(m_pages.data(), m_pages.data() + m_pages.size());
1262 }
1263
1266 GAIA_NODISCARD auto end() noexcept {
1267 GAIA_ASSERT(!empty());
1268 return iterator(m_pages.data() + m_pages.size());
1269 }
1270
1273 GAIA_NODISCARD auto end() const noexcept {
1274 GAIA_ASSERT(!empty());
1275 return const_iterator(m_pages.data() + m_pages.size());
1276 }
1277
1280 GAIA_NODISCARD auto cend() const noexcept {
1281 GAIA_ASSERT(!empty());
1282 return const_iterator(m_pages.data() + m_pages.size());
1283 }
1284
1287 GAIA_NODISCARD auto rbegin() noexcept {
1288 GAIA_ASSERT(!empty());
1289 return iterator_reverse(m_pages.data() + m_pages.size() - 1, m_pages.data() - 1);
1290 }
1291
1294 GAIA_NODISCARD auto rbegin() const noexcept {
1295 GAIA_ASSERT(!empty());
1296 return const_iterator_reverse(m_pages.data() + m_pages.size() - 1, m_pages.data() - 1);
1297 }
1298
1301 GAIA_NODISCARD auto crbegin() const noexcept {
1302 GAIA_ASSERT(!empty());
1303 return const_iterator_reverse(m_pages.data() + m_pages.size() - 1, m_pages.data() - 1);
1304 }
1305
1308 GAIA_NODISCARD auto rend() noexcept {
1309 GAIA_ASSERT(!empty());
1310 return iterator_reverse(m_pages.data() - 1);
1311 }
1312
1315 GAIA_NODISCARD auto rend() const noexcept {
1316 GAIA_ASSERT(!empty());
1317 return const_iterator_reverse(m_pages.data() - 1);
1318 }
1319
1322 GAIA_NODISCARD auto crend() const noexcept {
1323 GAIA_ASSERT(!empty());
1324 return const_iterator_reverse(m_pages.data() - 1);
1325 }
1326
1330 GAIA_NODISCARD bool operator==(const page_storage& other) const noexcept {
1331 return m_pages == other.m_pages;
1332 }
1333
1337 GAIA_NODISCARD bool operator!=(const page_storage& other) const noexcept {
1338 return !operator==(other);
1339 }
1340 };
1341 } // namespace cnt
1342
1343} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:27
GAIA_NODISCARD auto rbegin() noexcept
Returns a reverse traversal iterator to the last element.
Definition darray_impl.h:574
GAIA_NODISCARD size_type size() const noexcept
Returns the number of elements.
Definition darray_impl.h:504
GAIA_NODISCARD auto begin() noexcept
Returns an iterator to the first element.
Definition darray_impl.h:556
void resize(size_type count)
Changes the number of elements.
Definition darray_impl.h:240
GAIA_NODISCARD pointer data() noexcept
Returns a pointer to the element storage.
Definition darray_impl.h:193
Heap-allocated paged storage for elements of type T.
Definition paged_storage.h:966
GAIA_NODISCARD bool operator==(const page_storage &other) const noexcept
Checks whether two storages contain equal elements.
Definition paged_storage.h:1330
detail::mem_page< T, Allocator > page_type
Page type used by the storage.
Definition paged_storage.h:993
const_page_iterator< T, Allocator, false > const_iterator_reverse
Reverse read-only iterator.
Definition paged_storage.h:1008
GAIA_NODISCARD auto end() noexcept
Returns an iterator past the last stored element.
Definition paged_storage.h:1266
GAIA_NODISCARD auto rbegin() const noexcept
Returns a reverse iterator to the last stored element.
Definition paged_storage.h:1294
GAIA_NODISCARD decltype(auto) front() const noexcept
Accesses the first stored element.
Definition paged_storage.h:1224
GAIA_NODISCARD auto crend() const noexcept
Returns a read-only reverse iterator before the first stored element.
Definition paged_storage.h:1322
GAIA_NODISCARD auto cbegin() const noexcept
Returns a read-only iterator to the first stored element.
Definition paged_storage.h:1259
decltype(auto) add(TType &&arg)
Inserts the item arg into the storage.
Definition paged_storage.h:1135
GAIA_NODISCARD decltype(auto) back() const noexcept
Accesses the last stored element.
Definition paged_storage.h:1238
void clear()
Clears the storage.
Definition paged_storage.h:1198
page_iterator< T, Allocator, true > iterator
Forward mutable iterator.
Definition paged_storage.h:998
void del(const T &arg) noexcept
Removes the item arg from the storage.
Definition paged_storage.h:1192
const_page_iterator< T, Allocator, true > const_iterator
Forward read-only iterator.
Definition paged_storage.h:1006
static constexpr uint32_t AllocatorBlockSize
Size in bytes of one allocator block.
Definition paged_storage.h:986
GAIA_NODISCARD auto rend() noexcept
Returns a reverse iterator before the first stored element.
Definition paged_storage.h:1308
decltype(auto) set(page_storage_id id)
Accesses the record at the index id for update.
Definition paged_storage.h:1163
page_storage & operator=(page_storage &&other) noexcept
Move-assigns a storage.
Definition paged_storage.h:1067
page_iterator< T, Allocator, false > iterator_reverse
Reverse mutable iterator.
Definition paged_storage.h:1000
GAIA_NODISCARD size_type size() const noexcept
Returns the number of items inserted into the storage.
Definition paged_storage.h:1205
GAIA_NODISCARD auto begin() noexcept
Returns an iterator to the first stored element.
Definition paged_storage.h:1245
detail::difference_type difference_type
Type used for iterator distances.
Definition paged_storage.h:981
static constexpr uint32_t PageCapacity
Maximum number of elements addressable in one page.
Definition paged_storage.h:995
GAIA_NODISCARD auto rend() const noexcept
Returns a reverse iterator before the first stored element.
Definition paged_storage.h:1315
GAIA_NODISCARD auto rbegin() noexcept
Returns a reverse iterator to the last stored element.
Definition paged_storage.h:1287
detail::mem_page_data< T > page_data_type
Raw page-data type.
Definition paged_storage.h:991
GAIA_NODISCARD bool empty() const noexcept
Checks if the storage is empty (no items inserted).
Definition paged_storage.h:1211
GAIA_NODISCARD bool has(const T &arg) const noexcept
Checks if an item arg exists within the storage.
Definition paged_storage.h:1124
GAIA_NODISCARD auto begin() const noexcept
Returns an iterator to the first stored element.
Definition paged_storage.h:1252
GAIA_NODISCARD decltype(auto) front() noexcept
Accesses the first stored element.
Definition paged_storage.h:1217
void del(page_storage_id id) noexcept
Removes the item at the index id from the storage.
Definition paged_storage.h:1175
GAIA_NODISCARD auto cend() const noexcept
Returns a read-only iterator past the last stored element.
Definition paged_storage.h:1280
GAIA_NODISCARD auto end() const noexcept
Returns an iterator past the last stored element.
Definition paged_storage.h:1273
GAIA_NODISCARD bool operator!=(const page_storage &other) const noexcept
Checks whether two storages differ.
Definition paged_storage.h:1337
detail::size_type size_type
Type used for sizes and indices.
Definition paged_storage.h:983
GAIA_NODISCARD auto crbegin() const noexcept
Returns a read-only reverse iterator to the last stored element.
Definition paged_storage.h:1301
GAIA_NODISCARD decltype(auto) back() noexcept
Accesses the last stored element.
Definition paged_storage.h:1231
page_storage & operator=(const page_storage &other)
Copy-assigns a storage.
Definition paged_storage.h:1047
page_storage(page_storage &&other) noexcept
Move-constructs a storage.
Definition paged_storage.h:1057
GAIA_NODISCARD bool has(page_storage_id id) const noexcept
Checks if an item with a given page id exists.
Definition paged_storage.h:1111
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 structure-of-arrays elements in paged storage.
Definition paged_storage.h:861
const_page_iterator_soa(const page_type *pPage)
Constructs an end iterator at a page boundary.
Definition paged_storage.h:885
iterator operator++(int)
Advances to the next element.
Definition paged_storage.h:943
detail::difference_type difference_type
Type used for iterator distances.
Definition paged_storage.h:867
detail::size_type size_type
Type used for sizes and indices.
Definition paged_storage.h:869
iterator & operator++()
Advances to the next element.
Definition paged_storage.h:925
GAIA_NODISCARD bool operator==(const iterator &other) const
Checks whether two iterators have the same position.
Definition paged_storage.h:952
value_type operator*() const
Accesses the current element view.
Definition paged_storage.h:914
value_type operator->() const
Accesses the current element view through arrow syntax.
Definition paged_storage.h:919
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:890
GAIA_NODISCARD bool operator!=(const iterator &other) const
Checks whether two iterators have different positions.
Definition paged_storage.h:958
Read-only iterator over elements in paged storage.
Definition paged_storage.h:635
iterator operator++(int)
Advances to the next element.
Definition paged_storage.h:730
reference operator*() const
Accesses the current element.
Definition paged_storage.h:693
GAIA_NODISCARD bool operator==(const iterator &other) const
Checks whether two iterators have the same position.
Definition paged_storage.h:739
GAIA_NODISCARD bool operator!=(const iterator &other) const
Checks whether two iterators have different positions.
Definition paged_storage.h:745
detail::difference_type difference_type
Type used for iterator distances.
Definition paged_storage.h:643
const_page_iterator(const page_type *pPage)
Constructs an end iterator at a page boundary.
Definition paged_storage.h:661
detail::size_type size_type
Type used for sizes and indices.
Definition paged_storage.h:645
const_page_iterator(const page_type *pPage, const page_type *pPageLast)
Constructs a read-only iterator over a page range.
Definition paged_storage.h:666
pointer operator->() const
Accesses the current element through a pointer.
Definition paged_storage.h:698
iterator & operator++()
Advances to the next element.
Definition paged_storage.h:704
Iterator over structure-of-arrays elements in paged storage.
Definition paged_storage.h:755
iterator & operator++()
Advances to the next element.
Definition paged_storage.h:818
detail::size_type size_type
Type used for sizes and indices.
Definition paged_storage.h:763
detail::difference_type difference_type
Type used for iterator distances.
Definition paged_storage.h:761
iterator operator++(int)
Advances to the next element.
Definition paged_storage.h:836
GAIA_NODISCARD bool operator!=(const iterator &other) const
Checks whether two iterators have different positions.
Definition paged_storage.h:851
value_type operator*() const
Accesses the current element view.
Definition paged_storage.h:807
GAIA_NODISCARD bool operator==(const iterator &other) const
Checks whether two iterators have the same position.
Definition paged_storage.h:845
value_type operator->() const
Accesses the current element view through arrow syntax.
Definition paged_storage.h:812
page_iterator_soa(page_type *pPage)
Constructs an end iterator at a page boundary.
Definition paged_storage.h:779
page_iterator_soa(page_type *pPage, page_type *pPageLast)
Constructs an iterator over a page range.
Definition paged_storage.h:784
Iterator over elements in paged storage.
Definition paged_storage.h:515
detail::size_type size_type
Type used for sizes and indices.
Definition paged_storage.h:525
detail::difference_type difference_type
Type used for iterator distances.
Definition paged_storage.h:523
iterator & operator++()
Advances to the next element.
Definition paged_storage.h:584
GAIA_NODISCARD bool operator==(const iterator &other) const
Checks whether two iterators have the same position.
Definition paged_storage.h:619
iterator operator++(int)
Advances to the next element.
Definition paged_storage.h:610
page_iterator(page_type *pPage)
Constructs an end iterator at a page boundary.
Definition paged_storage.h:541
pointer operator->() const
Accesses the current element through a pointer.
Definition paged_storage.h:578
GAIA_NODISCARD bool operator!=(const iterator &other) const
Checks whether two iterators have different positions.
Definition paged_storage.h:625
reference operator*() const
Accesses the current element.
Definition paged_storage.h:573
page_iterator(page_type *pPage, page_type *pPageLast)
Constructs an iterator over a page range.
Definition paged_storage.h:546
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:123