Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
sparse_storage.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstddef>
5#include <initializer_list>
6#include <type_traits>
7#include <utility>
8
9#include "gaia/cnt/darray.h"
10#include "gaia/core/iterator.h"
11#include "gaia/core/utility.h"
12#include "gaia/mem/data_layout_policy.h"
13#include "gaia/mem/mem_utils.h"
14
15namespace gaia {
16 namespace cnt {
18 using sparse_id = uint64_t;
19
20 namespace detail {
22 using difference_type = uint32_t;
23 using size_type = uint32_t;
24
25 constexpr static sparse_id InvalidSparseId = (sparse_id)-1;
26 constexpr static size_type InvalidDenseId = BadIndex - 1;
27 inline constexpr sparse_id EmptyDenseId = InvalidSparseId;
29
34 template <typename Dense>
35 GAIA_NODISCARD auto sparse_dense_data(Dense& dense) noexcept {
36 auto* pData = dense.data();
37 return pData != nullptr ? pData : &EmptyDenseId;
38 }
39
41 template <typename T, uint32_t PageCapacity, typename Allocator, typename>
42 class sparse_page;
44 } // namespace detail
45
46 template <typename T>
49 struct to_sparse_id {
53 static sparse_id get(const T& item) noexcept {
54 (void)item;
55 static_assert(
56 std::is_empty_v<T>,
57 "Sparse_storage items require a conversion function to be defined in gaia::cnt namespace");
58 return detail::InvalidSparseId;
59 }
60 };
61
62 template <typename T, uint32_t PageCapacity, typename Allocator, typename = void>
69 using value_type = T;
70 using pointer = T*;
71 using reference = T&;
72 using difference_type = detail::difference_type;
73 using size_type = detail::size_type;
75
76 private:
77 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_iterator must be a power of 2");
78 constexpr static sparse_id page_mask = PageCapacity - 1;
79 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
80
81 using page_type = detail::sparse_page<T, PageCapacity, Allocator, void>;
82
83 const sparse_id* m_pDense;
84 page_type* m_pPages;
85
86 public:
90 sparse_iterator(const sparse_id* pDense, page_type* pPages): m_pDense(pDense), m_pPages(pPages) {}
91
95 const auto sid = *m_pDense;
96 const auto pid = uint32_t(sid >> to_page_index);
97 const auto did = uint32_t(sid & page_mask);
98 auto& page = m_pPages[pid];
99 return page.set_data(did);
100 }
104 const auto sid = *m_pDense;
105 const auto pid = uint32_t(sid >> to_page_index);
106 const auto did = uint32_t(sid & page_mask);
107 auto& page = m_pPages[pid];
108 return &page.set_data(did);
109 }
114 return {m_pDense + offset, m_pPages};
115 }
116
121 m_pDense += diff;
122 return *this;
123 }
128 m_pDense -= diff;
129 return *this;
130 }
134 ++m_pDense;
135 return *this;
136 }
140 iterator temp(*this);
141 ++*this;
142 return temp;
143 }
147 --m_pDense;
148 return *this;
149 }
153 iterator temp(*this);
154 --*this;
155 return temp;
156 }
157
162 return {m_pDense + offset, m_pPages};
163 }
168 return {m_pDense - offset, m_pPages};
169 }
173 difference_type operator-(const iterator& other) const {
174 return (difference_type)(m_pDense - other.m_pDense);
175 }
176
180 GAIA_NODISCARD bool operator==(const iterator& other) const {
181 return m_pDense == other.m_pDense;
182 }
186 GAIA_NODISCARD bool operator!=(const iterator& other) const {
187 return m_pDense != other.m_pDense;
188 }
192 GAIA_NODISCARD bool operator>(const iterator& other) const {
193 return m_pDense > other.m_pDense;
194 }
198 GAIA_NODISCARD bool operator>=(const iterator& other) const {
199 return m_pDense >= other.m_pDense;
200 }
204 GAIA_NODISCARD bool operator<(const iterator& other) const {
205 return m_pDense < other.m_pDense;
206 }
210 GAIA_NODISCARD bool operator<=(const iterator& other) const {
211 return m_pDense <= other.m_pDense;
212 }
213 };
214
215 template <typename T, uint32_t PageCapacity, typename Allocator, typename = void>
222 using value_type = T;
223 using pointer = const T*;
224 using reference = const T&;
225 using difference_type = detail::difference_type;
226 using size_type = detail::size_type;
228
229 private:
230 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_iterator must be a power of 2");
231 constexpr static sparse_id page_mask = PageCapacity - 1;
232 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
233
234 using page_type = detail::sparse_page<T, PageCapacity, Allocator, void>;
235
236 const sparse_id* m_pDense;
237 const page_type* m_pPages;
238
239 public:
243 const_sparse_iterator(const sparse_id* pDense, const page_type* pPages): m_pDense(pDense), m_pPages(pPages) {}
244
248 const auto sid = *m_pDense;
249 const auto pid = uint32_t(sid >> to_page_index);
250 const auto did = uint32_t(sid & page_mask);
251 auto& page = m_pPages[pid];
252 return page.get_data(did);
253 }
257 const auto sid = *m_pDense;
258 const auto pid = uint32_t(sid >> to_page_index);
259 const auto did = uint32_t(sid & page_mask);
260 auto& page = m_pPages[pid];
261 return &page.get_data(did);
262 }
267 return {m_pDense + offset, m_pPages};
268 }
269
274 m_pDense += diff;
275 return *this;
276 }
281 m_pDense -= diff;
282 return *this;
283 }
287 ++m_pDense;
288 return *this;
289 }
293 iterator temp(*this);
294 ++*this;
295 return temp;
296 }
300 --m_pDense;
301 return *this;
302 }
306 iterator temp(*this);
307 --*this;
308 return temp;
309 }
310
315 return {m_pDense + offset, m_pPages};
316 }
321 return {m_pDense - offset, m_pPages};
322 }
326 difference_type operator-(const iterator& other) const {
327 return (difference_type)(m_pDense - other.m_pDense);
328 }
329
333 GAIA_NODISCARD bool operator==(const iterator& other) const {
334 return m_pDense == other.m_pDense;
335 }
339 GAIA_NODISCARD bool operator!=(const iterator& other) const {
340 return m_pDense != other.m_pDense;
341 }
345 GAIA_NODISCARD bool operator>(const iterator& other) const {
346 return m_pDense > other.m_pDense;
347 }
351 GAIA_NODISCARD bool operator>=(const iterator& other) const {
352 return m_pDense >= other.m_pDense;
353 }
357 GAIA_NODISCARD bool operator<(const iterator& other) const {
358 return m_pDense < other.m_pDense;
359 }
363 GAIA_NODISCARD bool operator<=(const iterator& other) const {
364 return m_pDense <= other.m_pDense;
365 }
366 };
367
368 template <typename T, uint32_t PageCapacity, typename Allocator>
373 struct sparse_iterator<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>> {
375 using value_type = sparse_id;
376 // using pointer = sparse_id*; not supported
377 // using reference = sparse_id&; not supported
378 using difference_type = detail::difference_type;
379 using size_type = detail::size_type;
381
382 private:
383 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_iterator must be a power of 2");
384 constexpr static sparse_id page_mask = PageCapacity - 1;
385 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
386
387 using page_type = detail::sparse_page<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>>;
388
389 const value_type* m_pDense;
390
391 public:
395
399 const auto sid = *m_pDense;
400 return sid;
401 }
405 const auto sid = *m_pDense;
406 return sid;
407 }
412 return {m_pDense + offset};
413 }
414
419 m_pDense += diff;
420 return *this;
421 }
426 m_pDense -= diff;
427 return *this;
428 }
432 ++m_pDense;
433 return *this;
434 }
438 iterator temp(*this);
439 ++*this;
440 return temp;
441 }
445 --m_pDense;
446 return *this;
447 }
451 iterator temp(*this);
452 --*this;
453 return temp;
454 }
455
460 return {m_pDense + offset};
461 }
466 return {m_pDense - offset};
467 }
471 difference_type operator-(const iterator& other) const {
472 return (difference_type)(m_pDense - other.m_pDense);
473 }
474
478 GAIA_NODISCARD bool operator==(const iterator& other) const {
479 return m_pDense == other.m_pDense;
480 }
484 GAIA_NODISCARD bool operator!=(const iterator& other) const {
485 return m_pDense != other.m_pDense;
486 }
490 GAIA_NODISCARD bool operator>(const iterator& other) const {
491 return m_pDense > other.m_pDense;
492 }
496 GAIA_NODISCARD bool operator>=(const iterator& other) const {
497 return m_pDense >= other.m_pDense;
498 }
502 GAIA_NODISCARD bool operator<(const iterator& other) const {
503 return m_pDense < other.m_pDense;
504 }
508 GAIA_NODISCARD bool operator<=(const iterator& other) const {
509 return m_pDense <= other.m_pDense;
510 }
511 };
512
513 template <typename T, uint32_t PageCapacity, typename Allocator>
518 struct const_sparse_iterator<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>> {
520 using value_type = sparse_id;
521 // using pointer = sparse_id*; not supported
522 // using reference = sparse_id&; not supported
523 using difference_type = detail::difference_type;
524 using size_type = detail::size_type;
526
527 private:
528 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_iterator must be a power of 2");
529 constexpr static sparse_id page_mask = PageCapacity - 1;
530 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
531
532 using page_type = detail::sparse_page<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>>;
533
534 const value_type* m_pDense;
535
536 public:
540
544 const auto sid = *m_pDense;
545 return sid;
546 }
550 const auto sid = *m_pDense;
551 return sid;
552 }
557 return {m_pDense + offset};
558 }
559
564 m_pDense += diff;
565 return *this;
566 }
571 m_pDense -= diff;
572 return *this;
573 }
577 ++m_pDense;
578 return *this;
579 }
583 iterator temp(*this);
584 ++*this;
585 return temp;
586 }
590 --m_pDense;
591 return *this;
592 }
596 iterator temp(*this);
597 --*this;
598 return temp;
599 }
600
605 return {m_pDense + offset};
606 }
611 return {m_pDense - offset};
612 }
616 difference_type operator-(const iterator& other) const {
617 return (difference_type)(m_pDense - other.m_pDense);
618 }
619
623 GAIA_NODISCARD bool operator==(const iterator& other) const {
624 return m_pDense == other.m_pDense;
625 }
629 GAIA_NODISCARD bool operator!=(const iterator& other) const {
630 return m_pDense != other.m_pDense;
631 }
635 GAIA_NODISCARD bool operator>(const iterator& other) const {
636 return m_pDense > other.m_pDense;
637 }
641 GAIA_NODISCARD bool operator>=(const iterator& other) const {
642 return m_pDense >= other.m_pDense;
643 }
647 GAIA_NODISCARD bool operator<(const iterator& other) const {
648 return m_pDense < other.m_pDense;
649 }
653 GAIA_NODISCARD bool operator<=(const iterator& other) const {
654 return m_pDense <= other.m_pDense;
655 }
656 };
657
658 namespace detail {
660 template <typename T, uint32_t PageCapacity, typename Allocator, typename = void>
661 class sparse_page {
662 public:
663 using value_type = T;
664 using reference = T&;
665 using const_reference = const T&;
666 using pointer = T*;
667 using const_pointer = const T*;
668 using view_policy = mem::data_view_policy_aos<T>;
669 using difference_type = detail::difference_type;
670 using size_type = detail::size_type;
671
672 using iterator = sparse_iterator<T, PageCapacity, Allocator>;
673 using const_iterator = const_sparse_iterator<T, PageCapacity, Allocator>;
674
675 private:
676 size_type* m_pSparse = nullptr;
677 uint8_t* m_pData = nullptr;
678 size_type m_cnt = 0;
679
680 void ensure() {
681 if (m_pSparse != nullptr)
682 return;
683
684 // Allocate memory for sparse->dense index mapping.
685 // Make sure initial values are detail::InvalidDenseId.
686 m_pSparse = mem::AllocHelper::alloc<size_type>("SparsePage", PageCapacity);
687 GAIA_FOR(PageCapacity) m_pSparse[i] = detail::InvalidDenseId;
688
689 // Allocate memory for data
690 m_pData = view_policy::template alloc<Allocator>(PageCapacity);
691 }
692
693 void dtr_data_inter(uint32_t idx) noexcept {
694 GAIA_ASSERT(!empty());
695
696 auto* ptr = &data()[idx];
697 core::call_dtor(ptr);
698 }
699
700 void dtr_active_data() noexcept {
701 GAIA_ASSERT(m_pSparse != nullptr);
702
703 for (uint32_t i = 0; m_cnt != 0 && i != PageCapacity; ++i) {
704 if (m_pSparse[i] == detail::InvalidDenseId)
705 continue;
706
707 auto* ptr = &data()[i];
708 core::call_dtor(ptr);
709 }
710 }
711
712 void invalidate() {
713 if (m_pSparse == nullptr)
714 return;
715
716 // Destruct active items
717 if (m_cnt != 0)
718 dtr_active_data();
719
720 // Release allocated memory
721 mem::AllocHelper::free("SparsePage", m_pSparse);
722 view_policy::template free<Allocator>(m_pData, m_cnt);
723
724 m_pSparse = nullptr;
725 m_pData = nullptr;
726 m_cnt = 0;
727 }
728
729 public:
730 sparse_page() = default;
731
732 sparse_page(const sparse_page& other) {
733 // Copy new items over
734 if (other.m_pSparse == nullptr) {
735 invalidate();
736 } else {
737 ensure();
738
739 for (uint32_t i = 0; i < PageCapacity; ++i) {
740 // Copy indices
741 m_pSparse[i] = other.m_pSparse[i];
742 if (other.m_pSparse[i] == detail::InvalidDenseId)
743 continue;
744
745 // Copy construct data
746 add_data(i, other.set_data(i));
747 }
748
749 m_cnt = other.m_cnt;
750 }
751 }
752
753 sparse_page& operator=(const sparse_page& other) {
754 GAIA_ASSERT(core::addressof(other) != this);
755
756 if (other.m_pSparse == nullptr) {
757 // If the other array is empty, let's just invalidate this one
758 invalidate();
759 } else {
760 ensure();
761
762 // Remove current active items
763 if (m_pSparse != nullptr)
764 dtr_active_data();
765
766 // Copy new items over if there are any
767 for (uint32_t i = 0; i < PageCapacity; ++i) {
768 // Copy indices
769 m_pSparse[i] = other.m_pSparse[i];
770 if (other.m_pSparse[i] == detail::InvalidDenseId)
771 continue;
772
773 // Copy construct data
774 add_data(i, other.get_data(i));
775 }
776
777 m_cnt = other.m_cnt;
778 }
779
780 return *this;
781 }
782
783 sparse_page(sparse_page&& other) noexcept {
784 m_pSparse = other.m_pSparse;
785 m_pData = other.m_pData;
786 m_cnt = other.m_cnt;
787
788 other.m_pSparse = nullptr;
789 other.m_pData = nullptr;
790 other.m_cnt = size_type(0);
791 }
792
793 sparse_page& operator=(sparse_page&& other) noexcept {
794 GAIA_ASSERT(core::addressof(other) != this);
795
796 invalidate();
797
798 m_pSparse = other.m_pSparse;
799 m_pData = other.m_pData;
800 m_cnt = other.m_cnt;
801
802 other.m_pSparse = nullptr;
803 other.m_pData = nullptr;
804 other.m_cnt = size_type(0);
805
806 return *this;
807 }
808
809 ~sparse_page() {
810 invalidate();
811 }
812
813 GAIA_CLANG_WARNING_PUSH()
814 // Memory is aligned so we can silence this warning
815 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
816
817 GAIA_NODISCARD pointer data() noexcept {
818 return reinterpret_cast<pointer>(m_pData);
819 }
820
821 GAIA_NODISCARD const_pointer data() const noexcept {
822 return reinterpret_cast<const_pointer>(m_pData);
823 }
824
825 GAIA_NODISCARD auto& set_id(size_type pos) noexcept {
826 return m_pSparse[pos];
827 }
828
829 GAIA_NODISCARD auto get_id(size_type pos) const noexcept {
830 return m_pSparse[pos];
831 }
832
833 GAIA_NODISCARD decltype(auto) set_data(size_type pos) noexcept {
834 return view_policy::set({(typename view_policy::TargetCastType)m_pData, PageCapacity}, pos);
835 }
836
837 GAIA_NODISCARD decltype(auto) get_data(size_type pos) const noexcept {
838 return view_policy::get({(typename view_policy::TargetCastType)m_pData, PageCapacity}, pos);
839 }
840
841 GAIA_CLANG_WARNING_POP()
842
843 GAIA_NODISCARD bool allocated() const noexcept {
844 return m_pSparse != nullptr;
845 }
846
847 void add() {
848 ensure();
849 ++m_cnt;
850 }
851
852 decltype(auto) add_data(uint32_t idx, const T& arg) {
853 auto* ptr = &set_data(idx);
854 core::call_ctor(ptr, arg);
855 return (reference)(*ptr);
856 }
857
858 decltype(auto) add_data(uint32_t idx, T&& arg) {
859 auto* ptr = &set_data(idx);
860 core::call_ctor(ptr, GAIA_MOV(arg));
861 return (reference)(*ptr);
862 }
863
864 void del_data(uint32_t idx) noexcept {
865 dtr_data_inter(idx);
866
867 GAIA_ASSERT(m_cnt > 0);
868 --m_cnt;
869
870 // If there is no more data, release the memory allocated by the page
871 if (m_cnt == 0)
872 invalidate();
873 }
874
875 GAIA_NODISCARD size_type size() const noexcept {
876 return m_cnt;
877 }
878
879 GAIA_NODISCARD bool empty() const noexcept {
880 return size() == 0;
881 }
882
883 GAIA_NODISCARD decltype(auto) front() noexcept {
884 GAIA_ASSERT(!empty());
885 return (reference)*begin();
886 }
887
888 GAIA_NODISCARD decltype(auto) front() const noexcept {
889 GAIA_ASSERT(!empty());
890 return (const_reference)*begin();
891 }
892
893 GAIA_NODISCARD decltype(auto) back() noexcept {
894 GAIA_ASSERT(!empty());
895 return (reference)(set_data(m_cnt - 1));
896 }
897
898 GAIA_NODISCARD decltype(auto) back() const noexcept {
899 GAIA_ASSERT(!empty());
900 return (const_reference)set_data(m_cnt - 1);
901 }
902
903 GAIA_NODISCARD auto begin() noexcept {
904 return iterator(data());
905 }
906
907 GAIA_NODISCARD auto begin() const noexcept {
908 return const_iterator(data());
909 }
910
911 GAIA_NODISCARD auto cbegin() const noexcept {
912 return const_iterator(data());
913 }
914
915 GAIA_NODISCARD auto rbegin() noexcept {
916 return iterator((pointer)&back());
917 }
918
919 GAIA_NODISCARD auto rbegin() const noexcept {
920 return const_iterator((pointer)&back());
921 }
922
923 GAIA_NODISCARD auto crbegin() const noexcept {
924 return const_iterator((pointer)&back());
925 }
926
927 GAIA_NODISCARD auto end() noexcept {
928 return iterator(data() + size());
929 }
930
931 GAIA_NODISCARD auto end() const noexcept {
932 return const_iterator(data() + size());
933 }
934
935 GAIA_NODISCARD auto cend() const noexcept {
936 return const_iterator(data() + size());
937 }
938
939 GAIA_NODISCARD auto rend() noexcept {
940 return iterator(data() - 1);
941 }
942
943 GAIA_NODISCARD auto rend() const noexcept {
944 return const_iterator(data() - 1);
945 }
946
947 GAIA_NODISCARD auto crend() const noexcept {
948 return const_iterator(data() - 1);
949 }
950
951 GAIA_NODISCARD bool operator==(const sparse_page& other) const {
952 if (m_cnt != other.m_cnt)
953 return false;
954 const size_type n = size();
955 for (size_type i = 0; i < n; ++i)
956 if (!(get_data(i) == other[i]))
957 return false;
958 return true;
959 }
960
961 GAIA_NODISCARD constexpr bool operator!=(const sparse_page& other) const {
962 return !operator==(other);
963 }
964 };
965
967 template <typename T, uint32_t PageCapacity, typename Allocator>
968 class sparse_page<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>> {
969 public:
970 using value_type = T;
971 // using reference = T&; not supported
972 // using const_reference = const T&; not supported
973 using pointer = T*;
974 using const_pointer = const T*;
975 using view_policy = mem::data_view_policy_aos<T>;
976 using difference_type = detail::difference_type;
977 using size_type = detail::size_type;
978
979 using iterator = sparse_iterator<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>>;
980 using const_iterator = const_sparse_iterator<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>>;
981
982 private:
983 size_type* m_pSparse = nullptr;
984 size_type m_cnt = 0;
985
986 void ensure() {
987 if (m_pSparse == nullptr) {
988 // Allocate memory for sparse->dense index mapping.
989 // Make sure initial values are detail::InvalidId.
990 m_pSparse = mem::AllocHelper::alloc<size_type>("SparsePage", PageCapacity);
991 GAIA_FOR(PageCapacity) m_pSparse[i] = detail::InvalidDenseId;
992 }
993 }
994
995 void dtr_data_inter([[maybe_unused]] uint32_t idx) noexcept {
996 GAIA_ASSERT(!empty());
997 }
998
999 void dtr_active_data() noexcept {
1000 GAIA_ASSERT(m_pSparse != nullptr);
1001 }
1002
1003 void invalidate() {
1004 if (m_pSparse == nullptr)
1005 return;
1006
1007 // Release allocated memory
1008 mem::AllocHelper::free("SparsePage", m_pSparse);
1009
1010 m_pSparse = nullptr;
1011 m_cnt = 0;
1012 }
1013
1014 public:
1015 sparse_page() = default;
1016
1017 sparse_page(const sparse_page& other) {
1018 // Copy new items over
1019 if (other.m_pSparse == nullptr) {
1020 invalidate();
1021 } else {
1022 for (uint32_t i = 0; i < PageCapacity; ++i) {
1023 // Copy indices
1024 m_pSparse[i] = other.m_pSparse[i];
1025 if (m_pSparse[i] == detail::InvalidDenseId)
1026 continue;
1027 }
1028
1029 m_cnt = other.m_cnt;
1030 }
1031 }
1032
1033 sparse_page& operator=(const sparse_page& other) {
1034 GAIA_ASSERT(core::addressof(other) != this);
1035
1036 if (m_pSparse == nullptr && other.m_pSparse != nullptr)
1037 ensure();
1038
1039 // Copy new items over if there are any
1040 if (other.m_pSparse == nullptr) {
1041 invalidate();
1042 } else {
1043 // Remove current active items
1044 if (m_pSparse != nullptr)
1045 dtr_active_data();
1046
1047 // Copy indices
1048 for (uint32_t i = 0; i < PageCapacity; ++i)
1049 m_pSparse[i] = other.m_pSparse[i];
1050
1051 m_cnt = other.m_cnt;
1052 }
1053
1054 return *this;
1055 }
1056
1057 sparse_page(sparse_page&& other) noexcept {
1058 // This is a newly constructed object.
1059 // It can't have any memory allocated, yet.
1060 GAIA_ASSERT(m_pSparse == nullptr);
1061
1062 m_pSparse = other.m_pSparse;
1063 m_cnt = other.m_cnt;
1064
1065 other.m_pSparse = nullptr;
1066 other.m_cnt = size_type(0);
1067 }
1068
1069 sparse_page& operator=(sparse_page&& other) noexcept {
1070 GAIA_ASSERT(core::addressof(other) != this);
1071
1072 invalidate();
1073
1074 m_pSparse = other.m_pSparse;
1075 m_cnt = other.m_cnt;
1076
1077 other.m_pSparse = nullptr;
1078 other.m_cnt = size_type(0);
1079
1080 return *this;
1081 }
1082
1083 ~sparse_page() {
1084 invalidate();
1085 }
1086
1087 GAIA_CLANG_WARNING_PUSH()
1088 // Memory is aligned so we can silence this warning
1089 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
1090
1091 GAIA_NODISCARD pointer data() noexcept {
1092 return reinterpret_cast<pointer>(m_pSparse);
1093 }
1094
1095 GAIA_NODISCARD const_pointer data() const noexcept {
1096 return reinterpret_cast<const_pointer>(m_pSparse);
1097 }
1098
1099 GAIA_CLANG_WARNING_POP()
1100
1101 GAIA_NODISCARD bool allocated() const noexcept {
1102 return m_pSparse != nullptr;
1103 }
1104
1105 void add() {
1106 ensure();
1107 ++m_cnt;
1108 }
1109
1110 GAIA_NODISCARD auto& set_id(size_type pos) noexcept {
1111 return m_pSparse[pos];
1112 }
1113
1114 GAIA_NODISCARD auto get_id(size_type pos) const noexcept {
1115 return m_pSparse[pos];
1116 }
1117
1118 void del_id(uint32_t idx) noexcept {
1119 dtr_data_inter(idx);
1120
1121 GAIA_ASSERT(m_cnt > 0);
1122 --m_cnt;
1123
1124 // If there is no more data, release the memory allocated by the page
1125 if (m_cnt == 0)
1126 invalidate();
1127 }
1128
1129 GAIA_NODISCARD size_type size() const noexcept {
1130 return m_cnt;
1131 }
1132
1133 GAIA_NODISCARD bool empty() const noexcept {
1134 return size() == 0;
1135 }
1136
1137 GAIA_NODISCARD auto front() const noexcept {
1138 GAIA_ASSERT(!empty());
1139 return *begin();
1140 }
1141
1142 GAIA_NODISCARD auto back() const noexcept {
1143 GAIA_ASSERT(!empty());
1144 return get_id(m_cnt - 1);
1145 }
1146
1147 GAIA_NODISCARD auto begin() noexcept {
1148 return iterator(data());
1149 }
1150
1151 GAIA_NODISCARD auto begin() const noexcept {
1152 return const_iterator(data());
1153 }
1154
1155 GAIA_NODISCARD auto cbegin() const noexcept {
1156 return const_iterator(data());
1157 }
1158
1159 GAIA_NODISCARD auto rbegin() noexcept {
1160 return iterator((pointer)&back());
1161 }
1162
1163 GAIA_NODISCARD auto rbegin() const noexcept {
1164 return const_iterator((pointer)&back());
1165 }
1166
1167 GAIA_NODISCARD auto crbegin() const noexcept {
1168 return const_iterator((pointer)&back());
1169 }
1170
1171 GAIA_NODISCARD auto end() noexcept {
1172 return iterator(data() + size());
1173 }
1174
1175 GAIA_NODISCARD auto end() const noexcept {
1176 return const_iterator(data() + size());
1177 }
1178
1179 GAIA_NODISCARD auto cend() const noexcept {
1180 return const_iterator(data() + size());
1181 }
1182
1183 GAIA_NODISCARD auto rend() noexcept {
1184 return iterator(data() - 1);
1185 }
1186
1187 GAIA_NODISCARD auto rend() const noexcept {
1188 return const_iterator(data() - 1);
1189 }
1190
1191 GAIA_NODISCARD auto crend() const noexcept {
1192 return const_iterator(data() - 1);
1193 }
1194
1195 GAIA_NODISCARD bool operator==(const sparse_page& other) const {
1196 if (m_cnt != other.m_cnt)
1197 return false;
1198 const size_type n = size();
1199 for (size_type i = 0; i < n; ++i)
1200 if (!(get_id(i) == other.get_id(i)))
1201 return false;
1202 return true;
1203 }
1204
1205 GAIA_NODISCARD constexpr bool operator!=(const sparse_page& other) const {
1206 return !operator==(other);
1207 }
1208 };
1210 } // namespace detail
1211
1218 template <
1219 typename T, uint32_t PageCapacity = 4096, typename Allocator = mem::DefaultAllocatorAdaptor, typename = void>
1221 public:
1222 using value_type = T;
1223 using reference = T&;
1224 using const_reference = const T&;
1225 using pointer = T*;
1226 using const_pointer = const T*;
1228 using difference_type = detail::difference_type;
1229 using size_type = detail::size_type;
1230
1233 using page_type = detail::sparse_page<T, PageCapacity, Allocator>;
1234
1235 private:
1236 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_storage must be a power of 2");
1237 constexpr static sparse_id page_mask = PageCapacity - 1;
1238 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
1239
1241 cnt::darray<sparse_id> m_dense;
1243 cnt::darray<page_type> m_pages;
1245 size_type m_cnt = size_type(0);
1246
1247 void try_grow(uint32_t pid) {
1248 const auto required = m_cnt + 1;
1249 if (required > m_dense.capacity()) {
1250 auto cap = m_dense.capacity() == 0 ? size_type(16) : m_dense.capacity();
1251 while (cap < required)
1252 cap *= 2;
1253 m_dense.reserve(cap);
1254 }
1255 m_dense.resize(required);
1256
1257 // The sparse array has to be able to take any sparse index
1258 if (pid >= m_pages.size())
1259 m_pages.resize(pid + 1);
1260
1261 m_pages[pid].add();
1262 }
1263
1264 public:
1265 constexpr sparse_storage() noexcept = default;
1266
1270 GAIA_ASSERT(core::addressof(other) != this);
1271
1272 m_dense = other.m_dense;
1273 m_pages = other.m_pages;
1274 m_cnt = other.m_cnt;
1275 }
1276
1281 GAIA_ASSERT(core::addressof(other) != this);
1282
1283 m_dense = other.m_dense;
1284 m_pages = other.m_pages;
1285 m_cnt = other.m_cnt;
1286
1287 return *this;
1288 }
1289
1292 sparse_storage(sparse_storage&& other) noexcept {
1293 // This is a newly constructed object.
1294 // It can't have any memory allocated, yet.
1295 GAIA_ASSERT(m_dense.data() == nullptr);
1296
1297 m_dense = GAIA_MOV(other.m_dense);
1298 m_pages = GAIA_MOV(other.m_pages);
1299 m_cnt = other.m_cnt;
1300
1301 other.m_dense = {};
1302 other.m_pages = {};
1303 other.m_cnt = size_type(0);
1304 }
1305
1310 GAIA_ASSERT(core::addressof(other) != this);
1311
1312 m_dense = GAIA_MOV(other.m_dense);
1313 m_pages = GAIA_MOV(other.m_pages);
1314 m_cnt = other.m_cnt;
1315
1316 other.m_dense = {};
1317 other.m_pages = {};
1318 other.m_cnt = size_type(0);
1319
1320 return *this;
1321 }
1322
1323 ~sparse_storage() = default;
1324
1325 GAIA_CLANG_WARNING_PUSH()
1326 // Memory is aligned so we can silence this warning
1327 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
1328
1332 GAIA_NODISCARD decltype(auto) operator[](sparse_id sid) noexcept {
1333 GAIA_ASSERT(has(sid));
1334 const auto pid = uint32_t(sid >> to_page_index);
1335 const auto did = uint32_t(sid & page_mask);
1336
1337 auto& page = m_pages[pid];
1338 return view_policy::set({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
1339 }
1340
1344 GAIA_NODISCARD decltype(auto) operator[](sparse_id sid) const noexcept {
1345 GAIA_ASSERT(has(sid));
1346 const auto pid = uint32_t(sid >> to_page_index);
1347 const auto did = uint32_t(sid & page_mask);
1348
1349 auto& page = m_pages[pid];
1350 return view_policy::get({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
1351 }
1352
1353 GAIA_CLANG_WARNING_POP()
1354
1355
1358 GAIA_NODISCARD bool has(sparse_id sid) const {
1359 if (sid == detail::InvalidSparseId)
1360 return false;
1361
1362 const auto pid = uint32_t(sid >> to_page_index);
1363 if (pid >= m_pages.size())
1364 return false;
1365
1366 const auto did = uint32_t(sid & page_mask);
1367 const auto& page = m_pages[pid];
1368 // Empty pages release their internal buffers but remain in m_pages until the
1369 // outer page array is compacted. Treat such slots as missing.
1370 if (!page.allocated())
1371 return false;
1372
1373 const auto id = page.get_id(did);
1374 return id != detail::InvalidDenseId;
1375 }
1376
1380 GAIA_NODISCARD bool has(const T& arg) const {
1381 const auto sid = to_sparse_id<T>::get(arg);
1382 GAIA_ASSERT(sid != detail::InvalidSparseId);
1383 return has(sid);
1384 }
1385
1389 template <typename TType>
1391 decltype(auto) add(TType&& arg) {
1392 const auto sid = to_sparse_id<T>::get(arg);
1393 if (has(sid)) {
1394 const auto pid = uint32_t(sid >> to_page_index);
1395 const auto did = uint32_t(sid & page_mask);
1396 auto& page = m_pages[pid];
1397 return page.set_data(did);
1398 }
1399
1400 const auto pid = uint32_t(sid >> to_page_index);
1401 const auto did = uint32_t(sid & page_mask);
1402
1403 try_grow(pid);
1404 m_dense[m_cnt] = sid;
1405
1406 auto& page = m_pages[pid];
1407 page.set_id(did) = m_cnt++;
1408 return page.add_data(did, GAIA_FWD(arg));
1409 }
1410
1414 decltype(auto) set(sparse_id sid) {
1415 GAIA_ASSERT(has(sid));
1416
1417 const auto pid = uint32_t(sid >> to_page_index);
1418 const auto did = uint32_t(sid & page_mask);
1419
1420 auto& page = m_pages[pid];
1421 return page.set_data(did);
1422 }
1423
1426 void del(sparse_id sid) noexcept {
1427 GAIA_ASSERT(!empty());
1428 GAIA_ASSERT(sid != detail::InvalidSparseId);
1429
1430 if (!has(sid))
1431 return;
1432
1433 const auto pid = uint32_t(sid >> to_page_index);
1434 const auto did = uint32_t(sid & page_mask);
1435
1436 const auto sidPrev = std::as_const(m_dense)[m_cnt - 1];
1437 const auto pidPrev = uint32_t(sidPrev >> to_page_index);
1438 const auto didPrev = uint32_t(sidPrev & page_mask);
1439
1440 auto& page = m_pages[pid];
1441 const auto id = page.get_id(did);
1442 // The swapped-in dense item may live on a different sparse page.
1443 auto& pagePrev = m_pages[pidPrev];
1444 pagePrev.set_id(didPrev) = id;
1445 page.set_id(did) = detail::InvalidDenseId;
1446 page.del_data(did);
1447 m_dense[id] = sidPrev;
1448 m_dense.resize(m_cnt - 1);
1449
1450 GAIA_ASSERT(m_cnt > 0);
1451 --m_cnt;
1452 }
1453
1456 void del(const T& arg) noexcept {
1457 const auto sid = to_sparse_id<T>::get(arg);
1458 return del(sid);
1459 }
1460
1462 void clear() {
1463 m_dense.resize(0);
1464 m_pages.resize(0);
1465 m_cnt = 0;
1466 }
1467
1470 GAIA_NODISCARD size_type size() const noexcept {
1471 return m_cnt;
1472 }
1473
1476 GAIA_NODISCARD bool empty() const noexcept {
1477 return size() == 0;
1478 }
1479
1482 GAIA_NODISCARD decltype(auto) front() noexcept {
1483 GAIA_ASSERT(!empty());
1484 return (reference)*begin();
1485 }
1486
1489 GAIA_NODISCARD decltype(auto) front() const noexcept {
1490 GAIA_ASSERT(!empty());
1491 return (const_reference)*begin();
1492 }
1493
1496 GAIA_NODISCARD decltype(auto) back() noexcept {
1497 GAIA_ASSERT(!empty());
1498
1499 const auto sid = m_dense[m_cnt - 1];
1500 const auto pid = uint32_t(sid >> to_page_index);
1501 const auto did = uint32_t(sid & page_mask);
1502
1503 return (reference)m_pages[pid].set_data(did);
1504 }
1505
1508 GAIA_NODISCARD decltype(auto) back() const noexcept {
1509 GAIA_ASSERT(!empty());
1510
1511 const auto sid = m_dense[m_cnt - 1];
1512 const auto pid = uint32_t(sid >> to_page_index);
1513 const auto did = uint32_t(sid & page_mask);
1514
1515 return (const_reference)m_pages[pid].get_data(did);
1516 }
1517
1520 GAIA_NODISCARD auto begin() noexcept {
1521 return iterator(detail::sparse_dense_data(m_dense), m_pages.data());
1522 }
1523
1526 GAIA_NODISCARD auto begin() const noexcept {
1527 return const_iterator(detail::sparse_dense_data(m_dense), m_pages.data());
1528 }
1529
1532 GAIA_NODISCARD auto cbegin() const noexcept {
1533 return const_iterator(detail::sparse_dense_data(m_dense), m_pages.data());
1534 }
1535
1538 GAIA_NODISCARD auto end() noexcept {
1539 return iterator(detail::sparse_dense_data(m_dense) + size(), m_pages.data());
1540 }
1541
1544 GAIA_NODISCARD auto end() const noexcept {
1545 return const_iterator(detail::sparse_dense_data(m_dense) + size(), m_pages.data());
1546 }
1547
1550 GAIA_NODISCARD auto cend() const noexcept {
1551 return const_iterator(detail::sparse_dense_data(m_dense) + size(), m_pages.data());
1552 }
1553
1557 GAIA_NODISCARD bool operator==(const sparse_storage& other) const {
1558 // The number of items needs to be the same
1559 if (m_cnt != other.m_cnt)
1560 return false;
1561
1562 // Dense indices need to be the same.
1563 // We don't check m_sparse, because it m_dense doesn't
1564 // match, m_sparse will be different as well.
1565 if (m_dense != other.m_dense)
1566 return false;
1567
1568 // Check data one-by-one.
1569 // We don't compare the entire array, only the actually stored values,
1570 // because their is possible a lot of empty space in the data array (it is sparse).
1571 const size_type n = size();
1572 for (size_type i = 0, cnt = 0; i < n && cnt < m_cnt; ++i, ++cnt) {
1573 const auto sid = m_dense[i];
1574 const auto pid = uint32_t(sid >> to_page_index);
1575 const auto did = uint32_t(sid & page_mask);
1576
1577 const auto& item0 = m_pages[pid].get_data(did);
1578 const auto& item1 = m_pages[pid].get_data(did);
1579
1580 if (!(item0 == item1))
1581 return false;
1582 }
1583 return true;
1584 }
1585
1589 GAIA_NODISCARD constexpr bool operator!=(const sparse_storage& other) const {
1590 return !operator==(other);
1591 }
1592 };
1593
1601 template <typename T, uint32_t PageCapacity, typename Allocator>
1602 class sparse_storage<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>> {
1603 public:
1604 using value_type = T;
1605 using reference = T&;
1606 using const_reference = const T&;
1607 using pointer = T*;
1608 using const_pointer = const T*;
1610 using difference_type = detail::difference_type;
1611 using size_type = detail::size_type;
1612
1613 using iterator =
1619 detail::sparse_page<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>>;
1621
1622 private:
1623 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_storage must be a power of 2");
1624 constexpr static sparse_id page_mask = PageCapacity - 1;
1625 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
1626
1628 cnt::darray<sparse_id> m_dense;
1630 cnt::darray<page_type> m_pages;
1632 size_type m_cnt = size_type(0);
1633
1634 void try_grow(uint32_t pid) {
1635 const auto required = m_cnt + 1;
1636 if (required > m_dense.capacity()) {
1637 auto cap = m_dense.capacity() == 0 ? size_type(16) : m_dense.capacity();
1638 while (cap < required)
1639 cap *= 2;
1640 m_dense.reserve(cap);
1641 }
1642 m_dense.resize(required);
1643
1644 // The sparse array has to be able to take any sparse index
1645 if (pid >= m_pages.size())
1646 m_pages.resize(pid + 1);
1647
1648 m_pages[pid].add();
1649 }
1650
1651 public:
1652 constexpr sparse_storage() noexcept = default;
1653
1657 GAIA_ASSERT(core::addressof(other) != this);
1658
1659 m_dense = other.m_dense;
1660 m_pages = other.m_pages;
1661 m_cnt = other.m_cnt;
1662 }
1663
1668 GAIA_ASSERT(core::addressof(other) != this);
1669
1670 m_dense = other.m_dense;
1671 m_pages = other.m_pages;
1672 m_cnt = other.m_cnt;
1673
1674 return *this;
1675 }
1676
1679 sparse_storage(sparse_storage&& other) noexcept {
1680 // This is a newly constructed object.
1681 // It can't have any memory allocated, yet.
1682 GAIA_ASSERT(m_dense.data() == nullptr);
1683
1684 m_dense = GAIA_MOV(other.m_dense);
1685 m_pages = GAIA_MOV(other.m_pages);
1686 m_cnt = other.m_cnt;
1687
1688 other.m_dense = {};
1689 other.m_pages = {};
1690 other.m_cnt = size_type(0);
1691 }
1692
1697 GAIA_ASSERT(core::addressof(other) != this);
1698
1699 m_dense = GAIA_MOV(other.m_dense);
1700 m_pages = GAIA_MOV(other.m_pages);
1701 m_cnt = other.m_cnt;
1702
1703 other.m_dense = {};
1704 other.m_pages = {};
1705 other.m_cnt = size_type(0);
1706
1707 return *this;
1708 }
1709
1710 ~sparse_storage() = default;
1711
1715 GAIA_NODISCARD bool has(sparse_id sid) const {
1716 GAIA_ASSERT(sid != detail::InvalidSparseId);
1717
1718 const auto pid = uint32_t(sid >> to_page_index);
1719 const auto did = uint32_t(sid & page_mask);
1720 return has_internal(pid, did);
1721 }
1722
1723 private:
1724 GAIA_NODISCARD bool has_internal(uint32_t pid, uint32_t did) const {
1725 if (pid >= m_pages.size())
1726 return false;
1727
1728 const auto& page = m_pages[pid];
1729 if (!page.allocated())
1730 return false;
1731
1732 const auto id = page.get_id(did);
1733 return id != detail::InvalidDenseId;
1734 }
1735
1736 public:
1739 void add(sparse_id sid) {
1740 GAIA_ASSERT(sid != detail::InvalidSparseId);
1741
1742 const auto pid = uint32_t(sid >> to_page_index);
1743 const auto did = uint32_t(sid & page_mask);
1744
1745 if (has_internal(pid, did))
1746 return;
1747
1748 try_grow(pid);
1749 m_dense[m_cnt] = sid;
1750
1751 auto& page = m_pages[pid];
1752 page.set_id(did) = m_cnt++;
1753 }
1754
1757 void del(sparse_id sid) noexcept {
1758 GAIA_ASSERT(!empty());
1759 GAIA_ASSERT(sid != detail::InvalidSparseId);
1760
1761 const auto pid = uint32_t(sid >> to_page_index);
1762 const auto did = uint32_t(sid & page_mask);
1763
1764 if (!has_internal(pid, did))
1765 return;
1766
1767 const auto sidPrev = std::as_const(m_dense)[m_cnt - 1];
1768 const auto pidPrev = uint32_t(sidPrev >> to_page_index);
1769 const auto didPrev = uint32_t(sidPrev & page_mask);
1770
1771 auto& page = m_pages[pid];
1772 const auto id = page.get_id(did);
1773 // The swapped-in dense item may live on a different sparse page.
1774 auto& pagePrev = m_pages[pidPrev];
1775 pagePrev.set_id(didPrev) = id;
1776 page.set_id(did) = detail::InvalidDenseId;
1777 m_dense[id] = sidPrev;
1778 m_dense.resize(m_cnt - 1);
1779
1780 GAIA_ASSERT(m_cnt > 0);
1781 --m_cnt;
1782 }
1783
1785 void clear() {
1786 m_dense.resize(0);
1787 m_pages.resize(0);
1788 m_cnt = 0;
1789 }
1790
1793 GAIA_NODISCARD size_type size() const noexcept {
1794 return m_cnt;
1795 }
1796
1799 GAIA_NODISCARD bool empty() const noexcept {
1800 return size() == 0;
1801 }
1802
1805 GAIA_NODISCARD decltype(auto) front() noexcept {
1806 GAIA_ASSERT(!empty());
1807 return (reference)*begin();
1808 }
1809
1812 GAIA_NODISCARD decltype(auto) front() const noexcept {
1813 GAIA_ASSERT(!empty());
1814 return (const_reference)*begin();
1815 }
1816
1819 GAIA_NODISCARD decltype(auto) back() noexcept {
1820 GAIA_ASSERT(!empty());
1821
1822 const auto sid = m_dense[m_cnt - 1];
1823 const auto pid = uint32_t(sid >> to_page_index);
1824 const auto did = uint32_t(sid & page_mask);
1825
1826 return (reference)m_pages[pid].set_id(did);
1827 }
1828
1831 GAIA_NODISCARD decltype(auto) back() const noexcept {
1832 GAIA_ASSERT(!empty());
1833
1834 const auto sid = m_dense[m_cnt - 1];
1835 const auto pid = uint32_t(sid >> to_page_index);
1836 const auto did = uint32_t(sid & page_mask);
1837
1838 return (const_reference)m_pages[pid].get_id(did);
1839 }
1840
1843 GAIA_NODISCARD auto begin() noexcept {
1844 return iterator(detail::sparse_dense_data(m_dense));
1845 }
1846
1849 GAIA_NODISCARD auto begin() const noexcept {
1850 return const_iterator(detail::sparse_dense_data(m_dense));
1851 }
1852
1855 GAIA_NODISCARD auto cbegin() const noexcept {
1856 return const_iterator(detail::sparse_dense_data(m_dense));
1857 }
1858
1861 GAIA_NODISCARD auto end() noexcept {
1862 return iterator(detail::sparse_dense_data(m_dense) + size());
1863 }
1864
1867 GAIA_NODISCARD auto end() const noexcept {
1868 return const_iterator(detail::sparse_dense_data(m_dense) + size());
1869 }
1870
1873 GAIA_NODISCARD auto cend() const noexcept {
1874 return const_iterator(detail::sparse_dense_data(m_dense) + size());
1875 }
1876
1880 GAIA_NODISCARD bool operator==(const sparse_storage& other) const {
1881 // The number of items needs to be the same
1882 if (m_cnt != other.m_cnt)
1883 return false;
1884
1885 // Dense indices need to be the same.
1886 // We don't check m_sparse, because it m_dense doesn't
1887 // match, m_sparse will be different as well.
1888 if (m_dense != other.m_dense)
1889 return false;
1890
1891 return true;
1892 }
1893
1897 GAIA_NODISCARD constexpr bool operator!=(const sparse_storage& other) const {
1898 return !operator==(other);
1899 }
1900 };
1901 } // namespace cnt
1902
1903} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:27
GAIA_NODISCARD size_type capacity() const noexcept
Returns the number of elements that fit without reallocation.
Definition darray_impl.h:516
void reserve(size_type cap)
Ensures storage for at least the requested number of elements.
Definition darray_impl.h:223
GAIA_NODISCARD size_type size() const noexcept
Returns the number of elements.
Definition darray_impl.h:504
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
GAIA_NODISCARD decltype(auto) back() const noexcept
Returns the last registered sparse identifier.
Definition sparse_storage.h:1831
sparse_storage(sparse_storage &&other) noexcept
Move-constructs the storage.
Definition sparse_storage.h:1679
GAIA_NODISCARD auto end() noexcept
Returns an iterator past the last sparse identifier.
Definition sparse_storage.h:1861
void del(sparse_id sid) noexcept
Removes a sparse id from storage.
Definition sparse_storage.h:1757
GAIA_NODISCARD bool has(sparse_id sid) const
Checks whether a sparse identifier is registered.
Definition sparse_storage.h:1715
detail::size_type size_type
Type used for sizes and offsets.
Definition sparse_storage.h:1611
sparse_storage & operator=(sparse_storage &&other) noexcept
Move-assigns the storage.
Definition sparse_storage.h:1696
GAIA_NODISCARD auto cbegin() const noexcept
Returns a constant iterator to the first sparse identifier.
Definition sparse_storage.h:1855
detail::difference_type difference_type
Type used for iterator distances.
Definition sparse_storage.h:1610
detail::sparse_page< T, PageCapacity, Allocator, std::enable_if_t< std::is_empty_v< T > > > page_type
Internal sparse-page type.
Definition sparse_storage.h:1620
GAIA_NODISCARD bool empty() const noexcept
Checks if the storage is empty (no items inserted).
Definition sparse_storage.h:1799
GAIA_NODISCARD auto end() const noexcept
Returns an iterator past the last sparse identifier.
Definition sparse_storage.h:1867
GAIA_NODISCARD decltype(auto) back() noexcept
Returns the last registered sparse identifier.
Definition sparse_storage.h:1819
GAIA_NODISCARD decltype(auto) front() const noexcept
Returns the first registered sparse identifier.
Definition sparse_storage.h:1812
sparse_storage & operator=(const sparse_storage &other)
Copy-assigns the storage.
Definition sparse_storage.h:1667
GAIA_NODISCARD decltype(auto) front() noexcept
Returns the first registered sparse identifier.
Definition sparse_storage.h:1805
GAIA_NODISCARD constexpr bool operator!=(const sparse_storage &other) const
Checks whether two storages differ.
Definition sparse_storage.h:1897
void add(sparse_id sid)
Registers a new sparse id.
Definition sparse_storage.h:1739
GAIA_NODISCARD auto begin() noexcept
Returns an iterator to the first sparse identifier.
Definition sparse_storage.h:1843
GAIA_NODISCARD auto cend() const noexcept
Returns a constant iterator past the last sparse identifier.
Definition sparse_storage.h:1873
GAIA_NODISCARD size_type size() const noexcept
Returns the number of identifiers registered in the storage.
Definition sparse_storage.h:1793
GAIA_NODISCARD auto begin() const noexcept
Returns an iterator to the first sparse identifier.
Definition sparse_storage.h:1849
GAIA_NODISCARD bool operator==(const sparse_storage &other) const
Checks whether two storages contain the same sparse identifiers.
Definition sparse_storage.h:1880
Array with variable size of elements of type T allocated on heap. Allocates enough memory to support ...
Definition sparse_storage.h:1220
sparse_storage & operator=(sparse_storage &&other) noexcept
Move-assigns the storage.
Definition sparse_storage.h:1309
detail::size_type size_type
Type used for sizes and offsets.
Definition sparse_storage.h:1229
GAIA_NODISCARD size_type size() const noexcept
Returns the number of items inserted into the storage.
Definition sparse_storage.h:1470
GAIA_NODISCARD auto cend() const noexcept
Returns a constant iterator past the last value.
Definition sparse_storage.h:1550
GAIA_NODISCARD decltype(auto) front() const noexcept
Returns the first stored value.
Definition sparse_storage.h:1489
GAIA_NODISCARD auto begin() noexcept
Returns an iterator to the first value.
Definition sparse_storage.h:1520
decltype(auto) set(sparse_id sid)
Update the record at the index sid.
Definition sparse_storage.h:1414
detail::difference_type difference_type
Type used for iterator distances.
Definition sparse_storage.h:1228
detail::sparse_page< T, PageCapacity, Allocator > page_type
Internal sparse-page type.
Definition sparse_storage.h:1233
sparse_storage(sparse_storage &&other) noexcept
Move-constructs the storage.
Definition sparse_storage.h:1292
GAIA_NODISCARD bool empty() const noexcept
Checks if the storage is empty (no items inserted).
Definition sparse_storage.h:1476
GAIA_NODISCARD bool has(sparse_id sid) const
Checks whether an item with a sparse identifier exists.
Definition sparse_storage.h:1358
const_sparse_iterator< T, PageCapacity, Allocator > const_iterator
Constant iterator type.
Definition sparse_storage.h:1232
GAIA_NODISCARD bool has(const T &arg) const
Checks if an item arg exists within the storage.
Definition sparse_storage.h:1380
void del(sparse_id sid) noexcept
Removes the item at the index sid from the storage.
Definition sparse_storage.h:1426
GAIA_NODISCARD auto begin() const noexcept
Returns an iterator to the first value.
Definition sparse_storage.h:1526
void clear()
Clears the storage.
Definition sparse_storage.h:1462
GAIA_NODISCARD auto end() noexcept
Returns an iterator past the last value.
Definition sparse_storage.h:1538
GAIA_NODISCARD bool operator==(const sparse_storage &other) const
Checks whether two storages contain equal values at equal sparse identifiers.
Definition sparse_storage.h:1557
GAIA_NODISCARD constexpr bool operator!=(const sparse_storage &other) const
Checks whether two storages differ.
Definition sparse_storage.h:1589
GAIA_NODISCARD decltype(auto) back() const noexcept
Returns the last stored value.
Definition sparse_storage.h:1508
GAIA_NODISCARD decltype(auto) front() noexcept
Returns the first stored value.
Definition sparse_storage.h:1482
GAIA_NODISCARD auto cbegin() const noexcept
Returns a constant iterator to the first value.
Definition sparse_storage.h:1532
GAIA_NODISCARD decltype(auto) back() noexcept
Returns the last stored value.
Definition sparse_storage.h:1496
decltype(auto) add(TType &&arg)
Inserts the item arg into the storage.
Definition sparse_storage.h:1391
sparse_iterator< T, PageCapacity, Allocator > iterator
Mutable iterator type.
Definition sparse_storage.h:1231
sparse_storage & operator=(const sparse_storage &other)
Copy-assigns the storage.
Definition sparse_storage.h:1280
void del(const T &arg) noexcept
Removes the item arg from the storage.
Definition sparse_storage.h:1456
GAIA_NODISCARD auto end() const noexcept
Returns an iterator past the last value.
Definition sparse_storage.h:1544
value_type operator*() const
Returns the sparse identifier at the current position.
Definition sparse_storage.h:543
GAIA_NODISCARD bool operator<=(const iterator &other) const
Checks whether this iterator does not follow another iterator.
Definition sparse_storage.h:653
iterator operator++(int)
Advances to the next sparse identifier.
Definition sparse_storage.h:582
const_sparse_iterator(const value_type *pDense)
Constructs a constant iterator for a dense sparse-id position.
Definition sparse_storage.h:539
GAIA_NODISCARD bool operator>(const iterator &other) const
Checks whether this iterator follows another iterator.
Definition sparse_storage.h:635
detail::difference_type difference_type
Type used for iterator distances.
Definition sparse_storage.h:523
iterator & operator+=(size_type diff)
Advances the iterator.
Definition sparse_storage.h:563
GAIA_NODISCARD bool operator!=(const iterator &other) const
Checks whether two iterators refer to different positions.
Definition sparse_storage.h:629
difference_type operator-(const iterator &other) const
Returns the distance from another iterator.
Definition sparse_storage.h:616
iterator operator--(int)
Moves to the previous sparse identifier.
Definition sparse_storage.h:595
GAIA_NODISCARD bool operator<(const iterator &other) const
Checks whether this iterator precedes another iterator.
Definition sparse_storage.h:647
iterator & operator--()
Moves to the previous sparse identifier.
Definition sparse_storage.h:589
sparse_id value_type
Sparse identifier value type.
Definition sparse_storage.h:520
iterator operator[](size_type offset) const
Returns an iterator at an offset from the current position.
Definition sparse_storage.h:556
detail::size_type size_type
Type used for iterator offsets.
Definition sparse_storage.h:524
GAIA_NODISCARD bool operator>=(const iterator &other) const
Checks whether this iterator does not precede another iterator.
Definition sparse_storage.h:641
iterator operator-(size_type offset) const
Returns an iterator moved backward by an offset.
Definition sparse_storage.h:610
iterator & operator-=(size_type diff)
Moves the iterator backward.
Definition sparse_storage.h:570
iterator operator+(size_type offset) const
Returns an iterator advanced by an offset.
Definition sparse_storage.h:604
GAIA_NODISCARD bool operator==(const iterator &other) const
Checks whether two iterators refer to the same position.
Definition sparse_storage.h:623
value_type operator->() const
Returns the sparse identifier at the current position.
Definition sparse_storage.h:549
iterator & operator++()
Advances to the next sparse identifier.
Definition sparse_storage.h:576
Constant random-access iterator over sparse-storage values.
Definition sparse_storage.h:220
iterator & operator-=(size_type diff)
Moves the iterator backward.
Definition sparse_storage.h:280
iterator operator--(int)
Moves to the previous value.
Definition sparse_storage.h:305
pointer operator->() const
Returns a pointer to the value at the current position.
Definition sparse_storage.h:256
GAIA_NODISCARD bool operator>=(const iterator &other) const
Checks whether this iterator does not precede another iterator.
Definition sparse_storage.h:351
const_sparse_iterator(const sparse_id *pDense, const page_type *pPages)
Constructs a constant iterator for a dense position and sparse-page array.
Definition sparse_storage.h:243
GAIA_NODISCARD bool operator==(const iterator &other) const
Checks whether two iterators refer to the same position.
Definition sparse_storage.h:333
iterator operator-(size_type offset) const
Returns an iterator moved backward by an offset.
Definition sparse_storage.h:320
detail::difference_type difference_type
Type used for iterator distances.
Definition sparse_storage.h:225
iterator operator++(int)
Advances to the next value.
Definition sparse_storage.h:292
iterator operator[](size_type offset) const
Returns an iterator at an offset from the current position.
Definition sparse_storage.h:266
detail::size_type size_type
Type used for iterator offsets.
Definition sparse_storage.h:226
difference_type operator-(const iterator &other) const
Returns the distance from another iterator.
Definition sparse_storage.h:326
reference operator*() const
Returns the value at the current position.
Definition sparse_storage.h:247
iterator operator+(size_type offset) const
Returns an iterator advanced by an offset.
Definition sparse_storage.h:314
iterator & operator++()
Advances to the next value.
Definition sparse_storage.h:286
GAIA_NODISCARD bool operator<(const iterator &other) const
Checks whether this iterator precedes another iterator.
Definition sparse_storage.h:357
GAIA_NODISCARD bool operator!=(const iterator &other) const
Checks whether two iterators refer to different positions.
Definition sparse_storage.h:339
iterator & operator+=(size_type diff)
Advances the iterator.
Definition sparse_storage.h:273
iterator & operator--()
Moves to the previous value.
Definition sparse_storage.h:299
GAIA_NODISCARD bool operator>(const iterator &other) const
Checks whether this iterator follows another iterator.
Definition sparse_storage.h:345
GAIA_NODISCARD bool operator<=(const iterator &other) const
Checks whether this iterator does not follow another iterator.
Definition sparse_storage.h:363
Mutable random-access iterator over sparse identifiers for empty stored types.
Definition sparse_storage.h:373
value_type operator*() const
Returns the sparse identifier at the current position.
Definition sparse_storage.h:398
iterator & operator++()
Advances to the next sparse identifier.
Definition sparse_storage.h:431
iterator operator[](size_type offset) const
Returns an iterator at an offset from the current position.
Definition sparse_storage.h:411
iterator operator--(int)
Moves to the previous sparse identifier.
Definition sparse_storage.h:450
GAIA_NODISCARD bool operator!=(const iterator &other) const
Checks whether two iterators refer to different positions.
Definition sparse_storage.h:484
GAIA_NODISCARD bool operator>(const iterator &other) const
Checks whether this iterator follows another iterator.
Definition sparse_storage.h:490
detail::size_type size_type
Type used for iterator offsets.
Definition sparse_storage.h:379
iterator operator+(size_type offset) const
Returns an iterator advanced by an offset.
Definition sparse_storage.h:459
value_type operator->() const
Returns the sparse identifier at the current position.
Definition sparse_storage.h:404
iterator & operator--()
Moves to the previous sparse identifier.
Definition sparse_storage.h:444
GAIA_NODISCARD bool operator<=(const iterator &other) const
Checks whether this iterator does not follow another iterator.
Definition sparse_storage.h:508
iterator & operator+=(size_type diff)
Advances the iterator.
Definition sparse_storage.h:418
GAIA_NODISCARD bool operator<(const iterator &other) const
Checks whether this iterator precedes another iterator.
Definition sparse_storage.h:502
GAIA_NODISCARD bool operator>=(const iterator &other) const
Checks whether this iterator does not precede another iterator.
Definition sparse_storage.h:496
iterator & operator-=(size_type diff)
Moves the iterator backward.
Definition sparse_storage.h:425
difference_type operator-(const iterator &other) const
Returns the distance from another iterator.
Definition sparse_storage.h:471
sparse_iterator(const value_type *pDense)
Constructs an iterator for a dense sparse-id position.
Definition sparse_storage.h:394
iterator operator++(int)
Advances to the next sparse identifier.
Definition sparse_storage.h:437
sparse_id value_type
Sparse identifier value type.
Definition sparse_storage.h:375
GAIA_NODISCARD bool operator==(const iterator &other) const
Checks whether two iterators refer to the same position.
Definition sparse_storage.h:478
iterator operator-(size_type offset) const
Returns an iterator moved backward by an offset.
Definition sparse_storage.h:465
detail::difference_type difference_type
Type used for iterator distances.
Definition sparse_storage.h:378
Mutable random-access iterator over sparse-storage values.
Definition sparse_storage.h:67
iterator operator-(size_type offset) const
Returns an iterator moved backward by an offset.
Definition sparse_storage.h:167
GAIA_NODISCARD bool operator<=(const iterator &other) const
Checks whether this iterator does not follow another iterator.
Definition sparse_storage.h:210
GAIA_NODISCARD bool operator>=(const iterator &other) const
Checks whether this iterator does not precede another iterator.
Definition sparse_storage.h:198
detail::difference_type difference_type
Type used for iterator distances.
Definition sparse_storage.h:72
iterator operator[](size_type offset) const
Returns an iterator at an offset from the current position.
Definition sparse_storage.h:113
reference operator*() const
Returns the value at the current position.
Definition sparse_storage.h:94
sparse_iterator(const sparse_id *pDense, page_type *pPages)
Constructs an iterator for a dense position and sparse-page array.
Definition sparse_storage.h:90
iterator & operator-=(size_type diff)
Moves the iterator backward.
Definition sparse_storage.h:127
iterator & operator++()
Advances to the next value.
Definition sparse_storage.h:133
GAIA_NODISCARD bool operator<(const iterator &other) const
Checks whether this iterator precedes another iterator.
Definition sparse_storage.h:204
iterator & operator--()
Moves to the previous value.
Definition sparse_storage.h:146
difference_type operator-(const iterator &other) const
Returns the distance from another iterator.
Definition sparse_storage.h:173
iterator operator++(int)
Advances to the next value.
Definition sparse_storage.h:139
pointer operator->() const
Returns a pointer to the value at the current position.
Definition sparse_storage.h:103
GAIA_NODISCARD bool operator>(const iterator &other) const
Checks whether this iterator follows another iterator.
Definition sparse_storage.h:192
GAIA_NODISCARD bool operator==(const iterator &other) const
Checks whether two iterators refer to the same position.
Definition sparse_storage.h:180
detail::size_type size_type
Type used for iterator offsets.
Definition sparse_storage.h:73
iterator operator--(int)
Moves to the previous value.
Definition sparse_storage.h:152
GAIA_NODISCARD bool operator!=(const iterator &other) const
Checks whether two iterators refer to different positions.
Definition sparse_storage.h:186
iterator operator+(size_type offset) const
Returns an iterator advanced by an offset.
Definition sparse_storage.h:161
iterator & operator+=(size_type diff)
Advances the iterator.
Definition sparse_storage.h:120
Converts an item to the sparse identifier used by sparse_storage.
Definition sparse_storage.h:49
static sparse_id get(const T &item) noexcept
Returns the sparse identifier for an item.
Definition sparse_storage.h:53
View policy for accessing and storing data in the AoS way. Good for random access and when accessing ...
Definition data_layout_policy.h:162
std::add_pointer_t< ValueType > TargetCastType
Pointer type used to address stored values.
Definition data_layout_policy.h:164
GAIA_NODISCARD static constexpr ValueType & set(std::span< ValueType > s, size_t idx) noexcept
Returns a mutable value reference from an AoS span.
Definition data_layout_policy.h:238
GAIA_NODISCARD static constexpr const ValueType & get(std::span< const ValueType > s, size_t idx) noexcept
Returns a read-only value reference from an AoS span.
Definition data_layout_policy.h:230