Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
ilist.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstddef>
5#include <cstdint>
6#include <type_traits>
7
8#include "gaia/cnt/bitset.h"
9#include "gaia/cnt/darray.h"
10#include "gaia/core/utility.h"
11#include "gaia/mem/mem_alloc.h"
12#include "gaia/mem/raw_data_holder.h"
13
14namespace gaia {
15 namespace cnt {
18 template <typename TListItem>
19 struct ilist_item_traits;
20
22 namespace detail {
23 template <typename T, typename = void>
24 struct ilist_has_idx_member: std::false_type {};
25 template <typename T>
26 struct ilist_has_idx_member<T, std::void_t<decltype(std::declval<T&>().idx)>>: std::true_type {};
27
28 template <typename T, typename = void>
29 struct ilist_has_gen_member: std::false_type {};
30 template <typename T>
31 struct ilist_has_gen_member<T, std::void_t<decltype(std::declval<T&>().gen)>>: std::true_type {};
32
33 template <typename T, typename = void>
34 struct ilist_has_data_gen_member: std::false_type {};
35 template <typename T>
36 struct ilist_has_data_gen_member<T, std::void_t<decltype(std::declval<T&>().data.gen)>>: std::true_type {};
37
38 template <typename T, typename = void>
39 struct ilist_has_id_mask: std::false_type {};
40 template <typename T>
41 struct ilist_has_id_mask<T, std::void_t<decltype(T::IdMask)>>: std::true_type {};
42
43 template <typename T, typename = void>
44 struct ilist_has_handle_id: std::false_type {};
45 template <typename T>
46 struct ilist_has_handle_id<T, std::void_t<decltype(std::declval<const T&>().id())>>: std::true_type {};
47
48 template <typename T, typename = void>
49 struct ilist_has_handle_gen: std::false_type {};
50 template <typename T>
51 struct ilist_has_handle_gen<T, std::void_t<decltype(std::declval<const T&>().gen())>>: std::true_type {};
52
53 template <typename T, typename = void>
54 struct ilist_has_create: std::false_type {};
55 template <typename T>
56 struct ilist_has_create<
57 T, std::void_t<decltype(T::create(std::declval<uint32_t>(), std::declval<uint32_t>(), (void*)nullptr))>>:
58 std::true_type {};
59
60 template <typename T, typename THandle, typename = void>
61 struct ilist_has_handle: std::false_type {};
62 template <typename T, typename THandle>
63 struct ilist_has_handle<T, THandle, std::void_t<decltype(T::handle(std::declval<const T&>()))>>:
64 std::bool_constant<std::is_convertible_v<decltype(T::handle(std::declval<const T&>())), THandle>> {};
65
66 template <typename T, typename = void>
67 struct ilist_traits_has_idx: std::false_type {};
68 template <typename T>
69 struct ilist_traits_has_idx<T, std::void_t<decltype(ilist_item_traits<T>::idx(std::declval<const T&>()))>>:
70 std::true_type {};
71
72 template <typename T, typename = void>
73 struct ilist_traits_has_set_idx: std::false_type {};
74 template <typename T>
75 struct ilist_traits_has_set_idx<
76 T, std::void_t<decltype(ilist_item_traits<T>::set_idx(std::declval<T&>(), std::declval<uint32_t>()))>>:
77 std::true_type {};
78
79 template <typename T, typename = void>
80 struct ilist_traits_has_gen: std::false_type {};
81 template <typename T>
82 struct ilist_traits_has_gen<T, std::void_t<decltype(ilist_item_traits<T>::gen(std::declval<const T&>()))>>:
83 std::true_type {};
84
85 template <typename T, typename = void>
86 struct ilist_traits_has_set_gen: std::false_type {};
87 template <typename T>
88 struct ilist_traits_has_set_gen<
89 T, std::void_t<decltype(ilist_item_traits<T>::set_gen(std::declval<T&>(), std::declval<uint32_t>()))>>:
90 std::true_type {};
91 } // namespace detail
93
96 template <typename TListItem>
98 static_assert(
99 detail::ilist_has_idx_member<TListItem>::value,
100 "ilist item type must expose idx or specialize ilist_item_traits");
101 static_assert(
102 detail::ilist_has_gen_member<TListItem>::value || detail::ilist_has_data_gen_member<TListItem>::value,
103 "ilist item type must expose gen/data.gen or specialize ilist_item_traits");
104
108 GAIA_NODISCARD static uint32_t idx(const TListItem& item) noexcept {
109 return (uint32_t)item.idx;
110 }
111
115 static void set_idx(TListItem& item, uint32_t value) noexcept {
116 item.idx = value;
117 }
118
122 GAIA_NODISCARD static uint32_t gen(const TListItem& item) noexcept {
123 if constexpr (detail::ilist_has_gen_member<TListItem>::value)
124 return (uint32_t)item.gen;
125 else
126 return (uint32_t)item.data.gen;
127 }
128
132 static void set_gen(TListItem& item, uint32_t value) noexcept {
133 if constexpr (detail::ilist_has_gen_member<TListItem>::value)
134 item.gen = value;
135 else
136 item.data.gen = value;
137 }
138 };
139
141 struct ilist_item {
143 struct ItemData {
146 };
147
153
154 ilist_item() = default;
158 ilist_item(uint32_t index, uint32_t generation): idx(index) {
159 data.gen = generation;
160 }
161
164 ilist_item(const ilist_item& other) {
165 idx = other.idx;
166 data.gen = other.data.gen;
167 }
172 GAIA_ASSERT(core::addressof(other) != this);
173 idx = other.idx;
174 data.gen = other.data.gen;
175 return *this;
176 }
177
181 idx = other.idx;
182 data.gen = other.data.gen;
183
184 other.idx = (uint32_t)-1;
185 other.data.gen = (uint32_t)-1;
186 }
191 GAIA_ASSERT(core::addressof(other) != this);
192 idx = other.idx;
193 data.gen = other.data.gen;
194
195 other.idx = (uint32_t)-1;
196 other.data.gen = (uint32_t)-1;
197 return *this;
198 }
199 };
200
203 template <typename TListItem>
204 struct darray_ilist_storage: public cnt::darray<TListItem> {
208 this->push_back(GAIA_MOV(container));
209 }
210
214 };
215
222 template <typename TListItem, typename TItemHandle, typename TInternalStorage = darray_ilist_storage<TListItem>>
223 struct ilist {
226
236 using const_pointer = const TListItem*;
241
242 // TODO: replace this iterator with a real list iterator
249
250 static_assert(detail::ilist_traits_has_idx<TListItem>::value, "ilist_item_traits<T> must expose idx(const T&)");
251 static_assert(
252 detail::ilist_traits_has_set_idx<TListItem>::value, "ilist_item_traits<T> must expose set_idx(T&, uint32_t)");
253 static_assert(detail::ilist_traits_has_gen<TListItem>::value, "ilist_item_traits<T> must expose gen(const T&)");
254 static_assert(
255 detail::ilist_traits_has_set_gen<TListItem>::value, "ilist_item_traits<T> must expose set_gen(T&, uint32_t)");
256 static_assert(
257 detail::ilist_has_create<TListItem>::value,
258 "ilist item type must expose static create(index, generation, ctx)");
259 static_assert(
260 detail::ilist_has_handle<TListItem, TItemHandle>::value,
261 "ilist item type must expose static handle(const item) returning the handle type");
262 static_assert(detail::ilist_has_id_mask<TItemHandle>::value, "ilist handle type must expose IdMask");
263 static_assert(detail::ilist_has_handle_id<TItemHandle>::value, "ilist handle type must expose id()");
264 static_assert(detail::ilist_has_handle_gen<TItemHandle>::value, "ilist handle type must expose gen()");
271
274 GAIA_NODISCARD pointer data() noexcept {
275 return reinterpret_cast<pointer>(m_items.data());
276 }
277
280 GAIA_NODISCARD const_pointer data() const noexcept {
281 return reinterpret_cast<const_pointer>(m_items.data());
282 }
283
287 GAIA_NODISCARD reference operator[](size_type index) {
288 return m_items[index];
289 }
293 GAIA_NODISCARD const_reference operator[](size_type index) const {
294 return m_items[index];
295 }
296
298 void clear() {
299 m_items.clear();
301 m_freeItems = 0;
302 }
303
307 return m_nextFreeIdx;
308 }
309
313 return m_freeItems;
314 }
315
319 return size() - m_freeItems;
320 }
321
324 GAIA_NODISCARD size_type size() const noexcept {
325 return (size_type)m_items.size();
326 }
327
330 GAIA_NODISCARD bool empty() const noexcept {
331 return size() == 0;
332 }
333
336 GAIA_NODISCARD size_type capacity() const noexcept {
337 return (size_type)m_items.capacity();
338 }
339
342 GAIA_NODISCARD iterator begin() noexcept {
343 return m_items.begin();
344 }
345
349 return m_items.begin();
350 }
351
355 return m_items.begin();
356 }
357
360 GAIA_NODISCARD iterator end() noexcept {
361 return m_items.end();
362 }
363
366 GAIA_NODISCARD const_iterator end() const noexcept {
367 return m_items.end();
368 }
369
373 return m_items.end();
374 }
375
380 }
381
385 GAIA_NODISCARD TItemHandle alloc(void* ctx) {
386 if GAIA_UNLIKELY (m_freeItems == 0U) {
387 // We don't want to go out of range for new item
388 const auto itemCnt = (size_type)m_items.size();
389 GAIA_ASSERT(itemCnt < TItemHandle::IdMask && "Trying to allocate too many items!");
390
391 GAIA_GCC_WARNING_PUSH()
392 GAIA_CLANG_WARNING_PUSH()
393 GAIA_GCC_WARNING_DISABLE("-Wstringop-overflow");
394 GAIA_GCC_WARNING_DISABLE("-Wmissing-field-initializers");
395 GAIA_CLANG_WARNING_DISABLE("-Wmissing-field-initializers");
396 m_items.add_item(TListItem::create(itemCnt, 0U, ctx));
397 return TListItem::handle(m_items.back());
398 GAIA_GCC_WARNING_POP()
399 GAIA_CLANG_WARNING_POP()
400 }
401
402 // Make sure the list is not broken
403 GAIA_ASSERT(m_nextFreeIdx < (size_type)m_items.size() && "Item recycle list broken!");
404
405 --m_freeItems;
406 const auto index = m_nextFreeIdx;
407 auto& j = m_items[m_nextFreeIdx];
409 j = TListItem::create(index, ilist_item_traits<TListItem>::gen(j), ctx);
410 return TListItem::handle(j);
411 }
412
415 GAIA_NODISCARD TItemHandle alloc() {
416 if GAIA_UNLIKELY (m_freeItems == 0U) {
417 // We don't want to go out of range for new item
418 const auto itemCnt = (size_type)m_items.size();
419 GAIA_ASSERT(itemCnt < TItemHandle::IdMask && "Trying to allocate too many items!");
420
421 GAIA_GCC_WARNING_PUSH()
422 GAIA_CLANG_WARNING_PUSH()
423 GAIA_GCC_WARNING_DISABLE("-Wstringop-overflow");
424 GAIA_GCC_WARNING_DISABLE("-Wmissing-field-initializers");
425 GAIA_CLANG_WARNING_DISABLE("-Wmissing-field-initializers");
426 m_items.add_item(TListItem(itemCnt, 0U));
427 return {itemCnt, 0U};
428 GAIA_GCC_WARNING_POP()
429 GAIA_CLANG_WARNING_POP()
430 }
431
432 // Make sure the list is not broken
433 GAIA_ASSERT(m_nextFreeIdx < (size_type)m_items.size() && "Item recycle list broken!");
434
435 --m_freeItems;
436 const auto index = m_nextFreeIdx;
437 auto& j = m_items[m_nextFreeIdx];
439 return {index, ilist_item_traits<TListItem>::gen(m_items[index])};
440 }
441
447 auto& item = m_items[handle.id()];
448 m_items.del_item(item);
449
450 // Update our implicit list
451 if GAIA_UNLIKELY (m_freeItems == 0)
452 ilist_item_traits<TListItem>::set_idx(item, TItemHandle::IdMask);
453 else
456
457 m_nextFreeIdx = handle.id();
458 ++m_freeItems;
459
460 return item;
461 }
462
464 void validate() const {
467 return;
468
469 // If there's something to remove there has to be at least one entity left
470 GAIA_ASSERT(!m_items.empty());
471
472 auto freeItems = m_freeItems;
474 while (freeItems > 0) {
475 GAIA_ASSERT(nextFreeItem < m_items.size() && "Item recycle list broken!");
476
478 --freeItems;
479 }
480
481 // At this point the index of the last item in list should
482 // point to -1 because that's the tail of our implicit list.
483 GAIA_ASSERT(nextFreeItem == TItemHandle::IdMask);
484 }
485 };
486
490 template <typename TItemHandle, typename = void>
498 (void)prev;
499 return TItemHandle(id, gen);
500 }
501 };
502
505 template <typename TItemHandle>
506 struct ilist_handle_traits<TItemHandle, std::void_t<decltype(std::declval<const TItemHandle&>().prio())>> {
513 return TItemHandle(id, gen, prev.prio());
514 }
515 };
516
523 template <typename TListItem, typename TItemHandle, uint32_t MaxPages = 0>
524 struct paged_ilist;
525
530 template <typename TPagedIList, bool IsConst>
532 using owner_type = std::conditional_t<IsConst, const TPagedIList, TPagedIList>;
533 using owner_pointer = owner_type*;
534
535 owner_pointer m_pOwner = nullptr;
536 typename TPagedIList::size_type m_index = 0;
537
538 void skip_dead() {
539 while (m_pOwner != nullptr && m_index < m_pOwner->size() && !m_pOwner->has(m_index))
540 ++m_index;
541 }
542
543 public:
545 using value_type = typename TPagedIList::value_type;
547 using reference =
548 std::conditional_t<IsConst, typename TPagedIList::const_reference, typename TPagedIList::reference>;
550 using pointer = std::conditional_t<IsConst, typename TPagedIList::const_pointer, typename TPagedIList::pointer>;
552 using difference_type = typename TPagedIList::difference_type;
555
556 paged_ilist_iterator() = default;
557
560 paged_ilist_iterator(owner_pointer pOwner, typename TPagedIList::size_type index):
561 m_pOwner(pOwner), m_index(index) {
562 skip_dead();
563 }
564
567 GAIA_NODISCARD reference operator*() const {
568 return (*m_pOwner)[m_index];
569 }
570
573 GAIA_NODISCARD pointer operator->() const {
574 return &(*m_pOwner)[m_index];
575 }
576
580 ++m_index;
581 skip_dead();
582 return *this;
583 }
584
588 auto tmp = *this;
589 ++(*this);
590 return tmp;
591 }
592
596 GAIA_NODISCARD bool operator==(const paged_ilist_iterator& other) const {
597 return m_pOwner == other.m_pOwner && m_index == other.m_index;
598 }
599
603 GAIA_NODISCARD bool operator!=(const paged_ilist_iterator& other) const {
604 return !(*this == other);
605 }
606 };
607
617 template <typename TListItem, typename TItemHandle, uint32_t MaxPages>
618 struct paged_ilist {
628 using const_pointer = const TListItem*;
630 using difference_type = std::ptrdiff_t;
635
636 static_assert(detail::ilist_traits_has_idx<TListItem>::value, "ilist_item_traits<T> must expose idx(const T&)");
637 static_assert(
638 detail::ilist_traits_has_set_idx<TListItem>::value, "ilist_item_traits<T> must expose set_idx(T&, uint32_t)");
639 static_assert(detail::ilist_traits_has_gen<TListItem>::value, "ilist_item_traits<T> must expose gen(const T&)");
640 static_assert(
641 detail::ilist_traits_has_set_gen<TListItem>::value, "ilist_item_traits<T> must expose set_gen(T&, uint32_t)");
642 static_assert(
643 detail::ilist_has_create<TListItem>::value,
644 "paged_ilist item type must expose static create(index, generation, ctx)");
645 static_assert(
646 detail::ilist_has_handle<TListItem, TItemHandle>::value,
647 "paged_ilist item type must expose static handle(const item) returning the handle type");
648 static_assert(detail::ilist_has_id_mask<TItemHandle>::value, "paged_ilist handle type must expose IdMask");
649 static_assert(detail::ilist_has_handle_id<TItemHandle>::value, "paged_ilist handle type must expose id()");
650 static_assert(detail::ilist_has_handle_gen<TItemHandle>::value, "paged_ilist handle type must expose gen()");
651
652 private:
653 static constexpr size_type target_payload_bytes() noexcept {
654 return 16384;
655 }
656
657 static constexpr size_type min_page_capacity() noexcept {
658 return 8;
659 }
660
661 static constexpr size_type max_page_capacity() noexcept {
662 return 256;
663 }
664
665 static constexpr size_type calc_page_capacity() noexcept {
666 const size_type payloadItemBytes = sizeof(TListItem) == 0 ? 1U : (size_type)sizeof(TListItem);
667 const size_type desired = target_payload_bytes() / payloadItemBytes;
668 if (desired < min_page_capacity())
669 return min_page_capacity();
670 if (desired > max_page_capacity())
671 return max_page_capacity();
672 return desired;
673 }
674
675 static constexpr size_type PageCapacity = calc_page_capacity();
676 static constexpr bool FixedPageTable = MaxPages != 0;
677 static constexpr size_type StaticPageCount = MaxPages == 0 ? 1U : MaxPages;
678
679 struct page_type {
680 using alive_mask_type = cnt::bitset<PageCapacity>;
681 using storage_type = mem::raw_data_holder<TListItem, sizeof(TListItem) * PageCapacity>;
682
683 alive_mask_type aliveMask;
684 TItemHandle handles[PageCapacity]{};
685 uint32_t nextFree[PageCapacity];
686 uint32_t liveCount = 0;
687 storage_type* pStorage = nullptr;
688
689 page_type() {
690 GAIA_FOR(PageCapacity)
691 nextFree[i] = TItemHandle::IdMask;
692 }
693
694 ~page_type() {
695 clear();
696 }
697
698 page_type(const page_type&) = delete;
699 page_type& operator=(const page_type&) = delete;
700
701 page_type(page_type&& other) noexcept:
702 aliveMask(other.aliveMask), liveCount(other.liveCount), pStorage(other.pStorage) {
703 GAIA_FOR(PageCapacity) {
704 handles[i] = other.handles[i];
705 nextFree[i] = other.nextFree[i];
706 }
707
708 other.aliveMask.reset();
709 other.liveCount = 0;
710 other.pStorage = nullptr;
711 }
712
713 page_type& operator=(page_type&& other) noexcept {
714 GAIA_ASSERT(core::addressof(other) != this);
715
716 clear();
717 aliveMask = other.aliveMask;
718 liveCount = other.liveCount;
719 pStorage = other.pStorage;
720 GAIA_FOR(PageCapacity) {
721 handles[i] = other.handles[i];
722 nextFree[i] = other.nextFree[i];
723 }
724
725 other.aliveMask.reset();
726 other.liveCount = 0;
727 other.pStorage = nullptr;
728 return *this;
729 }
730
731 GAIA_NODISCARD pointer data() noexcept {
732 return pStorage == nullptr ? nullptr : reinterpret_cast<pointer>((uint8_t*)*pStorage);
733 }
734
735 GAIA_NODISCARD const_pointer data() const noexcept {
736 return pStorage == nullptr ? nullptr : reinterpret_cast<const_pointer>((const uint8_t*)*pStorage);
737 }
738
739 GAIA_NODISCARD pointer ptr(size_type slot) noexcept {
740 GAIA_ASSERT(slot < PageCapacity);
741 GAIA_ASSERT(pStorage != nullptr);
742 return data() + slot;
743 }
744
745 GAIA_NODISCARD const_pointer ptr(size_type slot) const noexcept {
746 GAIA_ASSERT(slot < PageCapacity);
747 GAIA_ASSERT(pStorage != nullptr);
748 return data() + slot;
749 }
750
751 void ensure_storage() {
752 if GAIA_LIKELY (pStorage != nullptr)
753 return;
754
755 constexpr auto StorageAlignment =
756 alignof(storage_type) < sizeof(void*) ? sizeof(void*) : alignof(storage_type);
757 pStorage = mem::AllocHelper::alloc_alig<storage_type>("PagedIListPage", StorageAlignment);
758 }
759
760 void maybe_release_storage() {
761 if (liveCount != 0 || pStorage == nullptr)
762 return;
763
764 mem::AllocHelper::free_alig("PagedIListPage", pStorage);
765 pStorage = nullptr;
766 }
767
768 void clear() {
769 if (pStorage != nullptr) {
770 for (auto slot: aliveMask)
771 core::call_dtor(ptr(slot));
772 mem::AllocHelper::free_alig("PagedIListPage", pStorage);
773 pStorage = nullptr;
774 }
775
776 aliveMask.reset();
777 liveCount = 0;
778 GAIA_FOR(PageCapacity)
779 nextFree[i] = TItemHandle::IdMask;
780 }
781
782 template <typename... Args>
783 void construct(size_type slot, Args&&... args) {
784 GAIA_ASSERT(slot < PageCapacity);
785 ensure_storage();
786 core::call_ctor(ptr(slot), GAIA_FWD(args)...);
787 aliveMask.set(slot);
788 ++liveCount;
789 }
790
791 void destroy(size_type slot) {
792 GAIA_ASSERT(slot < PageCapacity);
793 GAIA_ASSERT(aliveMask.test(slot));
794 core::call_dtor(ptr(slot));
795 aliveMask.reset(slot);
796 GAIA_ASSERT(liveCount > 0);
797 --liveCount;
798 maybe_release_storage();
799 }
800 };
801
803 cnt::darray<page_type*> m_pages;
806 page_type* m_staticPages[StaticPageCount]{};
807 size_type m_size = 0;
808
809 public:
814
815 private:
816 GAIA_NODISCARD static constexpr size_type page_index(size_type index) noexcept {
817 return index / PageCapacity;
818 }
819
820 GAIA_NODISCARD static constexpr size_type slot_index(size_type index) noexcept {
821 return index % PageCapacity;
822 }
823
824 GAIA_NODISCARD static constexpr size_type page_count_for_slots(size_type slotCnt) noexcept {
825 return slotCnt == 0 ? 0U : (slotCnt + PageCapacity - 1) / PageCapacity;
826 }
827
828 public:
831 GAIA_NODISCARD static constexpr size_type page_capacity() noexcept {
832 return PageCapacity;
833 }
834
838 GAIA_NODISCARD static constexpr size_type page_count_for_capacity(size_type slotCnt) noexcept {
839 return page_count_for_slots(slotCnt);
840 }
841
842 private:
843 GAIA_NODISCARD size_type page_table_size() const noexcept {
844 if constexpr (FixedPageTable)
845 return MaxPages;
846 else
847 return (size_type)m_pages.size();
848 }
849
850 GAIA_NODISCARD page_type*& page_slot(size_type pageIdx) noexcept {
851 if constexpr (FixedPageTable) {
852 GAIA_ASSERT(pageIdx < MaxPages);
853 return m_staticPages[pageIdx];
854 } else {
855 GAIA_ASSERT(pageIdx < (size_type)m_pages.size());
856 return m_pages[pageIdx];
857 }
858 }
859
860 GAIA_NODISCARD const page_type* page_slot(size_type pageIdx) const noexcept {
861 if constexpr (FixedPageTable) {
862 GAIA_ASSERT(pageIdx < MaxPages);
863 return m_staticPages[pageIdx];
864 } else {
865 GAIA_ASSERT(pageIdx < (size_type)m_pages.size());
866 return m_pages[pageIdx];
867 }
868 }
869
870 void clear_pages() {
871 const auto pageCnt = page_table_size();
872 GAIA_FOR(pageCnt) {
873 auto*& pPage = page_slot(i);
874 if (pPage == nullptr)
875 continue;
876 delete pPage;
877 pPage = nullptr;
878 }
879
880 if constexpr (!FixedPageTable)
881 m_pages.clear();
882 }
883
884 void ensure_page_count(size_type slotCnt) {
885 const auto pageCnt = page_count_for_slots(slotCnt);
886 if constexpr (FixedPageTable) {
887 GAIA_ASSERT(pageCnt <= MaxPages && "Trying to allocate too many paged_ilist pages!");
888 } else {
889 if (pageCnt > (size_type)m_pages.size())
890 m_pages.resize(pageCnt, nullptr);
891 }
892 }
893
894 GAIA_NODISCARD page_type& ensure_page(size_type index) {
895 ensure_page_count(index + 1);
896 auto*& pPage = page_slot(page_index(index));
897 if (pPage == nullptr)
898 pPage = new page_type();
899 return *pPage;
900 }
901
902 GAIA_NODISCARD page_type* try_page(size_type index) noexcept {
903 const auto pageIdx = page_index(index);
904 if (pageIdx >= page_table_size())
905 return nullptr;
906 return page_slot(pageIdx);
907 }
908
909 GAIA_NODISCARD const page_type* try_page(size_type index) const noexcept {
910 const auto pageIdx = page_index(index);
911 if (pageIdx >= page_table_size())
912 return nullptr;
913 return page_slot(pageIdx);
914 }
915
916 GAIA_NODISCARD reference slot_ref(size_type index) {
917 auto* pPage = try_page(index);
918 GAIA_ASSERT(pPage != nullptr);
919 return *pPage->ptr(slot_index(index));
920 }
921
922 GAIA_NODISCARD const_reference slot_ref(size_type index) const {
923 auto* pPage = try_page(index);
924 GAIA_ASSERT(pPage != nullptr);
925 return *pPage->ptr(slot_index(index));
926 }
927
928 public:
933
934 ~paged_ilist() {
935 clear_pages();
936 }
937
938 paged_ilist() = default;
939 paged_ilist(const paged_ilist&) = delete;
940 paged_ilist& operator=(const paged_ilist&) = delete;
943 paged_ilist(paged_ilist&& other) noexcept:
944 m_size(other.m_size), m_nextFreeIdx(other.m_nextFreeIdx), m_freeItems(other.m_freeItems) {
945 if constexpr (FixedPageTable) {
946 GAIA_FOR(MaxPages) {
947 m_staticPages[i] = other.m_staticPages[i];
948 other.m_staticPages[i] = nullptr;
949 }
950 } else {
951 m_pages = GAIA_MOV(other.m_pages);
952 }
953
954 other.m_size = 0;
955 other.m_nextFreeIdx = (size_type)-1;
956 other.m_freeItems = 0;
957 }
961 paged_ilist& operator=(paged_ilist&& other) noexcept {
962 GAIA_ASSERT(core::addressof(other) != this);
963 clear_pages();
964
965 if constexpr (FixedPageTable) {
966 GAIA_FOR(MaxPages) {
967 m_staticPages[i] = other.m_staticPages[i];
968 other.m_staticPages[i] = nullptr;
969 }
970 } else {
971 m_pages = GAIA_MOV(other.m_pages);
972 }
973
974 m_size = other.m_size;
975 m_nextFreeIdx = other.m_nextFreeIdx;
976 m_freeItems = other.m_freeItems;
977
978 other.m_size = 0;
979 other.m_nextFreeIdx = (size_type)-1;
980 other.m_freeItems = 0;
981 return *this;
982 }
983
986 GAIA_NODISCARD pointer data() noexcept {
987 return nullptr;
988 }
989
992 GAIA_NODISCARD const_pointer data() const noexcept {
993 return nullptr;
994 }
995
999 GAIA_NODISCARD bool has(size_type index) const noexcept {
1000 if (index >= m_size)
1001 return false;
1002
1003 const auto* pPage = try_page(index);
1004 return pPage != nullptr && pPage->aliveMask.test(slot_index(index));
1005 }
1006
1010 GAIA_NODISCARD bool has(TItemHandle handle) const noexcept {
1011 return has(handle.id()) && this->handle(handle.id()) == handle;
1012 }
1013
1017 GAIA_NODISCARD TItemHandle handle(size_type index) const noexcept {
1018 GAIA_ASSERT(index < m_size);
1019 const auto* pPage = try_page(index);
1020 GAIA_ASSERT(pPage != nullptr);
1021 return pPage->handles[slot_index(index)];
1022 }
1023
1027 GAIA_NODISCARD uint32_t generation(size_type index) const noexcept {
1028 return handle(index).gen();
1029 }
1030
1034 GAIA_NODISCARD uint32_t next_free(size_type index) const noexcept {
1035 GAIA_ASSERT(index < m_size);
1036 const auto* pPage = try_page(index);
1037 GAIA_ASSERT(pPage != nullptr);
1038 return pPage->nextFree[slot_index(index)];
1039 }
1040
1044 GAIA_NODISCARD reference operator[](size_type index) {
1045 GAIA_ASSERT(has(index));
1046 return slot_ref(index);
1047 }
1048
1052 GAIA_NODISCARD const_reference operator[](size_type index) const {
1053 GAIA_ASSERT(has(index));
1054 return slot_ref(index);
1055 }
1056
1058 void clear() {
1059 clear_pages();
1060 m_size = 0;
1062 m_freeItems = 0;
1063 }
1064
1068 return m_nextFreeIdx;
1069 }
1070
1074 return m_freeItems;
1075 }
1076
1080 return m_size - m_freeItems;
1081 }
1082
1085 GAIA_NODISCARD size_type size() const noexcept {
1086 return m_size;
1087 }
1088
1091 GAIA_NODISCARD bool empty() const noexcept {
1092 return m_size == 0;
1093 }
1094
1098 if constexpr (FixedPageTable)
1099 return MaxPages * PageCapacity;
1100 else
1101 return (size_type)m_pages.capacity() * PageCapacity;
1102 }
1103
1106 GAIA_NODISCARD iterator begin() noexcept {
1107 return iterator(this, 0);
1108 }
1109
1113 return const_iterator(this, 0);
1114 }
1115
1119 return const_iterator(this, 0);
1120 }
1121
1124 GAIA_NODISCARD iterator end() noexcept {
1125 return iterator(this, m_size);
1126 }
1127
1131 return const_iterator(this, m_size);
1132 }
1133
1137 return const_iterator(this, m_size);
1138 }
1139
1145 const auto pageCnt = page_count_for_slots(cap);
1146 if constexpr (FixedPageTable) {
1147 GAIA_ASSERT(pageCnt <= MaxPages);
1148 } else {
1149 m_pages.reserve(pageCnt);
1150 }
1151 }
1152
1159 const auto pageCnt = page_count_for_slots(cap);
1160 if constexpr (FixedPageTable) {
1161 GAIA_ASSERT(pageCnt <= MaxPages);
1162 } else {
1163 ensure_page_count(cap);
1164 }
1165 }
1166
1172 GAIA_NODISCARD reference live_unsafe(size_type index) {
1173 auto* pPage = try_page(index);
1174 GAIA_ASSERT(pPage != nullptr);
1175 const auto slot = slot_index(index);
1176 GAIA_ASSERT(pPage->aliveMask.test(slot));
1177 return *pPage->ptr(slot);
1178 }
1179
1185 GAIA_NODISCARD const_reference live_unsafe(size_type index) const {
1186 const auto* pPage = try_page(index);
1187 GAIA_ASSERT(pPage != nullptr);
1188 const auto slot = slot_index(index);
1189 GAIA_ASSERT(pPage->aliveMask.test(slot));
1190 return *pPage->ptr(slot);
1191 }
1192
1197 GAIA_NODISCARD reference payload_unsafe(size_type index) {
1198 auto* pPage = try_page(index);
1199 GAIA_ASSERT(pPage != nullptr);
1200 return *pPage->ptr(slot_index(index));
1201 }
1202
1207 GAIA_NODISCARD const_reference payload_unsafe(size_type index) const {
1208 const auto* pPage = try_page(index);
1209 GAIA_ASSERT(pPage != nullptr);
1210 return *pPage->ptr(slot_index(index));
1211 }
1212
1216 GAIA_NODISCARD pointer try_get(size_type index) noexcept {
1217 if (!has(index))
1218 return nullptr;
1219 return &slot_ref(index);
1220 }
1221
1225 GAIA_NODISCARD const_pointer try_get(size_type index) const noexcept {
1226 if (!has(index))
1227 return nullptr;
1228 return &slot_ref(index);
1229 }
1230
1235 void add_live(TListItem&& item) {
1236 const auto index = (size_type)ilist_item_traits<TListItem>::idx(item);
1237 auto& page = ensure_page(index);
1238 const auto slot = slot_index(index);
1239 const bool existed = index < m_size;
1240 const bool wasAlive = existed && page.aliveMask.test(slot);
1241 const bool wasFree = existed && !wasAlive;
1242
1243 if (index >= m_size)
1244 m_size = index + 1;
1245 else if (wasAlive)
1246 page.destroy(slot);
1247 else if (wasFree && m_freeItems > 0)
1248 --m_freeItems;
1249
1250 page.construct(slot, GAIA_MOV(item));
1251 page.handles[slot] = TListItem::handle(*page.ptr(slot));
1252 page.nextFree[slot] = TItemHandle::IdMask;
1253 }
1254
1259 const auto index = (size_type)handle.id();
1260 auto& page = ensure_page(index);
1261 const auto slot = slot_index(index);
1262 const bool existed = index < m_size;
1263 const bool wasAlive = existed && page.aliveMask.test(slot);
1264 const bool wasFree = existed && !wasAlive;
1265
1266 if (index >= m_size)
1267 m_size = index + 1;
1268 else if (wasAlive)
1269 page.destroy(slot);
1270
1271 page.handles[slot] = handle;
1272 page.nextFree[slot] = nextFreeIdx;
1273 if (!wasFree)
1274 ++m_freeItems;
1275 if (m_nextFreeIdx == (size_type)-1)
1276 m_nextFreeIdx = index;
1277 }
1278
1286
1291 GAIA_NODISCARD TItemHandle alloc(void* ctx) {
1292 size_type index = 0;
1293 uint32_t generation = 0;
1294
1295 if GAIA_UNLIKELY (m_freeItems == 0U) {
1296 index = m_size;
1297 GAIA_ASSERT(index < TItemHandle::IdMask && "Trying to allocate too many items!");
1298 ++m_size;
1299 } else {
1300 GAIA_ASSERT(m_nextFreeIdx < m_size && "Item recycle list broken!");
1301 index = m_nextFreeIdx;
1302 auto& page = ensure_page(index);
1303 const auto slot = slot_index(index);
1304 m_nextFreeIdx = page.nextFree[slot];
1305 page.nextFree[slot] = TItemHandle::IdMask;
1306 generation = page.handles[slot].gen();
1307 --m_freeItems;
1308 if (page.aliveMask.test(slot))
1309 page.destroy(slot);
1310 }
1311
1312 auto& page = ensure_page(index);
1313 const auto slot = slot_index(index);
1314 page.construct(slot, TListItem::create(index, generation, ctx));
1315 page.handles[slot] = TListItem::handle(*page.ptr(slot));
1316 page.nextFree[slot] = TItemHandle::IdMask;
1317 return page.handles[slot];
1318 }
1319
1323 GAIA_NODISCARD TItemHandle alloc() {
1324 size_type index = 0;
1325 uint32_t generation = 0;
1326
1327 if GAIA_UNLIKELY (m_freeItems == 0U) {
1328 index = m_size;
1329 GAIA_ASSERT(index < TItemHandle::IdMask && "Trying to allocate too many items!");
1330 ++m_size;
1331 } else {
1332 GAIA_ASSERT(m_nextFreeIdx < m_size && "Item recycle list broken!");
1333 index = m_nextFreeIdx;
1334 auto& page = ensure_page(index);
1335 const auto slot = slot_index(index);
1336 m_nextFreeIdx = page.nextFree[slot];
1337 page.nextFree[slot] = TItemHandle::IdMask;
1338 generation = page.handles[slot].gen();
1339 --m_freeItems;
1340 if (page.aliveMask.test(slot))
1341 page.destroy(slot);
1342 }
1343
1344 auto& page = ensure_page(index);
1345 const auto slot = slot_index(index);
1346 page.construct(slot, TListItem(index, generation));
1348 page.nextFree[slot] = TItemHandle::IdMask;
1349 return page.handles[slot];
1350 }
1351
1356 GAIA_ASSERT(has(handle));
1357
1358 auto& page = ensure_page(handle.id());
1359 const auto slot = slot_index(handle.id());
1360 page.destroy(slot);
1361 page.handles[slot] = ilist_handle_traits<TItemHandle>::make(handle.id(), handle.gen() + 1, page.handles[slot]);
1362 page.nextFree[slot] = m_freeItems == 0 ? TItemHandle::IdMask : m_nextFreeIdx;
1363 m_nextFreeIdx = handle.id();
1364 ++m_freeItems;
1365 }
1366
1376 GAIA_ASSERT(has(handle));
1377
1378 auto& page = ensure_page(handle.id());
1379 const auto slot = slot_index(handle.id());
1380 page.handles[slot] = ilist_handle_traits<TItemHandle>::make(handle.id(), handle.gen() + 1, page.handles[slot]);
1381 page.nextFree[slot] = m_freeItems == 0 ? TItemHandle::IdMask : m_nextFreeIdx;
1382 m_nextFreeIdx = handle.id();
1383 ++m_freeItems;
1384 }
1385
1387 void validate() const {
1388 if (m_freeItems == 0)
1389 return;
1390
1391 auto freeItems = m_freeItems;
1393 while (freeItems > 0) {
1394 GAIA_ASSERT(nextFreeItem < m_size && "Item recycle list broken!");
1395
1397 --freeItems;
1398 }
1399
1400 GAIA_ASSERT(nextFreeItem == TItemHandle::IdMask);
1401 }
1402 };
1403 } // namespace cnt
1404} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:27
const_pointer const_iterator
Read-only random-access iterator type.
Definition darray_impl.h:49
darr_detail::size_type size_type
Unsigned type used for sizes and indices.
Definition darray_impl.h:44
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
core::random_access_iterator_tag iterator_category
Iterator category exposed by the container.
Definition darray_impl.h:51
GAIA_NODISCARD decltype(auto) back() noexcept
Accesses the last element.
Definition darray_impl.h:542
void clear() noexcept
Removes all elements.
Definition darray_impl.h:449
GAIA_NODISCARD auto begin() noexcept
Returns an iterator to the first element.
Definition darray_impl.h:556
pointer iterator
Mutable random-access iterator type.
Definition darray_impl.h:47
darr_detail::difference_type difference_type
Type used for iterator differences.
Definition darray_impl.h:42
GAIA_NODISCARD bool empty() const noexcept
Checks whether the container has no elements.
Definition darray_impl.h:510
GAIA_NODISCARD pointer data() noexcept
Returns a pointer to the element storage.
Definition darray_impl.h:193
void push_back(const T &arg)
Appends an element.
Definition darray_impl.h:309
GAIA_NODISCARD auto end() noexcept
Returns an iterator one past the last element.
Definition darray_impl.h:592
Forward iterator used by paged_ilist. Kept outside the container template so the container body stays...
Definition ilist.h:531
GAIA_NODISCARD bool operator!=(const paged_ilist_iterator &other) const
Compares iterator positions and owners.
Definition ilist.h:603
GAIA_NODISCARD bool operator==(const paged_ilist_iterator &other) const
Compares iterator positions and owners.
Definition ilist.h:596
paged_ilist_iterator(owner_pointer pOwner, typename TPagedIList::size_type index)
Definition ilist.h:560
std::conditional_t< IsConst, typename TPagedIList::const_reference, typename TPagedIList::reference > reference
Reference type selected from iterator constness.
Definition ilist.h:548
GAIA_NODISCARD pointer operator->() const
Accesses the current live payload.
Definition ilist.h:573
std::conditional_t< IsConst, typename TPagedIList::const_pointer, typename TPagedIList::pointer > pointer
Pointer type selected from iterator constness.
Definition ilist.h:550
paged_ilist_iterator & operator++()
Advances to the next live payload.
Definition ilist.h:579
typename TPagedIList::value_type value_type
Stored payload type.
Definition ilist.h:545
typename TPagedIList::difference_type difference_type
Type used for iterator distances.
Definition ilist.h:552
GAIA_NODISCARD reference operator*() const
Dereferences the current live payload.
Definition ilist.h:567
paged_ilist_iterator operator++(int)
Advances to the next live payload.
Definition ilist.h:587
Contiguous storage adapter used by ilist by default.
Definition ilist.h:204
void del_item(TListItem &container)
Notifies the storage adapter that an item is being recycled.
Definition ilist.h:213
void add_item(TListItem &&container)
Appends a live item.
Definition ilist.h:207
static TItemHandle make(uint32_t id, uint32_t gen, const TItemHandle &prev)
Rebuilds a handle after its generation changes while preserving packed priority bits.
Definition ilist.h:512
Rebuild policy for implicit-list handles after generation changes.
Definition ilist.h:491
static TItemHandle make(uint32_t id, uint32_t gen, const TItemHandle &prev)
Rebuilds a handle after its generation changes.
Definition ilist.h:497
Generation metadata associated with an item slot.
Definition ilist.h:143
uint32_t gen
Generation ID.
Definition ilist.h:145
Access policy for implicit-list slot index and generation metadata.
Definition ilist.h:97
static void set_idx(TListItem &item, uint32_t value) noexcept
Sets an item's slot index or free-list link.
Definition ilist.h:115
static GAIA_NODISCARD uint32_t gen(const TListItem &item) noexcept
Returns an item's generation.
Definition ilist.h:122
static GAIA_NODISCARD uint32_t idx(const TListItem &item) noexcept
Returns an item's slot index or free-list link.
Definition ilist.h:108
static void set_gen(TListItem &item, uint32_t value) noexcept
Sets an item's generation.
Definition ilist.h:132
Basic item metadata supported by ilist.
Definition ilist.h:141
ilist_item(const ilist_item &other)
Copy-constructs item metadata.
Definition ilist.h:164
uint32_t idx
Allocated items: Index in the list. Deleted items: Index of the next deleted item in the list.
Definition ilist.h:150
ilist_item & operator=(const ilist_item &other)
Copy-assigns item metadata.
Definition ilist.h:171
ItemData data
Item data.
Definition ilist.h:152
ilist_item(ilist_item &&other)
Move-constructs item metadata and invalidates the source metadata.
Definition ilist.h:180
ilist_item & operator=(ilist_item &&other)
Move-assigns item metadata and invalidates the source metadata.
Definition ilist.h:190
ilist_item(uint32_t index, uint32_t generation)
Constructs item metadata for a slot.
Definition ilist.h:158
Implicit list. Rather than with pointers, items.
Definition ilist.h:223
GAIA_NODISCARD const_iterator end() const noexcept
Returns the immutable storage end sentinel.
Definition ilist.h:366
GAIA_NODISCARD size_type size() const noexcept
Returns the total number of allocated slots.
Definition ilist.h:324
GAIA_NODISCARD size_type get_next_free_item() const noexcept
Returns the free-list head.
Definition ilist.h:306
GAIA_NODISCARD const_reference operator[](size_type index) const
Returns an item slot by index.
Definition ilist.h:293
TListItem & free(TItemHandle handle)
Invalidates handle. Every time an item is deallocated its generation is increased by one.
Definition ilist.h:446
size_type m_nextFreeIdx
Index of the next item to recycle.
Definition ilist.h:268
void clear()
Removes all slots and resets the free list.
Definition ilist.h:298
typename internal_storage::difference_type difference_type
Underlying iterator distance type.
Definition ilist.h:238
GAIA_NODISCARD reference operator[](size_type index)
Returns an item slot by index.
Definition ilist.h:287
GAIA_NODISCARD iterator end() noexcept
Returns the mutable storage end sentinel.
Definition ilist.h:360
GAIA_NODISCARD iterator begin() noexcept
Returns a mutable iterator to the first storage slot.
Definition ilist.h:342
GAIA_NODISCARD pointer data() noexcept
Returns contiguous item storage.
Definition ilist.h:274
GAIA_NODISCARD const_pointer data() const noexcept
Returns contiguous item storage.
Definition ilist.h:280
void reserve(size_type cap)
Reserves storage for slots.
Definition ilist.h:378
typename internal_storage::iterator iterator
Mutable storage iterator.
Definition ilist.h:244
typename internal_storage::size_type size_type
Underlying size and index type.
Definition ilist.h:240
size_type m_freeItems
Number of items to recycle.
Definition ilist.h:270
void validate() const
Verifies that the implicit linked list is valid.
Definition ilist.h:464
GAIA_NODISCARD size_type get_free_items() const noexcept
Returns the number of recyclable slots.
Definition ilist.h:312
GAIA_NODISCARD size_type capacity() const noexcept
Returns slot capacity.
Definition ilist.h:336
GAIA_NODISCARD TItemHandle alloc()
Allocates a new item in the list.
Definition ilist.h:415
typename internal_storage::iterator_category iterator_category
Underlying iterator category tag.
Definition ilist.h:248
typename internal_storage::const_iterator const_iterator
Immutable storage iterator.
Definition ilist.h:246
GAIA_NODISCARD size_type item_count() const noexcept
Returns the number of live items.
Definition ilist.h:318
GAIA_NODISCARD bool empty() const noexcept
Checks whether no slots have been allocated.
Definition ilist.h:330
internal_storage m_items
Implicit list items.
Definition ilist.h:266
GAIA_NODISCARD const_iterator cend() const noexcept
Returns the immutable storage end sentinel.
Definition ilist.h:372
GAIA_NODISCARD const_iterator begin() const noexcept
Returns an immutable iterator to the first storage slot.
Definition ilist.h:348
GAIA_NODISCARD const_iterator cbegin() const noexcept
Returns an immutable iterator to the first storage slot.
Definition ilist.h:354
GAIA_NODISCARD TItemHandle alloc(void *ctx)
Allocates a new item in the list.
Definition ilist.h:385
Paged implicit list declaration.
Definition ilist.h:618
static GAIA_NODISCARD constexpr size_type page_capacity() noexcept
Returns the compile-time number of payload slots stored in one page.
Definition ilist.h:831
GAIA_NODISCARD size_type size() const noexcept
Returns the total number of addressable slots in use.
Definition ilist.h:1085
GAIA_NODISCARD reference live_unsafe(size_type index)
Returns a live payload slot without consulting list-wide size metadata.
Definition ilist.h:1172
GAIA_NODISCARD size_type get_free_items() const noexcept
Returns the number of recyclable slots.
Definition ilist.h:1073
GAIA_NODISCARD size_type capacity() const noexcept
Returns the slot capacity represented by the page table.
Definition ilist.h:1097
GAIA_NODISCARD pointer data() noexcept
Reports that paged storage is not globally contiguous.
Definition ilist.h:986
GAIA_NODISCARD pointer try_get(size_type index) noexcept
Attempts to access a live payload.
Definition ilist.h:1216
GAIA_NODISCARD const_pointer data() const noexcept
Reports that paged storage is not globally contiguous.
Definition ilist.h:992
uint32_t size_type
Type used for slot indices and sizes.
Definition ilist.h:632
GAIA_NODISCARD size_type item_count() const noexcept
Returns the number of live payloads.
Definition ilist.h:1079
static GAIA_NODISCARD constexpr size_type page_count_for_capacity(size_type slotCnt) noexcept
Calculates how many pages are needed to address slotCnt slots.
Definition ilist.h:838
void add_free(TItemHandle handle, uint32_t nextFreeIdx)
Restores a free slot with a preassigned id/generation and free-list link.
Definition ilist.h:1258
paged_ilist & operator=(paged_ilist &&other) noexcept
Move-assigns a paged list and leaves the source empty.
Definition ilist.h:961
size_type m_freeItems
Number of slots currently linked through the implicit free-list.
Definition ilist.h:813
GAIA_NODISCARD const_reference operator[](size_type index) const
Returns a live payload by slot index.
Definition ilist.h:1052
GAIA_NODISCARD TItemHandle alloc()
Allocates a new item in the list.
Definition ilist.h:1323
void validate() const
Verifies that the implicit free-list links are well formed.
Definition ilist.h:1387
GAIA_NODISCARD const_iterator cend() const noexcept
Returns the immutable end sentinel.
Definition ilist.h:1136
void clear()
Destroys all live payloads, releases all pages, and resets slot metadata.
Definition ilist.h:1058
GAIA_NODISCARD bool empty() const noexcept
Checks whether no slots are in use.
Definition ilist.h:1091
GAIA_NODISCARD const_iterator begin() const noexcept
Returns an iterator over live payload objects only.
Definition ilist.h:1112
const TListItem * const_pointer
Immutable payload pointer.
Definition ilist.h:628
GAIA_NODISCARD iterator end() noexcept
Returns the mutable end sentinel.
Definition ilist.h:1124
TListItem & reference
Mutable payload reference.
Definition ilist.h:622
GAIA_NODISCARD reference operator[](size_type index)
Returns a live payload by slot index.
Definition ilist.h:1044
GAIA_NODISCARD const_reference payload_unsafe(size_type index) const
Returns a constructed payload slot without consulting shared liveness metadata.
Definition ilist.h:1207
size_type m_nextFreeIdx
Head of the implicit free-list, or TItemHandle::IdMask when no slots are free.
Definition ilist.h:811
TListItem * pointer
Mutable payload pointer.
Definition ilist.h:626
GAIA_NODISCARD TItemHandle alloc(void *ctx)
Allocates a new item in the list.
Definition ilist.h:1291
GAIA_NODISCARD uint32_t generation(size_type index) const noexcept
Returns a slot's generation.
Definition ilist.h:1027
GAIA_NODISCARD const_iterator cbegin() const noexcept
Returns an iterator over live payload objects only.
Definition ilist.h:1118
GAIA_NODISCARD bool has(size_type index) const noexcept
Checks whether a slot contains a live payload.
Definition ilist.h:999
GAIA_NODISCARD const_iterator end() const noexcept
Returns the immutable end sentinel.
Definition ilist.h:1130
GAIA_NODISCARD reference payload_unsafe(size_type index)
Returns a constructed payload slot without consulting shared liveness metadata.
Definition ilist.h:1197
GAIA_NODISCARD TItemHandle handle(size_type index) const noexcept
Returns the handle metadata stored for a slot.
Definition ilist.h:1017
GAIA_NODISCARD const_reference live_unsafe(size_type index) const
Returns a live payload slot without consulting list-wide size metadata.
Definition ilist.h:1185
GAIA_NODISCARD iterator begin() noexcept
Returns an iterator over live payload objects only.
Definition ilist.h:1106
void add_free(size_type index, uint32_t generation, uint32_t nextFreeIdx)
Restores a free slot with a preassigned id/generation and free-list link.
Definition ilist.h:1283
std::ptrdiff_t difference_type
Type used for iterator distances.
Definition ilist.h:630
GAIA_NODISCARD bool has(TItemHandle handle) const noexcept
Checks whether a handle identifies its current live payload.
Definition ilist.h:1010
void reserve(size_type cap)
Reserves page-table capacity for at least cap slots.
Definition ilist.h:1144
paged_ilist_iterator< paged_ilist, false > iterator
Mutable forward iterator over live payloads.
Definition ilist.h:930
void add_live(TListItem &&item)
Restores a live slot with a preassigned id/generation.
Definition ilist.h:1235
paged_ilist_iterator< paged_ilist, true > const_iterator
Immutable forward iterator over live payloads.
Definition ilist.h:932
GAIA_NODISCARD size_type get_next_free_item() const noexcept
Returns the free-list head.
Definition ilist.h:1067
const TListItem & const_reference
Immutable payload reference.
Definition ilist.h:624
GAIA_NODISCARD uint32_t next_free(size_type index) const noexcept
Returns the free-list link stored for a slot.
Definition ilist.h:1034
GAIA_NODISCARD const_pointer try_get(size_type index) const noexcept
Attempts to access a live payload.
Definition ilist.h:1225
void free(TItemHandle handle)
Frees a live item and destroys its payload immediately.
Definition ilist.h:1355
void free_keep_live(TItemHandle handle)
Frees a handle while keeping the payload alive until slot reuse or clear().
Definition ilist.h:1375
void reserve_slot_table(size_type cap)
Ensures the page pointer table can address cap slots without resizing later.
Definition ilist.h:1158
paged_ilist(paged_ilist &&other) noexcept
Move-constructs a paged list and leaves the source empty.
Definition ilist.h:943
Definition iterator.h:12
static void free_alig(void *ptr)
Releases aligned storage allocated through an adaptor.
Definition mem_alloc.h:291