Gaia-ECS v0.9.3
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 {
17 using sparse_id = uint64_t;
18
19 namespace detail {
20 using difference_type = uint32_t;
21 using size_type = uint32_t;
22
23 constexpr static sparse_id InvalidSparseId = (sparse_id)-1;
24 constexpr static size_type InvalidDenseId = BadIndex - 1;
25
26 template <typename T, uint32_t PageCapacity, typename Allocator, typename>
27 class sparse_page;
28 } // namespace detail
29
30 template <typename T>
31 struct to_sparse_id {
32 static sparse_id get(const T& item) noexcept {
33 (void)item;
34 static_assert(
35 std::is_empty_v<T>,
36 "Sparse_storage items require a conversion function to be defined in gaia::cnt namespace");
37 return detail::InvalidSparseId;
38 }
39 };
40
41 template <typename T, uint32_t PageCapacity, typename Allocator, typename = void>
44 using value_type = T;
45 using pointer = T*;
46 using reference = T&;
47 using difference_type = detail::difference_type;
48 using size_type = detail::size_type;
50
51 private:
52 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_iterator must be a power of 2");
53 constexpr static sparse_id page_mask = PageCapacity - 1;
54 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
55
57
58 sparse_id* m_pDense;
59 page_type* m_pPages;
60
61 public:
62 sparse_iterator(sparse_id* pDense, page_type* pPages): m_pDense(pDense), m_pPages(pPages) {}
63
64 reference operator*() const {
65 const auto sid = *m_pDense;
66 const auto pid = uint32_t(sid >> to_page_index);
67 const auto did = uint32_t(sid & page_mask);
68 auto& page = m_pPages[pid];
69 return page.set_data(did);
70 }
71 pointer operator->() const {
72 const auto sid = *m_pDense;
73 const auto pid = uint32_t(sid >> to_page_index);
74 const auto did = uint32_t(sid & page_mask);
75 auto& page = m_pPages[pid];
76 return &page.set_data(did);
77 }
78 iterator operator[](size_type offset) const {
79 return {m_pDense + offset, m_pPages};
80 }
81
82 iterator& operator+=(size_type diff) {
83 m_pDense += diff;
84 return *this;
85 }
86 iterator& operator-=(size_type diff) {
87 m_pDense -= diff;
88 return *this;
89 }
90 iterator& operator++() {
91 ++m_pDense;
92 return *this;
93 }
94 iterator operator++(int) {
95 iterator temp(*this);
96 ++*this;
97 return temp;
98 }
99 iterator& operator--() {
100 --m_pDense;
101 return *this;
102 }
103 iterator operator--(int) {
104 iterator temp(*this);
105 --*this;
106 return temp;
107 }
108
109 iterator operator+(size_type offset) const {
110 return {m_pDense + offset, m_pPages};
111 }
112 iterator operator-(size_type offset) const {
113 return {m_pDense - offset, m_pPages};
114 }
115 difference_type operator-(const iterator& other) const {
116 return (difference_type)(m_pDense - other.m_pDense);
117 }
118
119 GAIA_NODISCARD bool operator==(const iterator& other) const {
120 return m_pDense == other.m_pDense;
121 }
122 GAIA_NODISCARD bool operator!=(const iterator& other) const {
123 return m_pDense != other.m_pDense;
124 }
125 GAIA_NODISCARD bool operator>(const iterator& other) const {
126 return m_pDense > other.m_pDense;
127 }
128 GAIA_NODISCARD bool operator>=(const iterator& other) const {
129 return m_pDense >= other.m_pDense;
130 }
131 GAIA_NODISCARD bool operator<(const iterator& other) const {
132 return m_pDense < other.m_pDense;
133 }
134 GAIA_NODISCARD bool operator<=(const iterator& other) const {
135 return m_pDense <= other.m_pDense;
136 }
137 };
138
139 template <typename T, uint32_t PageCapacity, typename Allocator, typename = void>
142 using value_type = T;
143 using pointer = const T*;
144 using reference = const T&;
145 using difference_type = detail::difference_type;
146 using size_type = detail::size_type;
148
149 private:
150 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_iterator must be a power of 2");
151 constexpr static sparse_id page_mask = PageCapacity - 1;
152 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
153
155
156 const sparse_id* m_pDense;
157 const page_type* m_pPages;
158
159 public:
160 const_sparse_iterator(const sparse_id* pDense, const page_type* pPages): m_pDense(pDense), m_pPages(pPages) {}
161
162 reference operator*() const {
163 const auto sid = *m_pDense;
164 const auto pid = uint32_t(sid >> to_page_index);
165 const auto did = uint32_t(sid & page_mask);
166 auto& page = m_pPages[pid];
167 return page.get_data(did);
168 }
169 pointer operator->() const {
170 const auto sid = *m_pDense;
171 const auto pid = uint32_t(sid >> to_page_index);
172 const auto did = uint32_t(sid & page_mask);
173 auto& page = m_pPages[pid];
174 return &page.get_data(did);
175 }
176 iterator operator[](size_type offset) const {
177 return {m_pDense + offset, m_pPages};
178 }
179
180 iterator& operator+=(size_type diff) {
181 m_pDense += diff;
182 return *this;
183 }
184 iterator& operator-=(size_type diff) {
185 m_pDense -= diff;
186 return *this;
187 }
188 iterator& operator++() {
189 ++m_pDense;
190 return *this;
191 }
192 iterator operator++(int) {
193 iterator temp(*this);
194 ++*this;
195 return temp;
196 }
197 iterator& operator--() {
198 --m_pDense;
199 return *this;
200 }
201 iterator operator--(int) {
202 iterator temp(*this);
203 --*this;
204 return temp;
205 }
206
207 iterator operator+(size_type offset) const {
208 return {m_pDense + offset, m_pPages};
209 }
210 iterator operator-(size_type offset) const {
211 return {m_pDense - offset, m_pPages};
212 }
213 difference_type operator-(const iterator& other) const {
214 return (difference_type)(m_pDense - other.m_pDense);
215 }
216
217 GAIA_NODISCARD bool operator==(const iterator& other) const {
218 return m_pDense == other.m_pDense;
219 }
220 GAIA_NODISCARD bool operator!=(const iterator& other) const {
221 return m_pDense != other.m_pDense;
222 }
223 GAIA_NODISCARD bool operator>(const iterator& other) const {
224 return m_pDense > other.m_pDense;
225 }
226 GAIA_NODISCARD bool operator>=(const iterator& other) const {
227 return m_pDense >= other.m_pDense;
228 }
229 GAIA_NODISCARD bool operator<(const iterator& other) const {
230 return m_pDense < other.m_pDense;
231 }
232 GAIA_NODISCARD bool operator<=(const iterator& other) const {
233 return m_pDense <= other.m_pDense;
234 }
235 };
236
237 template <typename T, uint32_t PageCapacity, typename Allocator>
238 struct sparse_iterator<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>> {
240 using value_type = sparse_id;
241 // using pointer = sparse_id*; not supported
242 // using reference = sparse_id&; not supported
243 using difference_type = detail::difference_type;
244 using size_type = detail::size_type;
246
247 private:
248 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_iterator must be a power of 2");
249 constexpr static sparse_id page_mask = PageCapacity - 1;
250 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
251
253
254 value_type* m_pDense;
255
256 public:
257 sparse_iterator(value_type* pDense): m_pDense(pDense) {}
258 sparse_iterator(const value_type* pDense): m_pDense(const_cast<value_type*>(pDense)) {}
259
260 value_type operator*() const {
261 const auto sid = *m_pDense;
262 return sid;
263 }
264 value_type operator->() const {
265 const auto sid = *m_pDense;
266 return sid;
267 }
268 iterator operator[](size_type offset) const {
269 return {m_pDense + offset};
270 }
271
272 iterator& operator+=(size_type diff) {
273 m_pDense += diff;
274 return *this;
275 }
276 iterator& operator-=(size_type diff) {
277 m_pDense -= diff;
278 return *this;
279 }
280 iterator& operator++() {
281 ++m_pDense;
282 return *this;
283 }
284 iterator operator++(int) {
285 iterator temp(*this);
286 ++*this;
287 return temp;
288 }
289 iterator& operator--() {
290 --m_pDense;
291 return *this;
292 }
293 iterator operator--(int) {
294 iterator temp(*this);
295 --*this;
296 return temp;
297 }
298
299 iterator operator+(size_type offset) const {
300 return {m_pDense + offset};
301 }
302 iterator operator-(size_type offset) const {
303 return {m_pDense - offset};
304 }
305 difference_type operator-(const iterator& other) const {
306 return (difference_type)(m_pDense - other.m_pDense);
307 }
308
309 GAIA_NODISCARD bool operator==(const iterator& other) const {
310 return m_pDense == other.m_pDense;
311 }
312 GAIA_NODISCARD bool operator!=(const iterator& other) const {
313 return m_pDense != other.m_pDense;
314 }
315 GAIA_NODISCARD bool operator>(const iterator& other) const {
316 return m_pDense > other.m_pDense;
317 }
318 GAIA_NODISCARD bool operator>=(const iterator& other) const {
319 return m_pDense >= other.m_pDense;
320 }
321 GAIA_NODISCARD bool operator<(const iterator& other) const {
322 return m_pDense < other.m_pDense;
323 }
324 GAIA_NODISCARD bool operator<=(const iterator& other) const {
325 return m_pDense <= other.m_pDense;
326 }
327 };
328
329 template <typename T, uint32_t PageCapacity, typename Allocator>
330 struct const_sparse_iterator<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>> {
332 using value_type = sparse_id;
333 // using pointer = sparse_id*; not supported
334 // using reference = sparse_id&; not supported
335 using difference_type = detail::difference_type;
336 using size_type = detail::size_type;
338
339 private:
340 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_iterator must be a power of 2");
341 constexpr static sparse_id page_mask = PageCapacity - 1;
342 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
343
345
346 value_type* m_pDense;
347
348 public:
349 const_sparse_iterator(value_type* pDense): m_pDense(pDense) {}
350
351 value_type operator*() const {
352 const auto sid = *m_pDense;
353 return sid;
354 }
355 value_type operator->() const {
356 const auto sid = *m_pDense;
357 return sid;
358 }
359 iterator operator[](size_type offset) const {
360 return {m_pDense + offset};
361 }
362
363 iterator& operator+=(size_type diff) {
364 m_pDense += diff;
365 return *this;
366 }
367 iterator& operator-=(size_type diff) {
368 m_pDense -= diff;
369 return *this;
370 }
371 iterator& operator++() {
372 ++m_pDense;
373 return *this;
374 }
375 iterator operator++(int) {
376 iterator temp(*this);
377 ++*this;
378 return temp;
379 }
380 iterator& operator--() {
381 --m_pDense;
382 return *this;
383 }
384 iterator operator--(int) {
385 iterator temp(*this);
386 --*this;
387 return temp;
388 }
389
390 iterator operator+(size_type offset) const {
391 return {m_pDense + offset};
392 }
393 iterator operator-(size_type offset) const {
394 return {m_pDense - offset};
395 }
396 difference_type operator-(const iterator& other) const {
397 return (difference_type)(m_pDense - other.m_pDense);
398 }
399
400 GAIA_NODISCARD bool operator==(const iterator& other) const {
401 return m_pDense == other.m_pDense;
402 }
403 GAIA_NODISCARD bool operator!=(const iterator& other) const {
404 return m_pDense != other.m_pDense;
405 }
406 GAIA_NODISCARD bool operator>(const iterator& other) const {
407 return m_pDense > other.m_pDense;
408 }
409 GAIA_NODISCARD bool operator>=(const iterator& other) const {
410 return m_pDense >= other.m_pDense;
411 }
412 GAIA_NODISCARD bool operator<(const iterator& other) const {
413 return m_pDense < other.m_pDense;
414 }
415 GAIA_NODISCARD bool operator<=(const iterator& other) const {
416 return m_pDense <= other.m_pDense;
417 }
418 };
419
420 namespace detail {
421 template <typename T, uint32_t PageCapacity, typename Allocator, typename = void>
423 public:
424 using value_type = T;
425 using reference = T&;
426 using const_reference = const T&;
427 using pointer = T*;
428 using const_pointer = const T*;
430 using difference_type = detail::difference_type;
431 using size_type = detail::size_type;
432
435
436 private:
437 size_type* m_pSparse = nullptr;
438 uint8_t* m_pData = nullptr;
439 size_type m_cnt = 0;
440
441 void ensure() {
442 if (m_pSparse != nullptr)
443 return;
444
445 // Allocate memory for sparse->dense index mapping.
446 // Make sure initial values are detail::InvalidDenseId.
447 m_pSparse = mem::AllocHelper::alloc<size_type>("SparsePage", PageCapacity);
448 GAIA_FOR(PageCapacity) m_pSparse[i] = detail::InvalidDenseId;
449
450 // Allocate memory for data
451 m_pData = view_policy::template alloc<Allocator>(PageCapacity);
452 }
453
454 void dtr_data_inter(uint32_t idx) noexcept {
455 GAIA_ASSERT(!empty());
456
457 auto* ptr = &data()[idx];
458 core::call_dtor(ptr);
459 }
460
461 void dtr_active_data() noexcept {
462 GAIA_ASSERT(m_pSparse != nullptr);
463
464 for (uint32_t i = 0; m_cnt != 0 && i != PageCapacity; ++i) {
465 if (m_pSparse[i] == detail::InvalidDenseId)
466 continue;
467
468 auto* ptr = &data()[i];
469 core::call_dtor(ptr);
470 }
471 }
472
473 void invalidate() {
474 if (m_pSparse == nullptr)
475 return;
476
477 // Destruct active items
478 if (m_cnt != 0)
479 dtr_active_data();
480
481 // Release allocated memory
482 mem::AllocHelper::free("SparsePage", m_pSparse);
483 view_policy::template free<Allocator>(m_pData, m_cnt);
484
485 m_pSparse = nullptr;
486 m_pData = nullptr;
487 m_cnt = 0;
488 }
489
490 public:
491 sparse_page() = default;
492
493 sparse_page(const sparse_page& other) {
494 // Copy new items over
495 if (other.m_pSparse == nullptr) {
496 invalidate();
497 } else {
498 ensure();
499
500 for (uint32_t i = 0; i < PageCapacity; ++i) {
501 // Copy indices
502 m_pSparse[i] = other.m_pSparse[i];
503 if (other.m_pSparse[i] == detail::InvalidDenseId)
504 continue;
505
506 // Copy construct data
507 add_data(i, other.set_data(i));
508 }
509
510 m_cnt = other.m_cnt;
511 }
512 }
513
514 sparse_page& operator=(const sparse_page& other) {
515 GAIA_ASSERT(core::addressof(other) != this);
516
517 if (other.m_pSparse == nullptr) {
518 // If the other array is empty, let's just invalidate this one
519 invalidate();
520 } else {
521 ensure();
522
523 // Remove current active items
524 if (m_pSparse != nullptr)
525 dtr_active_data();
526
527 // Copy new items over if there are any
528 for (uint32_t i = 0; i < PageCapacity; ++i) {
529 // Copy indices
530 m_pSparse[i] = other.m_pSparse[i];
531 if (other.m_pSparse[i] == detail::InvalidDenseId)
532 continue;
533
534 // Copy construct data
535 add_data(i, other.get_data(i));
536 }
537
538 m_cnt = other.m_cnt;
539 }
540
541 return *this;
542 }
543
544 sparse_page(sparse_page&& other) noexcept {
545 m_pSparse = other.m_pSparse;
546 m_pData = other.m_pData;
547 m_cnt = other.m_cnt;
548
549 other.m_pSparse = nullptr;
550 other.m_pData = nullptr;
551 other.m_cnt = size_type(0);
552 }
553
554 sparse_page& operator=(sparse_page&& other) noexcept {
555 GAIA_ASSERT(core::addressof(other) != this);
556
557 invalidate();
558
559 m_pSparse = other.m_pSparse;
560 m_pData = other.m_pData;
561 m_cnt = other.m_cnt;
562
563 other.m_pSparse = nullptr;
564 other.m_pData = nullptr;
565 other.m_cnt = size_type(0);
566
567 return *this;
568 }
569
570 ~sparse_page() {
571 invalidate();
572 }
573
574 GAIA_CLANG_WARNING_PUSH()
575 // Memory is aligned so we can silence this warning
576 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
577
578 GAIA_NODISCARD pointer data() noexcept {
579 return reinterpret_cast<pointer>(m_pData);
580 }
581
582 GAIA_NODISCARD const_pointer data() const noexcept {
583 return reinterpret_cast<const_pointer>(m_pData);
584 }
585
586 GAIA_NODISCARD auto& set_id(size_type pos) noexcept {
587 return m_pSparse[pos];
588 }
589
590 GAIA_NODISCARD auto get_id(size_type pos) const noexcept {
591 return m_pSparse[pos];
592 }
593
594 GAIA_NODISCARD decltype(auto) set_data(size_type pos) noexcept {
595 return view_policy::set({(typename view_policy::TargetCastType)m_pData, PageCapacity}, pos);
596 }
597
598 GAIA_NODISCARD decltype(auto) get_data(size_type pos) const noexcept {
599 return view_policy::get({(typename view_policy::TargetCastType)m_pData, PageCapacity}, pos);
600 }
601
602 GAIA_CLANG_WARNING_POP()
603
604 GAIA_NODISCARD bool allocated() const noexcept {
605 return m_pSparse != nullptr;
606 }
607
608 void add() {
609 ensure();
610 ++m_cnt;
611 }
612
613 decltype(auto) add_data(uint32_t idx, const T& arg) {
614 auto* ptr = &set_data(idx);
615 core::call_ctor(ptr, arg);
616 return (reference)(*ptr);
617 }
618
619 decltype(auto) add_data(uint32_t idx, T&& arg) {
620 auto* ptr = &set_data(idx);
621 core::call_ctor(ptr, GAIA_MOV(arg));
622 return (reference)(*ptr);
623 }
624
625 void del_data(uint32_t idx) noexcept {
626 dtr_data_inter(idx);
627
628 GAIA_ASSERT(m_cnt > 0);
629 --m_cnt;
630
631 // If there is no more data, release the memory allocated by the page
632 if (m_cnt == 0)
633 invalidate();
634 }
635
636 GAIA_NODISCARD size_type size() const noexcept {
637 return m_cnt;
638 }
639
640 GAIA_NODISCARD bool empty() const noexcept {
641 return size() == 0;
642 }
643
644 GAIA_NODISCARD decltype(auto) front() noexcept {
645 GAIA_ASSERT(!empty());
646 return (reference)*begin();
647 }
648
649 GAIA_NODISCARD decltype(auto) front() const noexcept {
650 GAIA_ASSERT(!empty());
651 return (const_reference)*begin();
652 }
653
654 GAIA_NODISCARD decltype(auto) back() noexcept {
655 GAIA_ASSERT(!empty());
656 return (reference)(set_data(m_cnt - 1));
657 }
658
659 GAIA_NODISCARD decltype(auto) back() const noexcept {
660 GAIA_ASSERT(!empty());
661 return (const_reference)set_data(m_cnt - 1);
662 }
663
664 GAIA_NODISCARD auto begin() noexcept {
665 return iterator(data());
666 }
667
668 GAIA_NODISCARD auto begin() const noexcept {
669 return const_iterator(data());
670 }
671
672 GAIA_NODISCARD auto cbegin() const noexcept {
673 return const_iterator(data());
674 }
675
676 GAIA_NODISCARD auto rbegin() noexcept {
677 return iterator((pointer)&back());
678 }
679
680 GAIA_NODISCARD auto rbegin() const noexcept {
681 return const_iterator((pointer)&back());
682 }
683
684 GAIA_NODISCARD auto crbegin() const noexcept {
685 return const_iterator((pointer)&back());
686 }
687
688 GAIA_NODISCARD auto end() noexcept {
689 return iterator(data() + size());
690 }
691
692 GAIA_NODISCARD auto end() const noexcept {
693 return const_iterator(data() + size());
694 }
695
696 GAIA_NODISCARD auto cend() const noexcept {
697 return const_iterator(data() + size());
698 }
699
700 GAIA_NODISCARD auto rend() noexcept {
701 return iterator(data() - 1);
702 }
703
704 GAIA_NODISCARD auto rend() const noexcept {
705 return const_iterator(data() - 1);
706 }
707
708 GAIA_NODISCARD auto crend() const noexcept {
709 return const_iterator(data() - 1);
710 }
711
712 GAIA_NODISCARD bool operator==(const sparse_page& other) const {
713 if (m_cnt != other.m_cnt)
714 return false;
715 const size_type n = size();
716 for (size_type i = 0; i < n; ++i)
717 if (!(get_data(i) == other[i]))
718 return false;
719 return true;
720 }
721
722 GAIA_NODISCARD constexpr bool operator!=(const sparse_page& other) const {
723 return !operator==(other);
724 }
725 };
726
728 template <typename T, uint32_t PageCapacity, typename Allocator>
729 class sparse_page<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>> {
730 public:
731 using value_type = T;
732 // using reference = T&; not supported
733 // using const_reference = const T&; not supported
734 using pointer = T*;
735 using const_pointer = const T*;
737 using difference_type = detail::difference_type;
738 using size_type = detail::size_type;
739
742
743 private:
744 size_type* m_pSparse = nullptr;
745 size_type m_cnt = 0;
746
747 void ensure() {
748 if (m_pSparse == nullptr) {
749 // Allocate memory for sparse->dense index mapping.
750 // Make sure initial values are detail::InvalidId.
751 m_pSparse = mem::AllocHelper::alloc<size_type>("SparsePage", PageCapacity);
752 GAIA_FOR(PageCapacity) m_pSparse[i] = detail::InvalidDenseId;
753 }
754 }
755
756 void dtr_data_inter([[maybe_unused]] uint32_t idx) noexcept {
757 GAIA_ASSERT(!empty());
758 }
759
760 void dtr_active_data() noexcept {
761 GAIA_ASSERT(m_pSparse != nullptr);
762 }
763
764 void invalidate() {
765 if (m_pSparse == nullptr)
766 return;
767
768 // Release allocated memory
769 mem::AllocHelper::free("SparsePage", m_pSparse);
770
771 m_pSparse = nullptr;
772 m_cnt = 0;
773 }
774
775 public:
776 sparse_page() = default;
777
778 sparse_page(const sparse_page& other) {
779 // Copy new items over
780 if (other.m_pSparse == nullptr) {
781 invalidate();
782 } else {
783 for (uint32_t i = 0; i < PageCapacity; ++i) {
784 // Copy indices
785 m_pSparse[i] = other.m_pSparse[i];
786 if (m_pSparse[i] == detail::InvalidDenseId)
787 continue;
788 }
789
790 m_cnt = other.m_cnt;
791 }
792 }
793
794 sparse_page& operator=(const sparse_page& other) {
795 GAIA_ASSERT(core::addressof(other) != this);
796
797 if (m_pSparse == nullptr && other.m_pSparse != nullptr)
798 ensure();
799
800 // Copy new items over if there are any
801 if (other.m_pSparse == nullptr) {
802 invalidate();
803 } else {
804 // Remove current active items
805 if (m_pSparse != nullptr)
806 dtr_active_data();
807
808 // Copy indices
809 for (uint32_t i = 0; i < PageCapacity; ++i)
810 m_pSparse[i] = other.m_pSparse[i];
811
812 m_cnt = other.m_cnt;
813 }
814
815 return *this;
816 }
817
818 sparse_page(sparse_page&& other) noexcept {
819 // This is a newly constructed object.
820 // It can't have any memory allocated, yet.
821 GAIA_ASSERT(m_pSparse == nullptr);
822
823 m_pSparse = other.m_pSparse;
824 m_cnt = other.m_cnt;
825
826 other.m_pSparse = nullptr;
827 other.m_cnt = size_type(0);
828 }
829
830 sparse_page& operator=(sparse_page&& other) noexcept {
831 GAIA_ASSERT(core::addressof(other) != this);
832
833 invalidate();
834
835 m_pSparse = other.m_pSparse;
836 m_cnt = other.m_cnt;
837
838 other.m_pSparse = nullptr;
839 other.m_cnt = size_type(0);
840
841 return *this;
842 }
843
844 ~sparse_page() {
845 invalidate();
846 }
847
848 GAIA_CLANG_WARNING_PUSH()
849 // Memory is aligned so we can silence this warning
850 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
851
852 GAIA_NODISCARD pointer data() noexcept {
853 return reinterpret_cast<pointer>(m_pSparse);
854 }
855
856 GAIA_NODISCARD const_pointer data() const noexcept {
857 return reinterpret_cast<const_pointer>(m_pSparse);
858 }
859
860 GAIA_CLANG_WARNING_POP()
861
862 GAIA_NODISCARD bool allocated() const noexcept {
863 return m_pSparse != nullptr;
864 }
865
866 void add() {
867 ensure();
868 ++m_cnt;
869 }
870
871 GAIA_NODISCARD auto& set_id(size_type pos) noexcept {
872 return m_pSparse[pos];
873 }
874
875 GAIA_NODISCARD auto get_id(size_type pos) const noexcept {
876 return m_pSparse[pos];
877 }
878
879 void del_id(uint32_t idx) noexcept {
880 dtr_data_inter(idx);
881
882 GAIA_ASSERT(m_cnt > 0);
883 --m_cnt;
884
885 // If there is no more data, release the memory allocated by the page
886 if (m_cnt == 0)
887 invalidate();
888 }
889
890 GAIA_NODISCARD size_type size() const noexcept {
891 return m_cnt;
892 }
893
894 GAIA_NODISCARD bool empty() const noexcept {
895 return size() == 0;
896 }
897
898 GAIA_NODISCARD auto front() const noexcept {
899 GAIA_ASSERT(!empty());
900 return *begin();
901 }
902
903 GAIA_NODISCARD auto back() const noexcept {
904 GAIA_ASSERT(!empty());
905 return get_id(m_cnt - 1);
906 }
907
908 GAIA_NODISCARD auto begin() noexcept {
909 return iterator(data());
910 }
911
912 GAIA_NODISCARD auto begin() const noexcept {
913 return const_iterator(data());
914 }
915
916 GAIA_NODISCARD auto cbegin() const noexcept {
917 return const_iterator(data());
918 }
919
920 GAIA_NODISCARD auto rbegin() noexcept {
921 return iterator((pointer)&back());
922 }
923
924 GAIA_NODISCARD auto rbegin() const noexcept {
925 return const_iterator((pointer)&back());
926 }
927
928 GAIA_NODISCARD auto crbegin() const noexcept {
929 return const_iterator((pointer)&back());
930 }
931
932 GAIA_NODISCARD auto end() noexcept {
933 return iterator(data() + size());
934 }
935
936 GAIA_NODISCARD auto end() const noexcept {
937 return const_iterator(data() + size());
938 }
939
940 GAIA_NODISCARD auto cend() const noexcept {
941 return const_iterator(data() + size());
942 }
943
944 GAIA_NODISCARD auto rend() noexcept {
945 return iterator(data() - 1);
946 }
947
948 GAIA_NODISCARD auto rend() const noexcept {
949 return const_iterator(data() - 1);
950 }
951
952 GAIA_NODISCARD auto crend() const noexcept {
953 return const_iterator(data() - 1);
954 }
955
956 GAIA_NODISCARD bool operator==(const sparse_page& other) const {
957 if (m_cnt != other.m_cnt)
958 return false;
959 const size_type n = size();
960 for (size_type i = 0; i < n; ++i)
961 if (!(get_id(i) == other.get_id(i)))
962 return false;
963 return true;
964 }
965
966 GAIA_NODISCARD constexpr bool operator!=(const sparse_page& other) const {
967 return !operator==(other);
968 }
969 };
970 } // namespace detail
971
975 template <
976 typename T, uint32_t PageCapacity = 4096, typename Allocator = mem::DefaultAllocatorAdaptor, typename = void>
978 public:
979 using value_type = T;
980 using reference = T&;
981 using const_reference = const T&;
982 using pointer = T*;
983 using const_pointer = const T*;
985 using difference_type = detail::difference_type;
986 using size_type = detail::size_type;
987
991
992 private:
993 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_storage must be a power of 2");
994 constexpr static sparse_id page_mask = PageCapacity - 1;
995 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
996
1000 cnt::darray<page_type> m_pages;
1002 size_type m_cnt = size_type(0);
1003
1004 void try_grow(uint32_t pid) {
1005 const auto required = m_cnt + 1;
1006 if (required > m_dense.capacity()) {
1007 auto cap = m_dense.capacity() == 0 ? size_type(16) : m_dense.capacity();
1008 while (cap < required)
1009 cap *= 2;
1010 m_dense.reserve(cap);
1011 }
1012 m_dense.resize(required);
1013
1014 // The sparse array has to be able to take any sparse index
1015 if (pid >= m_pages.size())
1016 m_pages.resize(pid + 1);
1017
1018 m_pages[pid].add();
1019 }
1020
1021 public:
1022 constexpr sparse_storage() noexcept = default;
1023
1024 sparse_storage(const sparse_storage& other) {
1025 GAIA_ASSERT(core::addressof(other) != this);
1026
1027 m_dense = other.m_dense;
1028 m_pages = other.m_pages;
1029 m_cnt = other.m_cnt;
1030 }
1031
1032 sparse_storage& operator=(const sparse_storage& other) {
1033 GAIA_ASSERT(core::addressof(other) != this);
1034
1035 m_dense = other.m_dense;
1036 m_pages = other.m_pages;
1037 m_cnt = other.m_cnt;
1038
1039 return *this;
1040 }
1041
1042 sparse_storage(sparse_storage&& other) noexcept {
1043 // This is a newly constructed object.
1044 // It can't have any memory allocated, yet.
1045 GAIA_ASSERT(m_dense.data() == nullptr);
1046
1047 m_dense = GAIA_MOV(other.m_dense);
1048 m_pages = GAIA_MOV(other.m_pages);
1049 m_cnt = other.m_cnt;
1050
1051 other.m_dense = {};
1052 other.m_pages = {};
1053 other.m_cnt = size_type(0);
1054 }
1055
1056 sparse_storage& operator=(sparse_storage&& other) noexcept {
1057 GAIA_ASSERT(core::addressof(other) != this);
1058
1059 m_dense = GAIA_MOV(other.m_dense);
1060 m_pages = GAIA_MOV(other.m_pages);
1061 m_cnt = other.m_cnt;
1062
1063 other.m_dense = {};
1064 other.m_pages = {};
1065 other.m_cnt = size_type(0);
1066
1067 return *this;
1068 }
1069
1070 ~sparse_storage() = default;
1071
1072 GAIA_CLANG_WARNING_PUSH()
1073 // Memory is aligned so we can silence this warning
1074 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
1075
1076 GAIA_NODISCARD decltype(auto) operator[](sparse_id sid) noexcept {
1077 GAIA_ASSERT(has(sid));
1078 const auto pid = uint32_t(sid >> to_page_index);
1079 const auto did = uint32_t(sid & page_mask);
1080
1081 auto& page = m_pages[pid];
1082 return view_policy::set({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
1083 }
1084
1085 GAIA_NODISCARD decltype(auto) operator[](sparse_id sid) const noexcept {
1086 GAIA_ASSERT(has(sid));
1087 const auto pid = uint32_t(sid >> to_page_index);
1088 const auto did = uint32_t(sid & page_mask);
1089
1090 auto& page = m_pages[pid];
1091 return view_policy::get({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
1092 }
1093
1094 GAIA_CLANG_WARNING_POP()
1095
1096
1097 GAIA_NODISCARD bool has(sparse_id sid) const {
1098 if (sid == detail::InvalidSparseId)
1099 return false;
1100
1101 const auto pid = uint32_t(sid >> to_page_index);
1102 if (pid >= m_pages.size())
1103 return false;
1104
1105 const auto did = uint32_t(sid & page_mask);
1106 const auto& page = m_pages[pid];
1107 // Empty pages release their internal buffers but remain in m_pages until the
1108 // outer page array is compacted. Treat such slots as missing.
1109 if (!page.allocated())
1110 return false;
1111
1112 const auto id = page.get_id(did);
1113 return id != detail::InvalidDenseId;
1114 }
1115
1118 GAIA_NODISCARD bool has(const T& arg) const {
1119 const auto sid = to_sparse_id<T>::get(arg);
1120 GAIA_ASSERT(sid != detail::InvalidSparseId);
1121 return has(sid);
1122 }
1123
1127 template <typename TType>
1128 decltype(auto) add(TType&& arg) {
1129 const auto sid = to_sparse_id<T>::get(arg);
1130 if (has(sid)) {
1131 const auto pid = uint32_t(sid >> to_page_index);
1132 const auto did = uint32_t(sid & page_mask);
1133 auto& page = m_pages[pid];
1134 return page.set_data(did);
1135 }
1136
1137 const auto pid = uint32_t(sid >> to_page_index);
1138 const auto did = uint32_t(sid & page_mask);
1139
1140 try_grow(pid);
1141 m_dense[m_cnt] = sid;
1142
1143 auto& page = m_pages[pid];
1144 page.set_id(did) = m_cnt++;
1145 return page.add_data(did, GAIA_FWD(arg));
1146 }
1147
1151 decltype(auto) set(sparse_id sid) {
1152 GAIA_ASSERT(has(sid));
1153
1154 const auto pid = uint32_t(sid >> to_page_index);
1155 const auto did = uint32_t(sid & page_mask);
1156
1157 auto& page = m_pages[pid];
1158 return page.set_data(did);
1159 }
1160
1163 void del(sparse_id sid) noexcept {
1164 GAIA_ASSERT(!empty());
1165 GAIA_ASSERT(sid != detail::InvalidSparseId);
1166
1167 if (!has(sid))
1168 return;
1169
1170 const auto pid = uint32_t(sid >> to_page_index);
1171 const auto did = uint32_t(sid & page_mask);
1172
1173 const auto sidPrev = std::as_const(m_dense)[m_cnt - 1];
1174 const auto pidPrev = uint32_t(sidPrev >> to_page_index);
1175 const auto didPrev = uint32_t(sidPrev & page_mask);
1176
1177 auto& page = m_pages[pid];
1178 const auto id = page.get_id(did);
1179 // The swapped-in dense item may live on a different sparse page.
1180 auto& pagePrev = m_pages[pidPrev];
1181 pagePrev.set_id(didPrev) = id;
1182 page.set_id(did) = detail::InvalidDenseId;
1183 page.del_data(did);
1184 m_dense[id] = sidPrev;
1185 m_dense.resize(m_cnt - 1);
1186
1187 GAIA_ASSERT(m_cnt > 0);
1188 --m_cnt;
1189 }
1190
1193 void del(const T& arg) noexcept {
1194 const auto sid = to_sparse_id<T>::get(arg);
1195 return del(sid);
1196 }
1197
1199 void clear() {
1200 m_dense.resize(0);
1201 m_pages.resize(0);
1202 m_cnt = 0;
1203 }
1204
1206 GAIA_NODISCARD size_type size() const noexcept {
1207 return m_cnt;
1208 }
1209
1211 GAIA_NODISCARD bool empty() const noexcept {
1212 return size() == 0;
1213 }
1214
1215 GAIA_NODISCARD decltype(auto) front() noexcept {
1216 GAIA_ASSERT(!empty());
1217 return (reference)*begin();
1218 }
1219
1220 GAIA_NODISCARD decltype(auto) front() const noexcept {
1221 GAIA_ASSERT(!empty());
1222 return (const_reference)*begin();
1223 }
1224
1225 GAIA_NODISCARD decltype(auto) back() noexcept {
1226 GAIA_ASSERT(!empty());
1227
1228 const auto sid = m_dense[m_cnt - 1];
1229 const auto pid = uint32_t(sid >> to_page_index);
1230 const auto did = uint32_t(sid & page_mask);
1231
1232 return (reference)m_pages[pid].set_data(did);
1233 }
1234
1235 GAIA_NODISCARD decltype(auto) back() const noexcept {
1236 GAIA_ASSERT(!empty());
1237
1238 const auto sid = m_dense[m_cnt - 1];
1239 const auto pid = uint32_t(sid >> to_page_index);
1240 const auto did = uint32_t(sid & page_mask);
1241
1242 return (const_reference)m_pages[pid].get_data(did);
1243 }
1244
1245 GAIA_NODISCARD auto begin() noexcept {
1246 GAIA_ASSERT(!empty());
1247
1248 return iterator(m_dense.data(), m_pages.data());
1249 }
1250
1251 GAIA_NODISCARD auto begin() const noexcept {
1252 GAIA_ASSERT(!empty());
1253
1254 return const_iterator(m_dense.data(), m_pages.data());
1255 }
1256
1257 GAIA_NODISCARD auto cbegin() const noexcept {
1258 GAIA_ASSERT(!empty());
1259
1260 return const_iterator(m_dense.data(), m_pages.data());
1261 }
1262
1263 GAIA_NODISCARD auto end() noexcept {
1264 GAIA_ASSERT(!empty());
1265
1266 return iterator(m_dense.data() + size(), m_pages.data());
1267 }
1268
1269 GAIA_NODISCARD auto end() const noexcept {
1270 GAIA_ASSERT(!empty());
1271
1272 return const_iterator(m_dense.data() + size(), m_pages.data());
1273 }
1274
1275 GAIA_NODISCARD auto cend() const noexcept {
1276 GAIA_ASSERT(!empty());
1277
1278 return const_iterator(m_dense.data() + size(), m_pages.data());
1279 }
1280
1281 GAIA_NODISCARD bool operator==(const sparse_storage& other) const {
1282 // The number of items needs to be the same
1283 if (m_cnt != other.m_cnt)
1284 return false;
1285
1286 // Dense indices need to be the same.
1287 // We don't check m_sparse, because it m_dense doesn't
1288 // match, m_sparse will be different as well.
1289 if (m_dense != other.m_dense)
1290 return false;
1291
1292 // Check data one-by-one.
1293 // We don't compare the entire array, only the actually stored values,
1294 // because their is possible a lot of empty space in the data array (it is sparse).
1295 const size_type n = size();
1296 for (size_type i = 0, cnt = 0; i < n && cnt < m_cnt; ++i, ++cnt) {
1297 const auto sid = m_dense[i];
1298 const auto pid = uint32_t(sid >> to_page_index);
1299 const auto did = uint32_t(sid & page_mask);
1300
1301 const auto& item0 = m_pages[pid].get_data(did);
1302 const auto& item1 = m_pages[pid].get_data(did);
1303
1304 if (!(item0 == item1))
1305 return false;
1306 }
1307 return true;
1308 }
1309
1310 GAIA_NODISCARD constexpr bool operator!=(const sparse_storage& other) const {
1311 return !operator==(other);
1312 }
1313 };
1314
1319 template <typename T, uint32_t PageCapacity, typename Allocator>
1320 class sparse_storage<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>> {
1321 public:
1322 using value_type = T;
1323 using reference = T&;
1324 using const_reference = const T&;
1325 using pointer = T*;
1326 using const_pointer = const T*;
1328 using difference_type = detail::difference_type;
1329 using size_type = detail::size_type;
1330
1334
1335 private:
1336 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_storage must be a power of 2");
1337 constexpr static sparse_id page_mask = PageCapacity - 1;
1338 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
1339
1341 cnt::darray<sparse_id> m_dense;
1343 cnt::darray<page_type> m_pages;
1345 size_type m_cnt = size_type(0);
1346
1347 void try_grow(uint32_t pid) {
1348 const auto required = m_cnt + 1;
1349 if (required > m_dense.capacity()) {
1350 auto cap = m_dense.capacity() == 0 ? size_type(16) : m_dense.capacity();
1351 while (cap < required)
1352 cap *= 2;
1353 m_dense.reserve(cap);
1354 }
1355 m_dense.resize(required);
1356
1357 // The sparse array has to be able to take any sparse index
1358 if (pid >= m_pages.size())
1359 m_pages.resize(pid + 1);
1360
1361 m_pages[pid].add();
1362 }
1363
1364 public:
1365 constexpr sparse_storage() noexcept = default;
1366
1367 sparse_storage(const sparse_storage& other) {
1368 GAIA_ASSERT(core::addressof(other) != this);
1369
1370 m_dense = other.m_dense;
1371 m_pages = other.m_pages;
1372 m_cnt = other.m_cnt;
1373 }
1374
1375 sparse_storage& operator=(const sparse_storage& other) {
1376 GAIA_ASSERT(core::addressof(other) != this);
1377
1378 m_dense = other.m_dense;
1379 m_pages = other.m_pages;
1380 m_cnt = other.m_cnt;
1381
1382 return *this;
1383 }
1384
1385 sparse_storage(sparse_storage&& other) noexcept {
1386 // This is a newly constructed object.
1387 // It can't have any memory allocated, yet.
1388 GAIA_ASSERT(m_dense.data() == nullptr);
1389
1390 m_dense = GAIA_MOV(other.m_dense);
1391 m_pages = GAIA_MOV(other.m_pages);
1392 m_cnt = other.m_cnt;
1393
1394 other.m_dense = {};
1395 other.m_pages = {};
1396 other.m_cnt = size_type(0);
1397 }
1398
1399 sparse_storage& operator=(sparse_storage&& other) noexcept {
1400 GAIA_ASSERT(core::addressof(other) != this);
1401
1402 m_dense = GAIA_MOV(other.m_dense);
1403 m_pages = GAIA_MOV(other.m_pages);
1404 m_cnt = other.m_cnt;
1405
1406 other.m_dense = {};
1407 other.m_pages = {};
1408 other.m_cnt = size_type(0);
1409
1410 return *this;
1411 }
1412
1413 ~sparse_storage() = default;
1414
1416 GAIA_NODISCARD bool has(sparse_id sid) const {
1417 GAIA_ASSERT(sid != detail::InvalidSparseId);
1418
1419 const auto pid = uint32_t(sid >> to_page_index);
1420 const auto did = uint32_t(sid & page_mask);
1421 return has_internal(pid, did);
1422 }
1423
1424 private:
1425 GAIA_NODISCARD bool has_internal(uint32_t pid, uint32_t did) const {
1426 if (pid >= m_pages.size())
1427 return false;
1428
1429 const auto& page = m_pages[pid];
1430 if (!page.allocated())
1431 return false;
1432
1433 const auto id = page.get_id(did);
1434 return id != detail::InvalidDenseId;
1435 }
1436
1437 public:
1440 void add(sparse_id sid) {
1441 GAIA_ASSERT(sid != detail::InvalidSparseId);
1442
1443 const auto pid = uint32_t(sid >> to_page_index);
1444 const auto did = uint32_t(sid & page_mask);
1445
1446 if (has_internal(pid, did))
1447 return;
1448
1449 try_grow(pid);
1450 m_dense[m_cnt] = sid;
1451
1452 auto& page = m_pages[pid];
1453 page.set_id(did) = m_cnt++;
1454 }
1455
1458 void del(sparse_id sid) noexcept {
1459 GAIA_ASSERT(!empty());
1460 GAIA_ASSERT(sid != detail::InvalidSparseId);
1461
1462 const auto pid = uint32_t(sid >> to_page_index);
1463 const auto did = uint32_t(sid & page_mask);
1464
1465 if (!has_internal(pid, did))
1466 return;
1467
1468 const auto sidPrev = std::as_const(m_dense)[m_cnt - 1];
1469 const auto pidPrev = uint32_t(sidPrev >> to_page_index);
1470 const auto didPrev = uint32_t(sidPrev & page_mask);
1471
1472 auto& page = m_pages[pid];
1473 const auto id = page.get_id(did);
1474 // The swapped-in dense item may live on a different sparse page.
1475 auto& pagePrev = m_pages[pidPrev];
1476 pagePrev.set_id(didPrev) = id;
1477 page.set_id(did) = detail::InvalidDenseId;
1478 m_dense[id] = sidPrev;
1479 m_dense.resize(m_cnt - 1);
1480
1481 GAIA_ASSERT(m_cnt > 0);
1482 --m_cnt;
1483 }
1484
1486 void clear() {
1487 m_dense.resize(0);
1488 m_pages.resize(0);
1489 m_cnt = 0;
1490 }
1491
1493 GAIA_NODISCARD size_type size() const noexcept {
1494 return m_cnt;
1495 }
1496
1498 GAIA_NODISCARD bool empty() const noexcept {
1499 return size() == 0;
1500 }
1501
1502 GAIA_NODISCARD decltype(auto) front() noexcept {
1503 GAIA_ASSERT(!empty());
1504 return (reference)*begin();
1505 }
1506
1507 GAIA_NODISCARD decltype(auto) front() const noexcept {
1508 GAIA_ASSERT(!empty());
1509 return (const_reference)*begin();
1510 }
1511
1512 GAIA_NODISCARD decltype(auto) back() noexcept {
1513 GAIA_ASSERT(!empty());
1514
1515 const auto sid = m_dense[m_cnt - 1];
1516 const auto pid = uint32_t(sid >> to_page_index);
1517 const auto did = uint32_t(sid & page_mask);
1518
1519 return (reference)m_pages[pid].set_id(did);
1520 }
1521
1522 GAIA_NODISCARD decltype(auto) back() const noexcept {
1523 GAIA_ASSERT(!empty());
1524
1525 const auto sid = m_dense[m_cnt - 1];
1526 const auto pid = uint32_t(sid >> to_page_index);
1527 const auto did = uint32_t(sid & page_mask);
1528
1529 return (const_reference)m_pages[pid].get_id(did);
1530 }
1531
1532 GAIA_NODISCARD auto begin() noexcept {
1533 GAIA_ASSERT(!empty());
1534 return iterator(m_dense.data());
1535 }
1536
1537 GAIA_NODISCARD auto begin() const noexcept {
1538 GAIA_ASSERT(!empty());
1539 return const_iterator(m_dense.data());
1540 }
1541
1542 GAIA_NODISCARD auto cbegin() const noexcept {
1543 GAIA_ASSERT(!empty());
1544 return const_iterator(m_dense.data());
1545 }
1546
1547 GAIA_NODISCARD auto end() noexcept {
1548 GAIA_ASSERT(!empty());
1549 return iterator(m_dense.data() + size());
1550 }
1551
1552 GAIA_NODISCARD auto end() const noexcept {
1553 GAIA_ASSERT(!empty());
1554 return const_iterator(m_dense.data() + size());
1555 }
1556
1557 GAIA_NODISCARD auto cend() const noexcept {
1558 GAIA_ASSERT(!empty());
1559 return const_iterator(m_dense.data() + size());
1560 }
1561
1562 GAIA_NODISCARD bool operator==(const sparse_storage& other) const {
1563 // The number of items needs to be the same
1564 if (m_cnt != other.m_cnt)
1565 return false;
1566
1567 // Dense indices need to be the same.
1568 // We don't check m_sparse, because it m_dense doesn't
1569 // match, m_sparse will be different as well.
1570 if (m_dense != other.m_dense)
1571 return false;
1572
1573 return true;
1574 }
1575
1576 GAIA_NODISCARD constexpr bool operator!=(const sparse_storage& other) const {
1577 return !operator==(other);
1578 }
1579 };
1580 } // namespace cnt
1581
1582} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:25
Sparse page. Specialized for zero-size.
Definition sparse_storage.h:729
Definition sparse_storage.h:422
void del(sparse_id sid) noexcept
Removes a sparse id from storage.
Definition sparse_storage.h:1458
GAIA_NODISCARD bool has(sparse_id sid) const
Checks if an item with a given sparse id.
Definition sparse_storage.h:1416
GAIA_NODISCARD bool empty() const noexcept
Checks if the storage is empty (no items inserted)
Definition sparse_storage.h:1498
void add(sparse_id sid)
Registers a new sparse id.
Definition sparse_storage.h:1440
GAIA_NODISCARD size_type size() const noexcept
Returns the number of items inserted into the storage.
Definition sparse_storage.h:1493
Array with variable size of elements of type.
Definition sparse_storage.h:977
GAIA_NODISCARD size_type size() const noexcept
Returns the number of items inserted into the storage.
Definition sparse_storage.h:1206
decltype(auto) set(sparse_id sid)
Update the record at the index sid.
Definition sparse_storage.h:1151
GAIA_NODISCARD bool empty() const noexcept
Checks if the storage is empty (no items inserted)
Definition sparse_storage.h:1211
GAIA_NODISCARD bool has(sparse_id sid) const
Checks if an item with a given sparse id.
Definition sparse_storage.h:1097
GAIA_NODISCARD bool has(const T &arg) const
Checks if an item arg exists within the storage.
Definition sparse_storage.h:1118
void del(sparse_id sid) noexcept
Removes the item at the index sid from the storage.
Definition sparse_storage.h:1163
void clear()
Clears the storage.
Definition sparse_storage.h:1199
decltype(auto) add(TType &&arg)
Inserts the item arg into the storage.
Definition sparse_storage.h:1128
void del(const T &arg) noexcept
Removes the item arg from the storage.
Definition sparse_storage.h:1193
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition sparse_storage.h:140
Definition sparse_storage.h:42
Definition sparse_storage.h:31
Definition mem_alloc.h:104
View policy for accessing and storing data in the AoS way. Good for random access and when accessing ...
Definition data_layout_policy.h:112