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&;
53 using difference_type = detail::difference_type;
54 using size_type = detail::size_type;
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;
69 mem_page_iterator(page_type* pPage): m_pPage(pPage) {}
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
102 using difference_type = detail::difference_type;
103 using size_type = detail::size_type;
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*;
185 using difference_type = detail::difference_type;
186 using size_type = detail::size_type;
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&;
504 using difference_type = detail::difference_type;
505 using size_type = detail::size_type;
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&;
594 using difference_type = detail::difference_type;
595 using size_type = detail::size_type;
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&;
684 using difference_type = detail::difference_type;
685 using size_type = detail::size_type;
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
699 page_iterator_soa(page_type* pPage, page_type* pPageLast): m_pPage(pPage), m_pPageLast(pPageLast) {
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&;
762 using difference_type = detail::difference_type;
763 using size_type = detail::size_type;
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
777 const_page_iterator_soa(const page_type* pPage, const page_type* pPageLast):
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
839 template <typename T>
841 public:
842 using value_type = T;
843 using reference = T&;
844 using const_reference = const T&;
845 using pointer = T*;
846 using const_pointer = const T*;
848 using difference_type = detail::difference_type;
849 using size_type = detail::size_type;
850
851 static constexpr uint32_t AllocatorBlockSize = (uint32_t)sizeof(detail::mem_page_data<T>);
853
856 static constexpr uint32_t PageCapacity = page_type::PageCapacity;
857
867
868 private:
869 constexpr static uint32_t PageMask = PageCapacity - 1;
870 constexpr static uint32_t ToPageIndex = core::count_bits(PageMask);
871
875 uint32_t m_itemCnt = 0;
876
877 void try_grow(uint32_t pid) {
878 // The page array has to be able to take any page index
879 if (pid >= m_pages.size())
880 m_pages.resize(pid + 1);
881
882 m_pages[pid].add();
883 ++m_itemCnt;
884 }
885
886 public:
887 constexpr page_storage() noexcept = default;
888
889 page_storage(const page_storage& other) {
890 m_pages = other.m_pages;
891 m_itemCnt = other.m_itemCnt;
892 }
893
894 page_storage& operator=(const page_storage& other) {
895 GAIA_ASSERT(core::addressof(other) != this);
896
897 m_pages = other.m_pages;
898 m_itemCnt = other.m_itemCnt;
899 return *this;
900 }
901
902 page_storage(page_storage&& other) noexcept {
903 m_pages = GAIA_MOV(other.m_pages);
904 m_itemCnt = other.m_itemCnt;
905
906 other.m_pages = {};
907 }
908
909 page_storage& operator=(page_storage&& other) noexcept {
910 GAIA_ASSERT(core::addressof(other) != this);
911
912 m_pages = GAIA_MOV(other.m_pages);
913 m_itemCnt = other.m_itemCnt;
914
915 other.m_pages = {};
916
917 return *this;
918 }
919
920 ~page_storage() = default;
921
922 GAIA_CLANG_WARNING_PUSH()
923 // Memory is aligned so we can silence this warning
924 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
925
926 GAIA_NODISCARD decltype(auto) operator[](page_storage_id id) noexcept {
927 GAIA_ASSERT(has(id));
928 const auto pid = size_type(id >> ToPageIndex);
929 const auto did = size_type(id & PageMask);
930 auto& page = m_pages[pid];
931 return view_policy::set({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
932 }
933
934 GAIA_NODISCARD decltype(auto) operator[](page_storage_id id) const noexcept {
935 GAIA_ASSERT(has(id));
936 const auto pid = size_type(id >> ToPageIndex);
937 const auto did = size_type(id & PageMask);
938 auto& page = m_pages[pid];
939 return view_policy::get({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
940 }
941
942 GAIA_CLANG_WARNING_POP()
943
944
946 GAIA_NODISCARD bool has(page_storage_id id) const noexcept {
947 const auto pid = size_type(id >> ToPageIndex);
948 if (pid >= m_pages.size())
949 return false;
950
951 const auto did = size_type(id & PageMask);
952 const auto val = page_data_type::bit_set::BitCount;
953 return did < val && m_pages[pid].has_data(did);
954 }
955
958 GAIA_NODISCARD bool has(const T& arg) const noexcept {
959 const auto id = to_page_storage_id<T>::get(arg);
960 GAIA_ASSERT(id != detail::InvalidPageStorageId);
961 return has(id);
962 }
963
967 template <typename TType>
968 decltype(auto) add(TType&& arg) {
969 const auto id = to_page_storage_id<T>::get(arg);
970 if (has(id)) {
971 if constexpr (mem::is_soa_layout_v<TType>)
972 return;
973 else {
974 const auto pid = size_type(id >> ToPageIndex);
975 const auto did = size_type(id & PageMask);
976 auto& page = m_pages[pid];
977 return page.set_data(did);
978 }
979 }
980
981 const auto pid = size_type(id >> ToPageIndex);
982 const auto did = size_type(id & PageMask);
983
984 try_grow(pid);
985
986 auto& page = m_pages[pid];
987 if constexpr (mem::is_soa_layout_v<TType>)
988 page.add_data(did, GAIA_FWD(arg));
989 else
990 return page.add_data(did, GAIA_FWD(arg));
991 }
992
996 decltype(auto) set(page_storage_id id) {
997 GAIA_ASSERT(has(id));
998
999 const auto pid = uint32_t(id >> ToPageIndex);
1000 const auto did = uint32_t(id & PageMask);
1001
1002 auto& page = m_pages[pid];
1003 return page.set_data(did);
1004 }
1005
1008 void del(page_storage_id id) noexcept {
1009 GAIA_ASSERT(!empty());
1010 GAIA_ASSERT(id != detail::InvalidPageStorageId);
1011
1012 if (!has(id))
1013 return;
1014
1015 const auto pid = uint32_t(id >> ToPageIndex);
1016 const auto did = uint32_t(id & PageMask);
1017
1018 auto& page = m_pages[pid];
1019 page.del_data(did);
1020 --m_itemCnt;
1021 }
1022
1025 void del(const T& arg) noexcept {
1026 const auto id = to_page_storage_id<T>::get(arg);
1027 return del(id);
1028 }
1029
1031 void clear() {
1032 m_pages.resize(0);
1033 m_itemCnt = 0;
1034 }
1035
1037 GAIA_NODISCARD size_type size() const noexcept {
1038 return m_itemCnt;
1039 }
1040
1042 GAIA_NODISCARD bool empty() const noexcept {
1043 return m_itemCnt == 0;
1044 }
1045
1046 GAIA_NODISCARD decltype(auto) front() noexcept {
1047 GAIA_ASSERT(!empty());
1048 return (reference)*begin();
1049 }
1050
1051 GAIA_NODISCARD decltype(auto) front() const noexcept {
1052 GAIA_ASSERT(!empty());
1053 return (const_reference)*begin();
1054 }
1055
1056 GAIA_NODISCARD decltype(auto) back() noexcept {
1057 GAIA_ASSERT(!empty());
1058 return (reference)*rbegin();
1059 }
1060
1061 GAIA_NODISCARD decltype(auto) back() const noexcept {
1062 GAIA_ASSERT(!empty());
1063 return (const_reference)*rbegin();
1064 }
1065
1066 GAIA_NODISCARD auto begin() noexcept {
1067 GAIA_ASSERT(!empty());
1068 return iterator(m_pages.data(), m_pages.data() + m_pages.size());
1069 }
1070
1071 GAIA_NODISCARD auto begin() const noexcept {
1072 GAIA_ASSERT(!empty());
1073 return const_iterator(m_pages.data(), m_pages.data() + m_pages.size());
1074 }
1075
1076 GAIA_NODISCARD auto cbegin() const noexcept {
1077 GAIA_ASSERT(!empty());
1078 return const_iterator(m_pages.data(), m_pages.data() + m_pages.size());
1079 }
1080
1081 GAIA_NODISCARD auto end() noexcept {
1082 GAIA_ASSERT(!empty());
1083 return iterator(m_pages.data() + m_pages.size());
1084 }
1085
1086 GAIA_NODISCARD auto end() const noexcept {
1087 GAIA_ASSERT(!empty());
1088 return const_iterator(m_pages.data() + m_pages.size());
1089 }
1090
1091 GAIA_NODISCARD auto cend() const noexcept {
1092 GAIA_ASSERT(!empty());
1093 return const_iterator(m_pages.data() + m_pages.size());
1094 }
1095
1096 GAIA_NODISCARD auto rbegin() noexcept {
1097 GAIA_ASSERT(!empty());
1098 return iterator_reverse(m_pages.data() + m_pages.size() - 1, m_pages.data() - 1);
1099 }
1100
1101 GAIA_NODISCARD auto rbegin() const noexcept {
1102 GAIA_ASSERT(!empty());
1103 return const_iterator_reverse(m_pages.data() + m_pages.size() - 1, m_pages.data() - 1);
1104 }
1105
1106 GAIA_NODISCARD auto crbegin() const noexcept {
1107 GAIA_ASSERT(!empty());
1108 return const_iterator_reverse(m_pages.data() + m_pages.size() - 1, m_pages.data() - 1);
1109 }
1110
1111 GAIA_NODISCARD auto rend() noexcept {
1112 GAIA_ASSERT(!empty());
1113 return iterator_reverse(m_pages.data() - 1);
1114 }
1115
1116 GAIA_NODISCARD auto rend() const noexcept {
1117 GAIA_ASSERT(!empty());
1118 return const_iterator_reverse(m_pages.data() - 1);
1119 }
1120
1121 GAIA_NODISCARD auto crend() const noexcept {
1122 GAIA_ASSERT(!empty());
1123 return const_iterator_reverse(m_pages.data() - 1);
1124 }
1125
1126 GAIA_NODISCARD bool operator==(const page_storage& other) const noexcept {
1127 return m_pages == other.m_pages;
1128 }
1129
1130 GAIA_NODISCARD bool operator!=(const page_storage& other) const noexcept {
1131 return !operator==(other);
1132 }
1133 };
1134 } // namespace cnt
1135
1136} // namespace gaia
GAIA_NODISCARD constexpr bool test(uint32_t pos) const
Returns the value of the bit at the position.
Definition bitset.h:202
constexpr void set()
Sets all bits.
Definition bitset.h:116
Array with variable size of elements of type.
Definition darray_impl.h:25
Definition paged_storage.h:175
Array with variable size of elements of type.
Definition paged_storage.h:840
decltype(auto) add(TType &&arg)
Inserts the item arg into the storage.
Definition paged_storage.h:968
void clear()
Clears the storage.
Definition paged_storage.h:1031
void del(const T &arg) noexcept
Removes the item arg from the storage.
Definition paged_storage.h:1025
decltype(auto) set(page_storage_id id)
Update the record at the index id.
Definition paged_storage.h:996
GAIA_NODISCARD size_type size() const noexcept
Returns the number of items inserted into the storage.
Definition paged_storage.h:1037
GAIA_NODISCARD bool empty() const noexcept
Checks if the storage is empty (no items inserted)
Definition paged_storage.h:1042
GAIA_NODISCARD bool has(const T &arg) const noexcept
Checks if an item arg exists within the storage.
Definition paged_storage.h:958
void del(page_storage_id id) noexcept
Removes the item at the index id from the storage.
Definition paged_storage.h:1008
GAIA_NODISCARD bool has(page_storage_id id) const noexcept
Checks if an item with a given page id exists.
Definition paged_storage.h:946
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