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
259 value_type operator*() const {
260 const auto sid = *m_pDense;
261 return sid;
262 }
263 value_type operator->() const {
264 const auto sid = *m_pDense;
265 return sid;
266 }
267 iterator operator[](size_type offset) const {
268 return {m_pDense + offset};
269 }
270
271 iterator& operator+=(size_type diff) {
272 m_pDense += diff;
273 return *this;
274 }
275 iterator& operator-=(size_type diff) {
276 m_pDense -= diff;
277 return *this;
278 }
279 iterator& operator++() {
280 ++m_pDense;
281 return *this;
282 }
283 iterator operator++(int) {
284 iterator temp(*this);
285 ++*this;
286 return temp;
287 }
288 iterator& operator--() {
289 --m_pDense;
290 return *this;
291 }
292 iterator operator--(int) {
293 iterator temp(*this);
294 --*this;
295 return temp;
296 }
297
298 iterator operator+(size_type offset) const {
299 return {m_pDense + offset};
300 }
301 iterator operator-(size_type offset) const {
302 return {m_pDense - offset};
303 }
304 difference_type operator-(const iterator& other) const {
305 return (difference_type)(m_pDense - other.m_pDense);
306 }
307
308 GAIA_NODISCARD bool operator==(const iterator& other) const {
309 return m_pDense == other.m_pDense;
310 }
311 GAIA_NODISCARD bool operator!=(const iterator& other) const {
312 return m_pDense != other.m_pDense;
313 }
314 GAIA_NODISCARD bool operator>(const iterator& other) const {
315 return m_pDense > other.m_pDense;
316 }
317 GAIA_NODISCARD bool operator>=(const iterator& other) const {
318 return m_pDense >= other.m_pDense;
319 }
320 GAIA_NODISCARD bool operator<(const iterator& other) const {
321 return m_pDense < other.m_pDense;
322 }
323 GAIA_NODISCARD bool operator<=(const iterator& other) const {
324 return m_pDense <= other.m_pDense;
325 }
326 };
327
328 template <typename T, uint32_t PageCapacity, typename Allocator>
329 struct const_sparse_iterator<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>> {
331 using value_type = sparse_id;
332 // using pointer = sparse_id*; not supported
333 // using reference = sparse_id&; not supported
334 using difference_type = detail::difference_type;
335 using size_type = detail::size_type;
337
338 private:
339 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_iterator must be a power of 2");
340 constexpr static sparse_id page_mask = PageCapacity - 1;
341 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
342
344
345 value_type* m_pDense;
346
347 public:
348 const_sparse_iterator(value_type* pDense): m_pDense(pDense) {}
349
350 value_type operator*() const {
351 const auto sid = *m_pDense;
352 return sid;
353 }
354 value_type operator->() const {
355 const auto sid = *m_pDense;
356 return sid;
357 }
358 iterator operator[](size_type offset) const {
359 return {m_pDense + offset};
360 }
361
362 iterator& operator+=(size_type diff) {
363 m_pDense += diff;
364 return *this;
365 }
366 iterator& operator-=(size_type diff) {
367 m_pDense -= diff;
368 return *this;
369 }
370 iterator& operator++() {
371 ++m_pDense;
372 return *this;
373 }
374 iterator operator++(int) {
375 iterator temp(*this);
376 ++*this;
377 return temp;
378 }
379 iterator& operator--() {
380 --m_pDense;
381 return *this;
382 }
383 iterator operator--(int) {
384 iterator temp(*this);
385 --*this;
386 return temp;
387 }
388
389 iterator operator+(size_type offset) const {
390 return {m_pDense + offset};
391 }
392 iterator operator-(size_type offset) const {
393 return {m_pDense - offset};
394 }
395 difference_type operator-(const iterator& other) const {
396 return (difference_type)(m_pDense - other.m_pDense);
397 }
398
399 GAIA_NODISCARD bool operator==(const iterator& other) const {
400 return m_pDense == other.m_pDense;
401 }
402 GAIA_NODISCARD bool operator!=(const iterator& other) const {
403 return m_pDense != other.m_pDense;
404 }
405 GAIA_NODISCARD bool operator>(const iterator& other) const {
406 return m_pDense > other.m_pDense;
407 }
408 GAIA_NODISCARD bool operator>=(const iterator& other) const {
409 return m_pDense >= other.m_pDense;
410 }
411 GAIA_NODISCARD bool operator<(const iterator& other) const {
412 return m_pDense < other.m_pDense;
413 }
414 GAIA_NODISCARD bool operator<=(const iterator& other) const {
415 return m_pDense <= other.m_pDense;
416 }
417 };
418
419 namespace detail {
420 template <typename T, uint32_t PageCapacity, typename Allocator, typename = void>
422 public:
423 using value_type = T;
424 using reference = T&;
425 using const_reference = const T&;
426 using pointer = T*;
427 using const_pointer = const T*;
429 using difference_type = detail::difference_type;
430 using size_type = detail::size_type;
431
434
435 private:
436 size_type* m_pSparse = nullptr;
437 uint8_t* m_pData = nullptr;
438 size_type m_cnt = 0;
439
440 void ensure() {
441 if (m_pSparse != nullptr)
442 return;
443
444 // Allocate memory for sparse->dense index mapping.
445 // Make sure initial values are detail::InvalidDenseId.
446 m_pSparse = mem::AllocHelper::alloc<size_type>("SparsePage", PageCapacity);
447 GAIA_FOR(PageCapacity) m_pSparse[i] = detail::InvalidDenseId;
448
449 // Allocate memory for data
450 m_pData = view_policy::template alloc<Allocator>(PageCapacity);
451 }
452
453 void dtr_data_inter(uint32_t idx) noexcept {
454 GAIA_ASSERT(!empty());
455
456 auto* ptr = &data()[idx];
457 core::call_dtor(ptr);
458 }
459
460 void dtr_active_data() noexcept {
461 GAIA_ASSERT(m_pSparse != nullptr);
462
463 for (uint32_t i = 0; m_cnt != 0 && i != PageCapacity; ++i) {
464 if (m_pSparse[i] == detail::InvalidDenseId)
465 continue;
466
467 auto* ptr = &data()[i];
468 core::call_dtor(ptr);
469 }
470 }
471
472 void invalidate() {
473 if (m_pSparse == nullptr)
474 return;
475
476 GAIA_ASSERT(m_cnt > 0);
477
478 // Destruct active items
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 void add() {
605 ensure();
606 ++m_cnt;
607 }
608
609 decltype(auto) add_data(uint32_t idx, const T& arg) {
610 auto* ptr = &set_data(idx);
611 core::call_ctor(ptr, arg);
612 return (reference)(*ptr);
613 }
614
615 decltype(auto) add_data(uint32_t idx, T&& arg) {
616 auto* ptr = &set_data(idx);
617 core::call_ctor(ptr, GAIA_MOV(arg));
618 return (reference)(*ptr);
619 }
620
621 void del_data(uint32_t idx) noexcept {
622 dtr_data_inter(idx);
623
624 GAIA_ASSERT(m_cnt > 0);
625 --m_cnt;
626
627 // If there is no more data, release the memory allocated by the page
628 if (m_cnt == 0)
629 invalidate();
630 }
631
632 GAIA_NODISCARD size_type size() const noexcept {
633 return m_cnt;
634 }
635
636 GAIA_NODISCARD bool empty() const noexcept {
637 return size() == 0;
638 }
639
640 GAIA_NODISCARD decltype(auto) front() noexcept {
641 GAIA_ASSERT(!empty());
642 return (reference)*begin();
643 }
644
645 GAIA_NODISCARD decltype(auto) front() const noexcept {
646 GAIA_ASSERT(!empty());
647 return (const_reference)*begin();
648 }
649
650 GAIA_NODISCARD decltype(auto) back() noexcept {
651 GAIA_ASSERT(!empty());
652 return (reference)(set_data(m_cnt - 1));
653 }
654
655 GAIA_NODISCARD decltype(auto) back() const noexcept {
656 GAIA_ASSERT(!empty());
657 return (const_reference)set_data(m_cnt - 1);
658 }
659
660 GAIA_NODISCARD auto begin() noexcept {
661 return iterator(data());
662 }
663
664 GAIA_NODISCARD auto begin() const noexcept {
665 return const_iterator(data());
666 }
667
668 GAIA_NODISCARD auto cbegin() const noexcept {
669 return const_iterator(data());
670 }
671
672 GAIA_NODISCARD auto rbegin() noexcept {
673 return iterator((pointer)&back());
674 }
675
676 GAIA_NODISCARD auto rbegin() const noexcept {
677 return const_iterator((pointer)&back());
678 }
679
680 GAIA_NODISCARD auto crbegin() const noexcept {
681 return const_iterator((pointer)&back());
682 }
683
684 GAIA_NODISCARD auto end() noexcept {
685 return iterator(data() + size());
686 }
687
688 GAIA_NODISCARD auto end() const noexcept {
689 return const_iterator(data() + size());
690 }
691
692 GAIA_NODISCARD auto cend() const noexcept {
693 return const_iterator(data() + size());
694 }
695
696 GAIA_NODISCARD auto rend() noexcept {
697 return iterator(data() - 1);
698 }
699
700 GAIA_NODISCARD auto rend() const noexcept {
701 return const_iterator(data() - 1);
702 }
703
704 GAIA_NODISCARD auto crend() const noexcept {
705 return const_iterator(data() - 1);
706 }
707
708 GAIA_NODISCARD bool operator==(const sparse_page& other) const {
709 if (m_cnt != other.m_cnt)
710 return false;
711 const size_type n = size();
712 for (size_type i = 0; i < n; ++i)
713 if (!(get_data(i) == other[i]))
714 return false;
715 return true;
716 }
717
718 GAIA_NODISCARD constexpr bool operator!=(const sparse_page& other) const {
719 return !operator==(other);
720 }
721 };
722
724 template <typename T, uint32_t PageCapacity, typename Allocator>
725 class sparse_page<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>> {
726 public:
727 using value_type = T;
728 // using reference = T&; not supported
729 // using const_reference = const T&; not supported
730 using pointer = T*;
731 using const_pointer = const T*;
733 using difference_type = detail::difference_type;
734 using size_type = detail::size_type;
735
738
739 private:
740 size_type* m_pSparse = nullptr;
741 size_type m_cnt = 0;
742
743 void ensure() {
744 if (m_pSparse == nullptr) {
745 // Allocate memory for sparse->dense index mapping.
746 // Make sure initial values are detail::InvalidId.
747 m_pSparse = mem::AllocHelper::alloc<size_type>("SparsePage", PageCapacity);
748 GAIA_FOR(PageCapacity) m_pSparse[i] = detail::InvalidDenseId;
749 }
750 }
751
752 void dtr_data_inter([[maybe_unused]] uint32_t idx) noexcept {
753 GAIA_ASSERT(!empty());
754 }
755
756 void dtr_active_data() noexcept {
757 GAIA_ASSERT(m_pSparse != nullptr);
758 }
759
760 void invalidate() {
761 if (m_pSparse == nullptr)
762 return;
763
764 // Release allocated memory
765 mem::AllocHelper::free("SparsePage", m_pSparse);
766
767 m_pSparse = nullptr;
768 m_cnt = 0;
769 }
770
771 public:
772 sparse_page() = default;
773
774 sparse_page(const sparse_page& other) {
775 // Copy new items over
776 if (other.m_pSparse == nullptr) {
777 invalidate();
778 } else {
779 for (uint32_t i = 0; i < PageCapacity; ++i) {
780 // Copy indices
781 m_pSparse[i] = other.m_pSparse[i];
782 if (m_pSparse[i] == detail::InvalidDenseId)
783 continue;
784 }
785
786 m_cnt = other.m_cnt;
787 }
788 }
789
790 sparse_page& operator=(const sparse_page& other) {
791 GAIA_ASSERT(core::addressof(other) != this);
792
793 if (m_pSparse == nullptr && other.m_pSparse != nullptr)
794 ensure();
795
796 // Copy new items over if there are any
797 if (other.m_pSparse == nullptr) {
798 invalidate();
799 } else {
800 // Remove current active items
801 if (m_pSparse != nullptr)
802 dtr_active_data();
803
804 // Copy indices
805 for (uint32_t i = 0; i < PageCapacity; ++i)
806 m_pSparse[i] = other.m_pSparse[i];
807
808 m_cnt = other.m_cnt;
809 }
810
811 return *this;
812 }
813
814 sparse_page(sparse_page&& other) noexcept {
815 // This is a newly constructed object.
816 // It can't have any memory allocated, yet.
817 GAIA_ASSERT(m_pSparse == nullptr);
818
819 m_pSparse = other.m_pSparse;
820 m_cnt = other.m_cnt;
821
822 other.m_pSparse = nullptr;
823 other.m_cnt = size_type(0);
824 }
825
826 sparse_page& operator=(sparse_page&& other) noexcept {
827 GAIA_ASSERT(core::addressof(other) != this);
828
829 invalidate();
830
831 m_pSparse = other.m_pSparse;
832 m_cnt = other.m_cnt;
833
834 other.m_pSparse = nullptr;
835 other.m_cnt = size_type(0);
836
837 return *this;
838 }
839
840 ~sparse_page() {
841 invalidate();
842 }
843
844 GAIA_CLANG_WARNING_PUSH()
845 // Memory is aligned so we can silence this warning
846 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
847
848 GAIA_NODISCARD pointer data() noexcept {
849 return reinterpret_cast<pointer>(m_pSparse);
850 }
851
852 GAIA_NODISCARD const_pointer data() const noexcept {
853 return reinterpret_cast<const_pointer>(m_pSparse);
854 }
855
856 GAIA_CLANG_WARNING_POP()
857
858 void add() {
859 ensure();
860 ++m_cnt;
861 }
862
863 GAIA_NODISCARD auto& set_id(size_type pos) noexcept {
864 return m_pSparse[pos];
865 }
866
867 GAIA_NODISCARD auto get_id(size_type pos) const noexcept {
868 return m_pSparse[pos];
869 }
870
871 void del_id(uint32_t idx) noexcept {
872 dtr_data_inter(idx);
873
874 GAIA_ASSERT(m_cnt > 0);
875 --m_cnt;
876
877 // If there is no more data, release the memory allocated by the page
878 if (m_cnt == 0)
879 invalidate();
880 }
881
882 GAIA_NODISCARD size_type size() const noexcept {
883 return m_cnt;
884 }
885
886 GAIA_NODISCARD bool empty() const noexcept {
887 return size() == 0;
888 }
889
890 GAIA_NODISCARD auto front() const noexcept {
891 GAIA_ASSERT(!empty());
892 return *begin();
893 }
894
895 GAIA_NODISCARD auto back() const noexcept {
896 GAIA_ASSERT(!empty());
897 return get_id(m_cnt - 1);
898 }
899
900 GAIA_NODISCARD auto begin() noexcept {
901 return iterator(data());
902 }
903
904 GAIA_NODISCARD auto begin() const noexcept {
905 return const_iterator(data());
906 }
907
908 GAIA_NODISCARD auto cbegin() const noexcept {
909 return const_iterator(data());
910 }
911
912 GAIA_NODISCARD auto rbegin() noexcept {
913 return iterator((pointer)&back());
914 }
915
916 GAIA_NODISCARD auto rbegin() const noexcept {
917 return const_iterator((pointer)&back());
918 }
919
920 GAIA_NODISCARD auto crbegin() const noexcept {
921 return const_iterator((pointer)&back());
922 }
923
924 GAIA_NODISCARD auto end() noexcept {
925 return iterator(data() + size());
926 }
927
928 GAIA_NODISCARD auto end() const noexcept {
929 return const_iterator(data() + size());
930 }
931
932 GAIA_NODISCARD auto cend() const noexcept {
933 return const_iterator(data() + size());
934 }
935
936 GAIA_NODISCARD auto rend() noexcept {
937 return iterator(data() - 1);
938 }
939
940 GAIA_NODISCARD auto rend() const noexcept {
941 return const_iterator(data() - 1);
942 }
943
944 GAIA_NODISCARD auto crend() const noexcept {
945 return const_iterator(data() - 1);
946 }
947
948 GAIA_NODISCARD bool operator==(const sparse_page& other) const {
949 if (m_cnt != other.m_cnt)
950 return false;
951 const size_type n = size();
952 for (size_type i = 0; i < n; ++i)
953 if (!(get_id(i) == other.get_id(i)))
954 return false;
955 return true;
956 }
957
958 GAIA_NODISCARD constexpr bool operator!=(const sparse_page& other) const {
959 return !operator==(other);
960 }
961 };
962 } // namespace detail
963
967 template <
968 typename T, uint32_t PageCapacity = 4096, typename Allocator = mem::DefaultAllocatorAdaptor, typename = void>
970 public:
971 using value_type = T;
972 using reference = T&;
973 using const_reference = const T&;
974 using pointer = T*;
975 using const_pointer = const T*;
977 using difference_type = detail::difference_type;
978 using size_type = detail::size_type;
979
983
984 private:
985 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_storage must be a power of 2");
986 constexpr static sparse_id page_mask = PageCapacity - 1;
987 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
988
994 size_type m_cnt = size_type(0);
995
996 void try_grow(uint32_t pid) {
997 m_dense.resize(m_cnt + 1);
998
999 // The sparse array has to be able to take any sparse index
1000 if (pid >= m_pages.size())
1001 m_pages.resize(pid + 1);
1002
1003 m_pages[pid].add();
1004 }
1005
1006 public:
1007 constexpr sparse_storage() noexcept = default;
1008
1009 sparse_storage(const sparse_storage& other) {
1010 GAIA_ASSERT(core::addressof(other) != this);
1011
1012 m_dense = other.m_dense;
1013 m_pages = other.m_pages;
1014 m_cnt = other.m_cnt;
1015 }
1016
1017 sparse_storage& operator=(const sparse_storage& other) {
1018 GAIA_ASSERT(core::addressof(other) != this);
1019
1020 m_dense = other.m_dense;
1021 m_pages = other.m_pages;
1022 m_cnt = other.m_cnt;
1023
1024 return *this;
1025 }
1026
1027 sparse_storage(sparse_storage&& other) noexcept {
1028 // This is a newly constructed object.
1029 // It can't have any memory allocated, yet.
1030 GAIA_ASSERT(m_dense.data() == nullptr);
1031
1032 m_dense = GAIA_MOV(other.m_dense);
1033 m_pages = GAIA_MOV(other.m_pages);
1034 m_cnt = other.m_cnt;
1035
1036 other.m_dense = {};
1037 other.m_pages = {};
1038 other.m_cnt = size_type(0);
1039 }
1040
1041 sparse_storage& operator=(sparse_storage&& other) noexcept {
1042 GAIA_ASSERT(core::addressof(other) != this);
1043
1044 m_dense = GAIA_MOV(other.m_dense);
1045 m_pages = GAIA_MOV(other.m_pages);
1046 m_cnt = other.m_cnt;
1047
1048 other.m_dense = {};
1049 other.m_pages = {};
1050 other.m_cnt = size_type(0);
1051
1052 return *this;
1053 }
1054
1055 ~sparse_storage() = default;
1056
1057 GAIA_CLANG_WARNING_PUSH()
1058 // Memory is aligned so we can silence this warning
1059 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
1060
1061 GAIA_NODISCARD decltype(auto) operator[](sparse_id sid) noexcept {
1062 GAIA_ASSERT(has(sid));
1063 const auto pid = uint32_t(sid >> to_page_index);
1064 const auto did = uint32_t(sid & page_mask);
1065
1066 auto& page = m_pages[pid];
1067 return view_policy::set({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
1068 }
1069
1070 GAIA_NODISCARD decltype(auto) operator[](sparse_id sid) const noexcept {
1071 GAIA_ASSERT(has(sid));
1072 const auto pid = uint32_t(sid >> to_page_index);
1073 const auto did = uint32_t(sid & page_mask);
1074
1075 auto& page = m_pages[pid];
1076 return view_policy::get({(typename view_policy::TargetCastType)page.data(), PageCapacity}, did);
1077 }
1078
1079 GAIA_CLANG_WARNING_POP()
1080
1081
1082 GAIA_NODISCARD bool has(sparse_id sid) const {
1083 if (sid == detail::InvalidSparseId)
1084 return false;
1085
1086 const auto pid = uint32_t(sid >> to_page_index);
1087 if (pid >= m_pages.size())
1088 return false;
1089
1090 const auto did = uint32_t(sid & page_mask);
1091 const auto id = m_pages[pid].get_id(did);
1092 return id != detail::InvalidDenseId;
1093 }
1094
1097 GAIA_NODISCARD bool has(const T& arg) const {
1098 const auto sid = to_sparse_id<T>::get(arg);
1099 GAIA_ASSERT(sid != detail::InvalidSparseId);
1100 return has(sid);
1101 }
1102
1106 template <typename TType>
1107 decltype(auto) add(TType&& arg) {
1108 const auto sid = to_sparse_id<T>::get(arg);
1109 if (has(sid)) {
1110 const auto pid = uint32_t(sid >> to_page_index);
1111 const auto did = uint32_t(sid & page_mask);
1112 auto& page = m_pages[pid];
1113 return page.set_data(did);
1114 }
1115
1116 const auto pid = uint32_t(sid >> to_page_index);
1117 const auto did = uint32_t(sid & page_mask);
1118
1119 try_grow(pid);
1120 m_dense[m_cnt] = sid;
1121
1122 auto& page = m_pages[pid];
1123 page.set_id(did) = m_cnt++;
1124 return page.add_data(did, GAIA_FWD(arg));
1125 }
1126
1130 decltype(auto) set(sparse_id sid) {
1131 GAIA_ASSERT(has(sid));
1132
1133 const auto pid = uint32_t(sid >> to_page_index);
1134 const auto did = uint32_t(sid & page_mask);
1135
1136 auto& page = m_pages[pid];
1137 return page.set_data(did);
1138 }
1139
1142 void del(sparse_id sid) noexcept {
1143 GAIA_ASSERT(!empty());
1144 GAIA_ASSERT(sid != detail::InvalidSparseId);
1145
1146 if (!has(sid))
1147 return;
1148
1149 const auto pid = uint32_t(sid >> to_page_index);
1150 const auto did = uint32_t(sid & page_mask);
1151
1152 const auto sidPrev = std::as_const(m_dense)[m_cnt - 1];
1153 const auto didPrev = uint32_t(sidPrev & page_mask);
1154
1155 auto& page = m_pages[pid];
1156 const auto id = page.get_id(did);
1157 page.set_id(didPrev) = id;
1158 page.set_id(did) = detail::InvalidDenseId;
1159 page.del_data(did);
1160 m_dense[id] = sidPrev;
1161 m_dense.resize(m_cnt - 1);
1162
1163 GAIA_ASSERT(m_cnt > 0);
1164 --m_cnt;
1165 }
1166
1169 void del(const T& arg) noexcept {
1170 const auto sid = to_sparse_id<T>::get(arg);
1171 return del(sid);
1172 }
1173
1175 void clear() {
1176 m_dense.resize(0);
1177 m_pages.resize(0);
1178 m_cnt = 0;
1179 }
1180
1182 GAIA_NODISCARD size_type size() const noexcept {
1183 return m_cnt;
1184 }
1185
1187 GAIA_NODISCARD bool empty() const noexcept {
1188 return size() == 0;
1189 }
1190
1191 GAIA_NODISCARD decltype(auto) front() noexcept {
1192 GAIA_ASSERT(!empty());
1193 return (reference)*begin();
1194 }
1195
1196 GAIA_NODISCARD decltype(auto) front() const noexcept {
1197 GAIA_ASSERT(!empty());
1198 return (const_reference)*begin();
1199 }
1200
1201 GAIA_NODISCARD decltype(auto) back() noexcept {
1202 GAIA_ASSERT(!empty());
1203
1204 const auto sid = m_dense[m_cnt - 1];
1205 const auto pid = uint32_t(sid >> to_page_index);
1206 const auto did = uint32_t(sid & page_mask);
1207
1208 return (reference)m_pages[pid].set_data(did);
1209 }
1210
1211 GAIA_NODISCARD decltype(auto) back() const noexcept {
1212 GAIA_ASSERT(!empty());
1213
1214 const auto sid = m_dense[m_cnt - 1];
1215 const auto pid = uint32_t(sid >> to_page_index);
1216 const auto did = uint32_t(sid & page_mask);
1217
1218 return (const_reference)m_pages[pid].get_data(did);
1219 }
1220
1221 GAIA_NODISCARD auto begin() noexcept {
1222 GAIA_ASSERT(!empty());
1223
1224 return iterator(m_dense.data(), m_pages.data());
1225 }
1226
1227 GAIA_NODISCARD auto begin() const noexcept {
1228 GAIA_ASSERT(!empty());
1229
1230 return const_iterator(m_dense.data(), m_pages.data());
1231 }
1232
1233 GAIA_NODISCARD auto cbegin() const noexcept {
1234 GAIA_ASSERT(!empty());
1235
1236 return const_iterator(m_dense.data(), m_pages.data());
1237 }
1238
1239 GAIA_NODISCARD auto end() noexcept {
1240 GAIA_ASSERT(!empty());
1241
1242 return iterator(m_dense.data() + size(), m_pages.data());
1243 }
1244
1245 GAIA_NODISCARD auto end() const noexcept {
1246 GAIA_ASSERT(!empty());
1247
1248 return const_iterator(m_dense.data() + size(), m_pages.data());
1249 }
1250
1251 GAIA_NODISCARD auto cend() const noexcept {
1252 GAIA_ASSERT(!empty());
1253
1254 return const_iterator(m_dense.data() + size(), m_pages.data());
1255 }
1256
1257 GAIA_NODISCARD bool operator==(const sparse_storage& other) const {
1258 // The number of items needs to be the same
1259 if (m_cnt != other.m_cnt)
1260 return false;
1261
1262 // Dense indices need to be the same.
1263 // We don't check m_sparse, because it m_dense doesn't
1264 // match, m_sparse will be different as well.
1265 if (m_dense != other.m_dense)
1266 return false;
1267
1268 // Check data one-by-one.
1269 // We don't compare the entire array, only the actually stored values,
1270 // because their is possible a lot of empty space in the data array (it is sparse).
1271 const size_type n = size();
1272 for (size_type i = 0, cnt = 0; i < n && cnt < m_cnt; ++i, ++cnt) {
1273 const auto sid = m_dense[i];
1274 const auto pid = uint32_t(sid >> to_page_index);
1275 const auto did = uint32_t(sid & page_mask);
1276
1277 const auto& item0 = m_pages[pid].get_data(did);
1278 const auto& item1 = m_pages[pid].get_data(did);
1279
1280 if (!(item0 == item1))
1281 return false;
1282 }
1283 return true;
1284 }
1285
1286 GAIA_NODISCARD constexpr bool operator!=(const sparse_storage& other) const {
1287 return !operator==(other);
1288 }
1289 };
1290
1295 template <typename T, uint32_t PageCapacity, typename Allocator>
1296 class sparse_storage<T, PageCapacity, Allocator, std::enable_if_t<std::is_empty_v<T>>> {
1297 public:
1298 using value_type = T;
1299 using reference = T&;
1300 using const_reference = const T&;
1301 using pointer = T*;
1302 using const_pointer = const T*;
1304 using difference_type = detail::difference_type;
1305 using size_type = detail::size_type;
1306
1310
1311 private:
1312 static_assert((PageCapacity & (PageCapacity - 1)) == 0, "PageCapacity of sparse_storage must be a power of 2");
1313 constexpr static sparse_id page_mask = PageCapacity - 1;
1314 constexpr static sparse_id to_page_index = core::count_bits(page_mask);
1315
1317 cnt::darray<sparse_id> m_dense;
1319 cnt::darray<page_type> m_pages;
1321 size_type m_cnt = size_type(0);
1322
1323 void try_grow(uint32_t pid) {
1324 m_dense.resize(m_cnt + 1);
1325
1326 // The sparse array has to be able to take any sparse index
1327 if (pid >= m_pages.size())
1328 m_pages.resize(pid + 1);
1329
1330 m_pages[pid].add();
1331 }
1332
1333 public:
1334 constexpr sparse_storage() noexcept = default;
1335
1336 sparse_storage(const sparse_storage& other) {
1337 GAIA_ASSERT(core::addressof(other) != this);
1338
1339 m_dense = other.m_dense;
1340 m_pages = other.m_pages;
1341 m_cnt = other.m_cnt;
1342 }
1343
1344 sparse_storage& operator=(const sparse_storage& other) {
1345 GAIA_ASSERT(core::addressof(other) != this);
1346
1347 m_dense = other.m_dense;
1348 m_pages = other.m_pages;
1349 m_cnt = other.m_cnt;
1350
1351 return *this;
1352 }
1353
1354 sparse_storage(sparse_storage&& other) noexcept {
1355 // This is a newly constructed object.
1356 // It can't have any memory allocated, yet.
1357 GAIA_ASSERT(m_dense.data() == nullptr);
1358
1359 m_dense = GAIA_MOV(other.m_dense);
1360 m_pages = GAIA_MOV(other.m_pages);
1361 m_cnt = other.m_cnt;
1362
1363 other.m_dense = {};
1364 other.m_pages = {};
1365 other.m_cnt = size_type(0);
1366 }
1367
1368 sparse_storage& operator=(sparse_storage&& other) noexcept {
1369 GAIA_ASSERT(core::addressof(other) != this);
1370
1371 m_dense = GAIA_MOV(other.m_dense);
1372 m_pages = GAIA_MOV(other.m_pages);
1373 m_cnt = other.m_cnt;
1374
1375 other.m_dense = {};
1376 other.m_pages = {};
1377 other.m_cnt = size_type(0);
1378
1379 return *this;
1380 }
1381
1382 ~sparse_storage() = default;
1383
1385 GAIA_NODISCARD bool has(sparse_id sid) const {
1386 GAIA_ASSERT(sid != detail::InvalidSparseId);
1387
1388 const auto pid = uint32_t(sid >> to_page_index);
1389 const auto did = uint32_t(sid & page_mask);
1390 return has_internal(pid, did);
1391 }
1392
1393 private:
1394 GAIA_NODISCARD bool has_internal(uint32_t pid, uint32_t did) const {
1395 if (pid >= m_pages.size())
1396 return false;
1397
1398 const auto id = m_pages[pid].get_id(did);
1399 return id != detail::InvalidDenseId;
1400 }
1401
1402 public:
1405 void add(sparse_id sid) {
1406 GAIA_ASSERT(sid != detail::InvalidSparseId);
1407
1408 const auto pid = uint32_t(sid >> to_page_index);
1409 const auto did = uint32_t(sid & page_mask);
1410
1411 if (has_internal(pid, did))
1412 return;
1413
1414 try_grow(pid);
1415 m_dense[m_cnt] = sid;
1416
1417 auto& page = m_pages[pid];
1418 page.set_id(did) = m_cnt++;
1419 }
1420
1423 void del(sparse_id sid) noexcept {
1424 GAIA_ASSERT(!empty());
1425 GAIA_ASSERT(sid != detail::InvalidSparseId);
1426
1427 const auto pid = uint32_t(sid >> to_page_index);
1428 const auto did = uint32_t(sid & page_mask);
1429
1430 if (!has_internal(pid, did))
1431 return;
1432
1433 const auto sidPrev = std::as_const(m_dense)[m_cnt - 1];
1434 const auto didPrev = uint32_t(sidPrev & page_mask);
1435
1436 auto& page = m_pages[pid];
1437 const auto id = page.get_id(did);
1438 page.set_id(didPrev) = id;
1439 page.set_id(did) = detail::InvalidDenseId;
1440 m_dense[id] = sidPrev;
1441 m_dense.resize(m_cnt - 1);
1442
1443 GAIA_ASSERT(m_cnt > 0);
1444 --m_cnt;
1445 }
1446
1448 void clear() {
1449 m_dense.resize(0);
1450 m_pages.resize(0);
1451 m_cnt = 0;
1452 }
1453
1455 GAIA_NODISCARD size_type size() const noexcept {
1456 return m_cnt;
1457 }
1458
1460 GAIA_NODISCARD bool empty() const noexcept {
1461 return size() == 0;
1462 }
1463
1464 GAIA_NODISCARD decltype(auto) front() noexcept {
1465 GAIA_ASSERT(!empty());
1466 return (reference)*begin();
1467 }
1468
1469 GAIA_NODISCARD decltype(auto) front() const noexcept {
1470 GAIA_ASSERT(!empty());
1471 return (const_reference)*begin();
1472 }
1473
1474 GAIA_NODISCARD decltype(auto) back() noexcept {
1475 GAIA_ASSERT(!empty());
1476
1477 const auto sid = m_dense[m_cnt - 1];
1478 const auto pid = uint32_t(sid >> to_page_index);
1479 const auto did = uint32_t(sid & page_mask);
1480
1481 return (reference)m_pages[pid].set_id(did);
1482 }
1483
1484 GAIA_NODISCARD decltype(auto) back() const noexcept {
1485 GAIA_ASSERT(!empty());
1486
1487 const auto sid = m_dense[m_cnt - 1];
1488 const auto pid = uint32_t(sid >> to_page_index);
1489 const auto did = uint32_t(sid & page_mask);
1490
1491 return (const_reference)m_pages[pid].get_id(did);
1492 }
1493
1494 GAIA_NODISCARD auto begin() noexcept {
1495 GAIA_ASSERT(!empty());
1496 return iterator(m_dense.data());
1497 }
1498
1499 GAIA_NODISCARD auto begin() const noexcept {
1500 GAIA_ASSERT(!empty());
1501 return const_iterator(m_dense.data());
1502 }
1503
1504 GAIA_NODISCARD auto cbegin() const noexcept {
1505 GAIA_ASSERT(!empty());
1506 return const_iterator(m_dense.data());
1507 }
1508
1509 GAIA_NODISCARD auto end() noexcept {
1510 GAIA_ASSERT(!empty());
1511 return iterator(m_dense.data() + size());
1512 }
1513
1514 GAIA_NODISCARD auto end() const noexcept {
1515 GAIA_ASSERT(!empty());
1516 return const_iterator(m_dense.data() + size());
1517 }
1518
1519 GAIA_NODISCARD auto cend() const noexcept {
1520 GAIA_ASSERT(!empty());
1521 return const_iterator(m_dense.data() + size());
1522 }
1523
1524 GAIA_NODISCARD bool operator==(const sparse_storage& other) const {
1525 // The number of items needs to be the same
1526 if (m_cnt != other.m_cnt)
1527 return false;
1528
1529 // Dense indices need to be the same.
1530 // We don't check m_sparse, because it m_dense doesn't
1531 // match, m_sparse will be different as well.
1532 if (m_dense != other.m_dense)
1533 return false;
1534
1535 return true;
1536 }
1537
1538 GAIA_NODISCARD constexpr bool operator!=(const sparse_storage& other) const {
1539 return !operator==(other);
1540 }
1541 };
1542 } // namespace cnt
1543
1544} // 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:725
Definition sparse_storage.h:421
void del(sparse_id sid) noexcept
Removes a sparse id from storage.
Definition sparse_storage.h:1423
GAIA_NODISCARD bool has(sparse_id sid) const
Checks if an item with a given sparse id.
Definition sparse_storage.h:1385
GAIA_NODISCARD bool empty() const noexcept
Checks if the storage is empty (no items inserted)
Definition sparse_storage.h:1460
void add(sparse_id sid)
Registers a new sparse id.
Definition sparse_storage.h:1405
GAIA_NODISCARD size_type size() const noexcept
Returns the number of items inserted into the storage.
Definition sparse_storage.h:1455
Array with variable size of elements of type.
Definition sparse_storage.h:969
GAIA_NODISCARD size_type size() const noexcept
Returns the number of items inserted into the storage.
Definition sparse_storage.h:1182
decltype(auto) set(sparse_id sid)
Update the record at the index sid.
Definition sparse_storage.h:1130
GAIA_NODISCARD bool empty() const noexcept
Checks if the storage is empty (no items inserted)
Definition sparse_storage.h:1187
GAIA_NODISCARD bool has(sparse_id sid) const
Checks if an item with a given sparse id.
Definition sparse_storage.h:1082
GAIA_NODISCARD bool has(const T &arg) const
Checks if an item arg exists within the storage.
Definition sparse_storage.h:1097
void del(sparse_id sid) noexcept
Removes the item at the index sid from the storage.
Definition sparse_storage.h:1142
void clear()
Clears the storage.
Definition sparse_storage.h:1175
decltype(auto) add(TType &&arg)
Inserts the item arg into the storage.
Definition sparse_storage.h:1107
void del(const T &arg) noexcept
Removes the item arg from the storage.
Definition sparse_storage.h:1169
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