Gaia-ECS v0.9.3
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 {
22 using page_storage_id = uint32_t;
23
24 namespace detail {
25 using difference_type = uint32_t;
26 using size_type = uint32_t;
27
28 constexpr static page_storage_id InvalidPageStorageId = (page_storage_id)-1;
29
30 template <typename T>
31 struct mem_page_data;
32 template <typename T, typename Allocator>
33 class mem_page;
34 } // namespace detail
35
36 template <typename T>
38 static page_storage_id get(const T& item) noexcept {
39 (void)item;
40 static_assert(
41 std::is_empty_v<T>,
42 "Sparse_storage items require a conversion function to be defined in gaia::cnt namespace");
43 return detail::InvalidPageStorageId;
44 }
45 };
46
47 namespace detail {
48 template <typename T, typename Allocator, bool IsFwd>
50 using value_type = T;
51 using pointer = T*;
52 using reference = T&;
57
58 private:
61 using bit_set_iter_type = std::conditional_t<
62 IsFwd, typename page_data_type::bit_set::iter, typename page_data_type::bit_set::iter_rev>;
63
64 page_type* m_pPage = nullptr;
65 bit_set_iter_type m_it;
66
67 public:
68 mem_page_iterator() = default;
70 mem_page_iterator(page_type* pPage, bit_set_iter_type it): m_pPage(pPage), m_it(it) {}
71
72 reference operator*() const {
73 return m_pPage->set_data(*m_it);
74 }
75 pointer operator->() const {
76 return &m_pPage->set_data(*m_it);
77 }
78
79 iterator& operator++() {
80 ++m_it;
81 return *this;
82 }
83 iterator operator++(int) {
84 iterator temp(*this);
85 ++*this;
86 return temp;
87 }
88
89 GAIA_NODISCARD bool operator==(const iterator& other) const {
90 return m_pPage == other.m_pPage && m_it == other.m_it;
91 }
92 GAIA_NODISCARD bool operator!=(const iterator& other) const {
93 return m_pPage != other.m_pPage && m_it != other.m_it;
94 }
95 };
96
97 template <typename T, typename Allocator, bool IsFwd>
99 using value_type = T;
100 // using pointer = T*; not supported
101 // using reference = T&; not supported
106
107 private:
110 using bit_set_iter_type = std::conditional_t<
111 IsFwd, typename page_data_type::bit_set::iter, typename page_data_type::bit_set::iter_rev>;
112
113 page_type* m_pPage;
114 bit_set_iter_type m_it;
115
116 public:
117 mem_page_iterator_soa(page_type* pPage, bit_set_iter_type it): m_pPage(pPage), m_it(it) {}
118
119 value_type operator*() const {
120 return m_pPage->set_data(*m_it);
121 }
122 value_type operator->() const {
123 return &m_pPage->set_data(*m_it);
124 }
125
126 iterator& operator++() {
127 ++m_it;
128 return *this;
129 }
130 iterator operator++(int) {
131 iterator temp(*this);
132 ++*this;
133 return temp;
134 }
135 iterator& operator--() {
136 --m_it;
137 return *this;
138 }
139 iterator operator--(int) {
140 iterator temp(*this);
141 --*this;
142 return temp;
143 }
144
145 GAIA_NODISCARD bool operator==(const iterator& other) const {
146 return m_pPage == other.m_pPage && m_it == other.m_it;
147 }
148 GAIA_NODISCARD bool operator!=(const iterator& other) const {
149 return m_pPage != other.m_pPage && m_it != other.m_it;
150 }
151 };
152
153 template <typename T>
155 static constexpr uint32_t PageCapacity = 4096;
156
159 static constexpr uint32_t AllocatedBytes = view_policy::get_min_byte_size(0, PageCapacity);
160
161 struct PageHeader {
163 size_type cnt = size_type(0);
166 };
167
172 };
173
174 template <typename T, typename Allocator>
175 class mem_page {
176 static_assert(!std::is_empty_v<T>, "It only makes sense to use page storage for data types with non-zero size");
177
178 public:
179 using value_type = T;
180 using reference = T&;
181 using const_reference = const T&;
182 using pointer = T*;
183 using const_pointer = const T*;
187
188 template <bool IsFwd>
192
193 template <bool IsFwd>
197
199 static constexpr uint32_t PageCapacity = PageData::PageCapacity;
200
201 private:
203 PageData* m_pData = nullptr;
204
205 void ensure() {
206 if GAIA_LIKELY (m_pData != nullptr)
207 return;
208
209 // Allocate memory for data
210 m_pData = mem::AllocHelper::alloc<PageData, Allocator>(1);
211 (void)new (m_pData) PageData{};
212 // Prepare the item data region
213 core::call_ctor_raw_n(data(), PageCapacity);
214 }
215
216 void dtr_data_inter(uint32_t idx) noexcept {
217 GAIA_ASSERT(!empty());
218
219 if constexpr (!mem::is_soa_layout_v<T>) {
220 auto* ptr = &data()[idx];
221 core::call_dtor(ptr);
222 }
223
224 m_pData->header.mask.set(idx, false);
225 --m_pData->header.cnt;
226 }
227
228 void dtr_active_data() noexcept {
229 if constexpr (!mem::is_soa_layout_v<T>) {
230 for (auto i: m_pData->header.mask) {
231 auto* ptr = &data()[i];
232 core::call_dtor(ptr);
233 }
234 }
235 }
236
237 void invalidate() {
238 if (m_pData == nullptr)
239 return;
240
241 // Destruct active items
242 dtr_active_data();
243
244 // Release allocated memory
245 m_pData->~PageData();
246 mem::AllocHelper::free<Allocator>(m_pData);
247 m_pData = nullptr;
248 }
249
250 public:
251 mem_page() = default;
252
253 mem_page(const mem_page& other) {
254 // Copy new items over
255 if (other.m_pData == nullptr) {
256 invalidate();
257 } else {
258 ensure();
259
260 m_pData->header.mask = other.m_pData->header.mask;
261 m_pData->header.cnt = other.m_pData->header.cnt;
262
263 // Copy construct data
264 for (auto i: other.m_pData->header.mask)
265 add_data(i, other.get_data(i));
266 }
267 }
268
269 mem_page& operator=(const mem_page& other) {
270 GAIA_ASSERT(core::addressof(other) != this);
271
272 // Copy new items over if there are any
273 if (other.m_pData == nullptr) {
274 invalidate();
275 } else {
276 ensure();
277
278 // Remove current active items
279 if (m_pData != nullptr)
280 dtr_active_data();
281
282 m_pData->header.mask = other.m_pData->header.mask;
283 m_pData->header.cnt = other.m_pData->header.cnt;
284
285 // Copy data
286 for (auto i: other.m_pData->header.mask)
287 add_data(i, other.get_data(i));
288 }
289
290 return *this;
291 }
292
293 mem_page(mem_page&& other) noexcept {
294 m_pData = other.m_pData;
295 other.m_pData = nullptr;
296 }
297
298 mem_page& operator=(mem_page&& other) noexcept {
299 GAIA_ASSERT(core::addressof(other) != this);
300
301 invalidate();
302
303 m_pData = other.m_pData;
304 other.m_pData = nullptr;
305
306 return *this;
307 }
308
309 ~mem_page() {
310 invalidate();
311 }
312
313 GAIA_CLANG_WARNING_PUSH()
314 // Memory is aligned so we can silence this warning
315 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
316
317 GAIA_NODISCARD pointer data() noexcept {
318 return GAIA_ACC((pointer)&m_pData->data[0]);
319 }
320
321 GAIA_NODISCARD const_pointer data() const noexcept {
322 return GAIA_ACC((const_pointer)&m_pData->data[0]);
323 }
324
325 GAIA_NODISCARD decltype(auto) set_data(size_type pos) noexcept {
326 GAIA_ASSERT(m_pData->header.mask.test(pos));
327 return view_policy::set(
328 {GAIA_ACC((typename view_policy::TargetCastType) & m_pData->data[0]), PageCapacity}, pos);
329 }
330
331 GAIA_NODISCARD decltype(auto) operator[](size_type pos) noexcept {
332 GAIA_ASSERT(m_pData->header.mask.test(pos));
333 return view_policy::set(
334 {GAIA_ACC((typename view_policy::TargetCastType) & m_pData->data[0]), PageCapacity}, pos);
335 }
336
337 GAIA_NODISCARD decltype(auto) get_data(size_type pos) const noexcept {
338 GAIA_ASSERT(m_pData->header.mask.test(pos));
339 return view_policy::get(
340 {GAIA_ACC((typename view_policy::TargetCastType) & m_pData->data[0]), PageCapacity}, pos);
341 }
342
343 GAIA_NODISCARD decltype(auto) operator[](size_type pos) const noexcept {
344 GAIA_ASSERT(m_pData->header.mask.test(pos));
345 return view_policy::get(
346 {GAIA_ACC((typename view_policy::TargetCastType) & m_pData->data[0]), PageCapacity}, pos);
347 }
348
349 GAIA_CLANG_WARNING_POP()
350
351 void add() {
352 ensure();
353 ++m_pData->header.cnt;
354 }
355
356 decltype(auto) add_data(uint32_t idx, const T& arg) {
357 m_pData->header.mask.set(idx);
358
359 if constexpr (mem::is_soa_layout_v<T>) {
360 set_data(idx) = arg;
361 } else {
362 auto* ptr = &set_data(idx);
363 core::call_ctor(ptr, arg);
364 return (reference)(*ptr);
365 }
366 }
367
368 decltype(auto) add_data(uint32_t idx, T&& arg) {
369 m_pData->header.mask.set(idx);
370
371 if constexpr (mem::is_soa_layout_v<T>) {
372 set_data(idx) = GAIA_MOV(arg);
373 } else {
374 auto* ptr = &set_data(idx);
375 core::call_ctor(ptr, GAIA_MOV(arg));
376 return (reference)(*ptr);
377 }
378 }
379
380 template <typename... Args>
381 decltype(auto) emplace_data(uint32_t idx, Args&&... args) {
382 m_pData->header.used.set(idx);
383
384 if constexpr (mem::is_soa_layout_v<T>) {
385 set_data(idx) = T(GAIA_FWD(args)...);
386 } else {
387 auto* ptr = &set_data(idx);
388 core::call_ctor(ptr, GAIA_FWD(args)...);
389 return (reference)(*ptr);
390 }
391 }
392
393 void del_data(uint32_t idx) noexcept {
394 dtr_data_inter(idx);
395
396 // If there is no more data, release the memory allocated by the page
397 if (m_pData->header.cnt == 0)
398 invalidate();
399 }
400
401 GAIA_NODISCARD bool has_data(uint32_t idx) const noexcept {
402 return m_pData ? m_pData->header.mask.test(idx) : false;
403 }
404
405 GAIA_NODISCARD size_type size() const noexcept {
406 return m_pData ? m_pData->header.cnt : 0;
407 }
408
409 GAIA_NODISCARD bool empty() const noexcept {
410 return size() == 0;
411 }
412
413 GAIA_NODISCARD decltype(auto) front() noexcept {
414 GAIA_ASSERT(!empty());
415 if constexpr (mem::is_soa_layout_v<T>)
416 return *begin();
417 else
418 return (reference)*begin();
419 }
420
421 GAIA_NODISCARD decltype(auto) front() const noexcept {
422 GAIA_ASSERT(!empty());
423 if constexpr (mem::is_soa_layout_v<T>)
424 return *begin();
425 else
426 return (const_reference)*begin();
427 }
428
429 GAIA_NODISCARD decltype(auto) back() noexcept {
430 GAIA_ASSERT(!empty());
431 const auto idx = *m_pData->header.mask.rbegin();
432 if constexpr (mem::is_soa_layout_v<T>)
433 return set_data(idx);
434 else
435 return (reference)(set_data(idx));
436 }
437
438 GAIA_NODISCARD decltype(auto) back() const noexcept {
439 GAIA_ASSERT(!empty());
440 const auto idx = *m_pData->header.mask.rbegin();
441 if constexpr (mem::is_soa_layout_v<T>)
442 return set_data(idx);
443 else
444 return (const_reference)set_data(idx);
445 }
446
447 static constexpr typename PageData::bit_set s_dummyBitSet{};
448
449 GAIA_NODISCARD auto begin() const noexcept {
450 if constexpr (mem::is_soa_layout_v<T>)
451 return iterator_soa((mem_page*)this, m_pData ? m_pData->header.mask.begin() : s_dummyBitSet.begin());
452 else
453 return iterator((mem_page*)this, m_pData ? m_pData->header.mask.begin() : s_dummyBitSet.begin());
454 }
455
456 GAIA_NODISCARD auto end() const noexcept {
457 if constexpr (mem::is_soa_layout_v<T>)
458 return iterator_soa((mem_page*)this, m_pData ? m_pData->header.mask.end() : s_dummyBitSet.end());
459 else
460 return iterator((mem_page*)this, m_pData ? m_pData->header.mask.end() : s_dummyBitSet.end());
461 }
462
463 GAIA_NODISCARD auto rbegin() const noexcept {
464 if constexpr (mem::is_soa_layout_v<T>)
466 (mem_page*)this, m_pData ? m_pData->header.mask.rbegin() : s_dummyBitSet.rbegin());
467 else
468 return iterator_reverse((mem_page*)this, m_pData ? m_pData->header.mask.rbegin() : s_dummyBitSet.rbegin());
469 }
470
471 GAIA_NODISCARD auto rend() const noexcept {
472 if constexpr (mem::is_soa_layout_v<T>)
473 return iterator_soa_reverse((mem_page*)this, m_pData ? m_pData->header.mask.rend() : s_dummyBitSet.rend());
474 else
475 return iterator_reverse((mem_page*)this, m_pData ? m_pData->header.mask.rend() : s_dummyBitSet.rend());
476 }
477
478 GAIA_NODISCARD bool operator==(const mem_page& other) const noexcept {
479 // We expect to compare only valid pages
480 GAIA_ASSERT(m_pData != nullptr);
481 GAIA_ASSERT(other.m_pData != nullptr);
482
483 if (m_pData->header.cnt != other.m_pData->header.cnt)
484 return false;
485 if (m_pData->header.mask != other.m_pData->header.mask)
486 return false;
487 for (auto i: m_pData->header.mask)
488 if (!(get_data(i) == other[i]))
489 return false;
490 return true;
491 }
492
493 GAIA_NODISCARD bool operator!=(const mem_page& other) const noexcept {
494 return !operator==(other);
495 }
496 };
497 } // namespace detail
498
499 template <typename T, typename Allocator, bool IsFwd>
501 using value_type = T;
502 using pointer = T*;
503 using reference = T&;
506 using iterator = page_iterator;
508
509 private:
511
512 page_type* m_pPage;
513 page_type* m_pPageLast;
514 typename page_type::template iterator_base<IsFwd> m_it;
515
516 public:
517 page_iterator(page_type* pPage): m_pPage(pPage), m_pPageLast(pPage) {}
518
519 page_iterator(page_type* pPage, page_type* pPageLast): m_pPage(pPage), m_pPageLast(pPageLast) {
520 // Find first page with data
521 if constexpr (!IsFwd) {
522 m_it = m_pPage->rbegin();
523 while (m_it == m_pPage->rend()) {
524 --m_pPage;
525 if (m_pPage == m_pPageLast) {
526 m_it = {};
527 break;
528 }
529 m_it = m_pPage->rbegin();
530 }
531 } else {
532 m_it = m_pPage->begin();
533 while (m_it == m_pPage->end()) {
534 ++m_pPage;
535 if (m_pPage == m_pPageLast) {
536 m_it = {};
537 break;
538 }
539 m_it = m_pPage->begin();
540 }
541 }
542 }
543
544 reference operator*() const {
545 return m_it.operator*();
546 }
547 pointer operator->() const {
548 return m_it.operator->();
549 }
550
551 iterator& operator++() {
552 if constexpr (!IsFwd) {
553 ++m_it;
554 if (m_it == m_pPage->rend()) {
555 --m_pPage;
556 if (m_pPage == m_pPageLast) {
557 m_it = {};
558 return *this;
559 }
560 m_it = m_pPage->rbegin();
561 }
562 } else {
563 ++m_it;
564 if (m_it == m_pPage->end()) {
565 ++m_pPage;
566 if (m_pPage == m_pPageLast) {
567 m_it = {};
568 return *this;
569 }
570 m_it = m_pPage->begin();
571 }
572 }
573 return *this;
574 }
575 iterator operator++(int) {
576 iterator temp(*this);
577 ++*this;
578 return temp;
579 }
580
581 GAIA_NODISCARD bool operator==(const iterator& other) const {
582 return m_pPage == other.m_pPage && m_it == other.m_it;
583 }
584 GAIA_NODISCARD bool operator!=(const iterator& other) const {
585 return m_pPage != other.m_pPage || m_it != other.m_it;
586 }
587 };
588
589 template <typename T, typename Allocator, bool IsFwd>
591 using value_type = T;
592 using pointer = const T*;
593 using reference = const T&;
598
599 private:
601
602 const page_type* m_pPage;
603 const page_type* m_pPageLast;
604 typename page_type::template iterator_base<IsFwd> m_it;
605
606 public:
607 const_page_iterator(const page_type* pPage): m_pPage(pPage), m_pPageLast(pPage) {}
608
609 const_page_iterator(const page_type* pPage, const page_type* pPageLast): m_pPage(pPage), m_pPageLast(pPageLast) {
610 // Find first page with data
611 if constexpr (!IsFwd) {
612 m_it = m_pPage->rbegin();
613 while (m_it == m_pPage->rend()) {
614 --m_pPage;
615 if (m_pPage == m_pPageLast) {
616 m_it = {};
617 break;
618 }
619 m_it = m_pPage->rbegin();
620 }
621 } else {
622 m_it = m_pPage->begin();
623 while (m_it == m_pPage->end()) {
624 ++m_pPage;
625 if (m_pPage == m_pPageLast) {
626 m_it = {};
627 break;
628 }
629 m_it = m_pPage->begin();
630 }
631 }
632 }
633
634 reference operator*() const {
635 return m_it.operator*();
636 }
637 pointer operator->() const {
638 return m_it.operator->();
639 }
640
641 iterator& operator++() {
642 if constexpr (!IsFwd) {
643 ++m_it;
644 if (m_it == m_pPage->rend()) {
645 --m_pPage;
646 if (m_pPage == m_pPageLast) {
647 m_it = {};
648 return *this;
649 }
650 m_it = m_pPage->rbegin();
651 }
652 } else {
653 ++m_it;
654 if (m_it == m_pPage->end()) {
655 ++m_pPage;
656 if (m_pPage == m_pPageLast) {
657 m_it = {};
658 return *this;
659 }
660 m_it = m_pPage->begin();
661 }
662 }
663 return *this;
664 }
665 iterator operator++(int) {
666 iterator temp(*this);
667 ++*this;
668 return temp;
669 }
670
671 GAIA_NODISCARD bool operator==(const iterator& other) const {
672 return m_pPage == other.m_pPage && m_it == other.m_it;
673 }
674 GAIA_NODISCARD bool operator!=(const iterator& other) const {
675 return m_pPage != other.m_pPage || m_it != other.m_it;
676 }
677 };
678
679 template <typename T, typename Allocator, bool IsFwd>
681 using value_type = T;
682 // using pointer = T*;
683 // using reference = T&;
688
689 private:
691
692 page_type* m_pPage;
693 page_type* m_pPageLast;
694 typename page_type::template iterator_soa_base<IsFwd> m_it;
695
696 public:
697 page_iterator_soa(page_type* pPage): m_pPage(pPage), m_pPageLast(pPage) {}
698
700 // Find first page with data
701 if constexpr (!IsFwd) {
702 m_it = m_pPage->rbegin();
703 while (m_it == m_pPage->rend()) {
704 if (m_pPage == m_pPageLast)
705 break;
706 --m_pPage;
707 m_it = m_pPage->rbegin();
708 }
709 } else {
710 m_it = m_pPage->begin();
711 while (m_it == m_pPage->end()) {
712 if (m_pPage == m_pPageLast)
713 break;
714 ++m_pPage;
715 m_it = m_pPage->begin();
716 }
717 }
718 }
719
720 value_type operator*() const {
721 return m_it.operator*();
722 }
723 value_type operator->() const {
724 return m_it.operator->();
725 }
726
727 iterator& operator++() {
728 if constexpr (!IsFwd) {
729 ++m_it;
730 while (m_it == m_pPage->rend()) {
731 --m_pPage;
732 m_it = m_pPage->rbegin();
733 }
734 } else {
735 ++m_it;
736 while (m_it == m_pPage->end()) {
737 ++m_pPage;
738 m_it = m_pPage->begin();
739 }
740 }
741 return *this;
742 }
743 iterator operator++(int) {
744 iterator temp(*this);
745 ++*this;
746 return temp;
747 }
748
749 GAIA_NODISCARD bool operator==(const iterator& other) const {
750 return m_pPage == other.m_pPage && m_it == other.m_it;
751 }
752 GAIA_NODISCARD bool operator!=(const iterator& other) const {
753 return m_pPage != other.m_pPage || m_it != other.m_it;
754 }
755 };
756
757 template <typename T, typename Allocator, bool IsFwd>
759 using value_type = T;
760 // using pointer = T*;
761 // using reference = T&;
766
767 private:
769
770 const page_type* m_pPage;
771 const page_type* m_pPageLast;
772 typename page_type::template iterator_soa_base<IsFwd> m_it;
773
774 public:
775 const_page_iterator_soa(const page_type* pPage): m_pPage(pPage), m_pPageLast(pPage) {}
776
778 m_pPage(pPage), m_pPageLast(pPageLast) {
779 // Find first page with data
780 if constexpr (!IsFwd) {
781 m_it = m_pPage->rbegin();
782 while (m_it == m_pPage->rend()) {
783 if (m_pPage == m_pPageLast)
784 break;
785 --m_pPage;
786 m_it = m_pPage->rbegin();
787 }
788 } else {
789 m_it = m_pPage->begin();
790 while (m_it == m_pPage->end()) {
791 if (m_pPage == m_pPageLast)
792 break;
793 ++m_pPage;
794 m_it = m_pPage->begin();
795 }
796 }
797 }
798
799 value_type operator*() const {
800 return m_it.operator*();
801 }
802 value_type operator->() const {
803 return m_it.operator->();
804 }
805
806 iterator& operator++() {
807 if constexpr (!IsFwd) {
808 ++m_it;
809 while (m_it == m_pPage->rend()) {
810 --m_pPage;
811 m_it = m_pPage->rbegin();
812 }
813 } else {
814 ++m_it;
815 while (m_it == m_pPage->end()) {
816 ++m_pPage;
817 m_it = m_pPage->begin();
818 }
819 }
820 return *this;
821 }
822 iterator operator++(int) {
823 iterator temp(*this);
824 ++*this;
825 return temp;
826 }
827
828 GAIA_NODISCARD bool operator==(const iterator& other) const {
829 return m_pPage == other.m_pPage && m_it == other.m_it;
830 }
831 GAIA_NODISCARD bool operator!=(const iterator& other) const {
832 return m_pPage != other.m_pPage || m_it != other.m_it;
833 }
834 };
835
837 template <typename T>
839 public:
840 using value_type = T;
841 using reference = T&;
842 using const_reference = const T&;
843 using pointer = T*;
844 using const_pointer = const T*;
848
849 static constexpr uint32_t AllocatorBlockSize = (uint32_t)sizeof(detail::mem_page_data<T>);
851
854 static constexpr uint32_t PageCapacity = page_type::PageCapacity;
855
865
866 private:
867 constexpr static uint32_t PageMask = PageCapacity - 1;
868 constexpr static uint32_t ToPageIndex = core::count_bits(PageMask);
869
873 uint32_t m_itemCnt = 0;
874
875 void try_grow(uint32_t pid) {
876 // The page array has to be able to take any page index
877 if (pid >= m_pages.size())
878 m_pages.resize(pid + 1);
879
880 m_pages[pid].add();
881 ++m_itemCnt;
882 }
883
884 public:
885 constexpr page_storage() noexcept = default;
886
887 page_storage(const page_storage& other) {
888 m_pages = other.m_pages;
889 m_itemCnt = other.m_itemCnt;
890 }
891
892 page_storage& operator=(const page_storage& other) {
893 GAIA_ASSERT(core::addressof(other) != this);
894
895 m_pages = other.m_pages;
896 m_itemCnt = other.m_itemCnt;
897 return *this;
898 }
899
900 page_storage(page_storage&& other) noexcept {
901 m_pages = GAIA_MOV(other.m_pages);
902 m_itemCnt = other.m_itemCnt;
903
904 other.m_pages = {};
905 }
906
907 page_storage& operator=(page_storage&& other) noexcept {
908 GAIA_ASSERT(core::addressof(other) != this);
909
910 m_pages = GAIA_MOV(other.m_pages);
911 m_itemCnt = other.m_itemCnt;
912
913 other.m_pages = {};
914
915 return *this;
916 }
917
918 ~page_storage() = default;
919
920 GAIA_CLANG_WARNING_PUSH()
921 // Memory is aligned so we can silence this warning
922 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
923
924 GAIA_NODISCARD decltype(auto) operator[](page_storage_id id) noexcept {
925 GAIA_ASSERT(has(id));
926 const auto pid = size_type(id >> ToPageIndex);
927 const auto did = size_type(id & PageMask);
928 auto& page = m_pages[pid];
929 return view_policy::set({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
930 }
931
932 GAIA_NODISCARD decltype(auto) operator[](page_storage_id id) const noexcept {
933 GAIA_ASSERT(has(id));
934 const auto pid = size_type(id >> ToPageIndex);
935 const auto did = size_type(id & PageMask);
936 auto& page = m_pages[pid];
937 return view_policy::get({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
938 }
939
940 GAIA_CLANG_WARNING_POP()
941
942
944 GAIA_NODISCARD bool has(page_storage_id id) const noexcept {
945 const auto pid = size_type(id >> ToPageIndex);
946 if (pid >= m_pages.size())
947 return false;
948
949 const auto did = size_type(id & PageMask);
950 const auto val = page_data_type::bit_set::BitCount;
951 return did < val && m_pages[pid].has_data(did);
952 }
953
956 GAIA_NODISCARD bool has(const T& arg) const noexcept {
957 const auto id = to_page_storage_id<T>::get(arg);
958 GAIA_ASSERT(id != detail::InvalidPageStorageId);
959 return has(id);
960 }
961
965 template <typename TType>
966 decltype(auto) add(TType&& arg) {
967 const auto id = to_page_storage_id<T>::get(arg);
968 if (has(id)) {
969 if constexpr (mem::is_soa_layout_v<TType>)
970 return;
971 else {
972 const auto pid = size_type(id >> ToPageIndex);
973 const auto did = size_type(id & PageMask);
974 auto& page = m_pages[pid];
975 return page.set_data(did);
976 }
977 }
978
979 const auto pid = size_type(id >> ToPageIndex);
980 const auto did = size_type(id & PageMask);
981
982 try_grow(pid);
983
984 auto& page = m_pages[pid];
985 if constexpr (mem::is_soa_layout_v<TType>)
986 page.add_data(did, GAIA_FWD(arg));
987 else
988 return page.add_data(did, GAIA_FWD(arg));
989 }
990
994 decltype(auto) set(page_storage_id id) {
995 GAIA_ASSERT(has(id));
996
997 const auto pid = uint32_t(id >> ToPageIndex);
998 const auto did = uint32_t(id & PageMask);
999
1000 auto& page = m_pages[pid];
1001 return page.set_data(did);
1002 }
1003
1006 void del(page_storage_id id) noexcept {
1007 GAIA_ASSERT(!empty());
1008 GAIA_ASSERT(id != detail::InvalidPageStorageId);
1009
1010 if (!has(id))
1011 return;
1012
1013 const auto pid = uint32_t(id >> ToPageIndex);
1014 const auto did = uint32_t(id & PageMask);
1015
1016 auto& page = m_pages[pid];
1017 page.del_data(did);
1018 --m_itemCnt;
1019 }
1020
1023 void del(const T& arg) noexcept {
1024 const auto id = to_page_storage_id<T>::get(arg);
1025 return del(id);
1026 }
1027
1029 void clear() {
1030 m_pages.resize(0);
1031 m_itemCnt = 0;
1032 }
1033
1035 GAIA_NODISCARD size_type size() const noexcept {
1036 return m_itemCnt;
1037 }
1038
1040 GAIA_NODISCARD bool empty() const noexcept {
1041 return m_itemCnt == 0;
1042 }
1043
1044 GAIA_NODISCARD decltype(auto) front() noexcept {
1045 GAIA_ASSERT(!empty());
1046 return (reference)*begin();
1047 }
1048
1049 GAIA_NODISCARD decltype(auto) front() const noexcept {
1050 GAIA_ASSERT(!empty());
1051 return (const_reference)*begin();
1052 }
1053
1054 GAIA_NODISCARD decltype(auto) back() noexcept {
1055 GAIA_ASSERT(!empty());
1056 return (reference)*rbegin();
1057 }
1058
1059 GAIA_NODISCARD decltype(auto) back() const noexcept {
1060 GAIA_ASSERT(!empty());
1061 return (const_reference)*rbegin();
1062 }
1063
1064 GAIA_NODISCARD auto begin() noexcept {
1065 GAIA_ASSERT(!empty());
1066 return iterator(m_pages.data(), m_pages.data() + m_pages.size());
1067 }
1068
1069 GAIA_NODISCARD auto begin() const noexcept {
1070 GAIA_ASSERT(!empty());
1071 return const_iterator(m_pages.data(), m_pages.data() + m_pages.size());
1072 }
1073
1074 GAIA_NODISCARD auto cbegin() const noexcept {
1075 GAIA_ASSERT(!empty());
1076 return const_iterator(m_pages.data(), m_pages.data() + m_pages.size());
1077 }
1078
1079 GAIA_NODISCARD auto end() noexcept {
1080 GAIA_ASSERT(!empty());
1081 return iterator(m_pages.data() + m_pages.size());
1082 }
1083
1084 GAIA_NODISCARD auto end() const noexcept {
1085 GAIA_ASSERT(!empty());
1086 return const_iterator(m_pages.data() + m_pages.size());
1087 }
1088
1089 GAIA_NODISCARD auto cend() const noexcept {
1090 GAIA_ASSERT(!empty());
1091 return const_iterator(m_pages.data() + m_pages.size());
1092 }
1093
1094 GAIA_NODISCARD auto rbegin() noexcept {
1095 GAIA_ASSERT(!empty());
1096 return iterator_reverse(m_pages.data() + m_pages.size() - 1, m_pages.data() - 1);
1097 }
1098
1099 GAIA_NODISCARD auto rbegin() const noexcept {
1100 GAIA_ASSERT(!empty());
1101 return const_iterator_reverse(m_pages.data() + m_pages.size() - 1, m_pages.data() - 1);
1102 }
1103
1104 GAIA_NODISCARD auto crbegin() const noexcept {
1105 GAIA_ASSERT(!empty());
1106 return const_iterator_reverse(m_pages.data() + m_pages.size() - 1, m_pages.data() - 1);
1107 }
1108
1109 GAIA_NODISCARD auto rend() noexcept {
1110 GAIA_ASSERT(!empty());
1111 return iterator_reverse(m_pages.data() - 1);
1112 }
1113
1114 GAIA_NODISCARD auto rend() const noexcept {
1115 GAIA_ASSERT(!empty());
1116 return const_iterator_reverse(m_pages.data() - 1);
1117 }
1118
1119 GAIA_NODISCARD auto crend() const noexcept {
1120 GAIA_ASSERT(!empty());
1121 return const_iterator_reverse(m_pages.data() - 1);
1122 }
1123
1124 GAIA_NODISCARD bool operator==(const page_storage& other) const noexcept {
1125 return m_pages == other.m_pages;
1126 }
1127
1128 GAIA_NODISCARD bool operator!=(const page_storage& other) const noexcept {
1129 return !operator==(other);
1130 }
1131 };
1132 } // namespace cnt
1133
1134} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:25
Definition paged_storage.h:175
Heap-allocated paged storage for elements of type T.
Definition paged_storage.h:838
decltype(auto) add(TType &&arg)
Inserts the item arg into the storage.
Definition paged_storage.h:966
void clear()
Clears the storage.
Definition paged_storage.h:1029
void del(const T &arg) noexcept
Removes the item arg from the storage.
Definition paged_storage.h:1023
decltype(auto) set(page_storage_id id)
Update the record at the index id.
Definition paged_storage.h:994
GAIA_NODISCARD size_type size() const noexcept
Returns the number of items inserted into the storage.
Definition paged_storage.h:1035
GAIA_NODISCARD bool empty() const noexcept
Checks if the storage is empty (no items inserted)
Definition paged_storage.h:1040
GAIA_NODISCARD bool has(const T &arg) const noexcept
Checks if an item arg exists within the storage.
Definition paged_storage.h:956
void del(page_storage_id id) noexcept
Removes the item at the index id from the storage.
Definition paged_storage.h:1006
GAIA_NODISCARD bool has(page_storage_id id) const noexcept
Checks if an item with a given page id exists.
Definition paged_storage.h:944
Gaia-ECS is a header-only library which means we want to avoid using global static variables because ...
Definition dyn_singleton.h:21
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition paged_storage.h:758
Definition paged_storage.h:590
Definition paged_storage.h:161
bit_set mask
Mask for used data.
Definition paged_storage.h:165
size_type cnt
How many items are allocated inside the page.
Definition paged_storage.h:163
Definition paged_storage.h:154
PageHeader header
Page header.
Definition paged_storage.h:169
mem::raw_data_holder< T, AllocatedBytes > data
Page data.
Definition paged_storage.h:171
Definition paged_storage.h:98
Definition paged_storage.h:49
Definition paged_storage.h:680
Definition paged_storage.h:500
Definition paged_storage.h:37
Definition data_layout_policy.h:87
Definition raw_data_holder.h:12