508 using difference_type = std::ptrdiff_t;
520 "paged_ilist item type must expose static create(index, generation, ctx)");
523 "paged_ilist item type must expose static handle(const item) returning the handle type");
529 static constexpr size_type target_payload_bytes()
noexcept {
533 static constexpr size_type min_page_capacity()
noexcept {
537 static constexpr size_type max_page_capacity()
noexcept {
541 static constexpr size_type calc_page_capacity()
noexcept {
544 if (
desired < min_page_capacity())
545 return min_page_capacity();
546 if (
desired > max_page_capacity())
547 return max_page_capacity();
551 static constexpr size_type PageCapacity = calc_page_capacity();
552 static constexpr bool FixedPageTable =
MaxPages != 0;
559 alive_mask_type aliveMask;
563 storage_type* pStorage =
nullptr;
566 GAIA_FOR(PageCapacity)
567 nextFree[
i] = TItemHandle::IdMask;
574 page_type(
const page_type&) =
delete;
575 page_type& operator=(
const page_type&) =
delete;
577 page_type(page_type&& other)
noexcept:
578 aliveMask(other.aliveMask), liveCount(other.liveCount), pStorage(other.pStorage) {
579 GAIA_FOR(PageCapacity) {
580 handles[
i] = other.handles[
i];
581 nextFree[
i] = other.nextFree[
i];
584 other.aliveMask.reset();
586 other.pStorage =
nullptr;
589 page_type& operator=(page_type&& other)
noexcept {
590 GAIA_ASSERT(core::addressof(other) !=
this);
593 aliveMask = other.aliveMask;
594 liveCount = other.liveCount;
595 pStorage = other.pStorage;
596 GAIA_FOR(PageCapacity) {
597 handles[
i] = other.handles[
i];
598 nextFree[
i] = other.nextFree[
i];
601 other.aliveMask.reset();
603 other.pStorage =
nullptr;
607 GAIA_NODISCARD
pointer data()
noexcept {
615 GAIA_NODISCARD
pointer ptr(size_type
slot)
noexcept {
616 GAIA_ASSERT(
slot < PageCapacity);
617 GAIA_ASSERT(pStorage !=
nullptr);
618 return data() +
slot;
622 GAIA_ASSERT(
slot < PageCapacity);
623 GAIA_ASSERT(pStorage !=
nullptr);
624 return data() +
slot;
627 void ensure_storage() {
628 if GAIA_LIKELY (pStorage !=
nullptr)
632 alignof(storage_type) <
sizeof(
void*) ?
sizeof(
void*) :
alignof(storage_type);
633 pStorage = mem::AllocHelper::alloc_alig<storage_type>(
"PagedIListPage",
StorageAlignment);
636 void maybe_release_storage() {
637 if (liveCount != 0 || pStorage ==
nullptr)
640 mem::AllocHelper::free_alig(
"PagedIListPage", pStorage);
645 if (pStorage !=
nullptr) {
646 for (
auto slot: aliveMask)
647 core::call_dtor(ptr(
slot));
648 mem::AllocHelper::free_alig(
"PagedIListPage", pStorage);
654 GAIA_FOR(PageCapacity)
655 nextFree[
i] = TItemHandle::IdMask;
658 template <
typename...
Args>
660 GAIA_ASSERT(
slot < PageCapacity);
662 core::call_ctor(ptr(
slot), GAIA_FWD(
args)...);
667 void destroy(size_type
slot) {
668 GAIA_ASSERT(
slot < PageCapacity);
669 GAIA_ASSERT(aliveMask.test(
slot));
670 core::call_dtor(ptr(
slot));
671 aliveMask.reset(
slot);
672 GAIA_ASSERT(liveCount > 0);
674 maybe_release_storage();
682 page_type* m_staticPages[StaticPageCount]{};
683 size_type m_size = 0;
692 GAIA_NODISCARD
static constexpr size_type page_index(size_type index)
noexcept {
693 return index / PageCapacity;
696 GAIA_NODISCARD
static constexpr size_type slot_index(size_type index)
noexcept {
697 return index % PageCapacity;
700 GAIA_NODISCARD
static constexpr size_type page_count_for_slots(size_type slotCnt)
noexcept {
701 return slotCnt == 0 ? 0U : (slotCnt + PageCapacity - 1) / PageCapacity;
715 return page_count_for_slots(
slotCnt);
720 if constexpr (FixedPageTable)
723 return (size_type)m_pages.size();
726 GAIA_NODISCARD page_type*& page_slot(size_type pageIdx)
noexcept {
727 if constexpr (FixedPageTable) {
728 GAIA_ASSERT(pageIdx < MaxPages);
729 return m_staticPages[pageIdx];
731 GAIA_ASSERT(pageIdx < (size_type)m_pages.size());
732 return m_pages[pageIdx];
736 GAIA_NODISCARD
const page_type* page_slot(size_type pageIdx)
const noexcept {
737 if constexpr (FixedPageTable) {
738 GAIA_ASSERT(pageIdx < MaxPages);
739 return m_staticPages[pageIdx];
741 GAIA_ASSERT(pageIdx < (size_type)m_pages.size());
742 return m_pages[pageIdx];
747 const auto pageCnt = page_table_size();
749 auto*& pPage = page_slot(i);
750 if (pPage ==
nullptr)
756 if constexpr (!FixedPageTable)
760 void ensure_page_count(size_type slotCnt) {
761 const auto pageCnt = page_count_for_slots(slotCnt);
762 if constexpr (FixedPageTable) {
763 GAIA_ASSERT(pageCnt <= MaxPages &&
"Trying to allocate too many paged_ilist pages!");
765 if (pageCnt > (size_type)m_pages.size())
766 m_pages.resize(pageCnt,
nullptr);
770 GAIA_NODISCARD page_type& ensure_page(size_type index) {
771 ensure_page_count(index + 1);
772 auto*& pPage = page_slot(page_index(index));
773 if (pPage ==
nullptr)
774 pPage =
new page_type();
778 GAIA_NODISCARD page_type* try_page(size_type index)
noexcept {
779 const auto pageIdx = page_index(index);
780 if (pageIdx >= page_table_size())
782 return page_slot(pageIdx);
785 GAIA_NODISCARD
const page_type* try_page(size_type index)
const noexcept {
786 const auto pageIdx = page_index(index);
787 if (pageIdx >= page_table_size())
789 return page_slot(pageIdx);
792 GAIA_NODISCARD reference slot_ref(size_type index) {
793 auto* pPage = try_page(index);
794 GAIA_ASSERT(pPage !=
nullptr);
795 return *pPage->ptr(slot_index(index));
798 GAIA_NODISCARD const_reference slot_ref(size_type index)
const {
799 auto* pPage = try_page(index);
800 GAIA_ASSERT(pPage !=
nullptr);
801 return *pPage->ptr(slot_index(index));
805 using iterator = paged_ilist_iterator<paged_ilist, false>;
806 using const_iterator = paged_ilist_iterator<paged_ilist, true>;
812 paged_ilist() =
default;
813 paged_ilist(
const paged_ilist&) =
delete;
814 paged_ilist& operator=(
const paged_ilist&) =
delete;
815 paged_ilist(paged_ilist&& other)
noexcept:
817 if constexpr (FixedPageTable) {
819 m_staticPages[i] = other.m_staticPages[i];
820 other.m_staticPages[i] =
nullptr;
823 m_pages = GAIA_MOV(other.m_pages);
827 other.m_nextFreeIdx = (size_type)-1;
828 other.m_freeItems = 0;
830 paged_ilist& operator=(paged_ilist&& other)
noexcept {
831 GAIA_ASSERT(core::addressof(other) !=
this);
834 if constexpr (FixedPageTable) {
836 m_staticPages[i] = other.m_staticPages[i];
837 other.m_staticPages[i] =
nullptr;
840 m_pages = GAIA_MOV(other.m_pages);
843 m_size = other.m_size;
848 other.m_nextFreeIdx = (size_type)-1;
849 other.m_freeItems = 0;
853 GAIA_NODISCARD pointer data() noexcept {
857 GAIA_NODISCARD const_pointer data() const noexcept {
861 GAIA_NODISCARD
bool has(size_type index)
const noexcept {
865 const auto* pPage = try_page(index);
866 return pPage !=
nullptr && pPage->aliveMask.test(slot_index(index));
869 GAIA_NODISCARD
bool has(TItemHandle handle)
const noexcept {
870 return has(handle.id()) && this->handle(handle.id()) == handle;
873 GAIA_NODISCARD TItemHandle handle(size_type index)
const noexcept {
874 GAIA_ASSERT(index < m_size);
875 const auto* pPage = try_page(index);
876 GAIA_ASSERT(pPage !=
nullptr);
877 return pPage->handles[slot_index(index)];
880 GAIA_NODISCARD uint32_t generation(size_type index)
const noexcept {
881 return handle(index).gen();
884 GAIA_NODISCARD uint32_t next_free(size_type index)
const noexcept {
885 GAIA_ASSERT(index < m_size);
886 const auto* pPage = try_page(index);
887 GAIA_ASSERT(pPage !=
nullptr);
888 return pPage->nextFree[slot_index(index)];
891 GAIA_NODISCARD reference operator[](size_type index) {
892 GAIA_ASSERT(has(index));
893 return slot_ref(index);
896 GAIA_NODISCARD const_reference operator[](size_type index)
const {
897 GAIA_ASSERT(has(index));
898 return slot_ref(index);
908 GAIA_NODISCARD size_type get_next_free_item() const noexcept {
912 GAIA_NODISCARD size_type get_free_items() const noexcept {
916 GAIA_NODISCARD size_type item_count() const noexcept {
920 GAIA_NODISCARD size_type size() const noexcept {
924 GAIA_NODISCARD
bool empty() const noexcept {
928 GAIA_NODISCARD size_type capacity() const noexcept {
929 if constexpr (FixedPageTable)
930 return MaxPages * PageCapacity;
932 return (size_type)m_pages.capacity() * PageCapacity;
944 GAIA_NODISCARD const_iterator cbegin() const noexcept {
945 return const_iterator(
this, 0);
948 GAIA_NODISCARD iterator end() noexcept {
949 return iterator(
this, m_size);
952 GAIA_NODISCARD const_iterator end() const noexcept {
953 return const_iterator(
this, m_size);
956 GAIA_NODISCARD const_iterator cend() const noexcept {
957 return const_iterator(
this, m_size);
965 const auto pageCnt = page_count_for_slots(
cap);
966 if constexpr (FixedPageTable) {
979 const auto pageCnt = page_count_for_slots(
cap);
980 if constexpr (FixedPageTable) {
983 ensure_page_count(
cap);
993 auto*
pPage = try_page(index);
994 GAIA_ASSERT(
pPage !=
nullptr);
995 const auto slot = slot_index(index);
996 GAIA_ASSERT(
pPage->aliveMask.test(
slot));
1006 const auto*
pPage = try_page(index);
1007 GAIA_ASSERT(
pPage !=
nullptr);
1008 const auto slot = slot_index(index);
1009 GAIA_ASSERT(
pPage->aliveMask.test(
slot));
1013 GAIA_NODISCARD pointer try_get(size_type index)
noexcept {
1016 return &slot_ref(index);
1019 GAIA_NODISCARD const_pointer try_get(size_type index)
const noexcept {
1022 return &slot_ref(index);
1031 auto&
page = ensure_page(index);
1032 const auto slot = slot_index(index);
1033 const bool existed = index < m_size;
1037 if (index >= m_size)
1044 page.construct(
slot, GAIA_MOV(item));
1046 page.nextFree[
slot] = TItemHandle::IdMask;
1053 const auto index = (size_type)handle.id();
1054 auto&
page = ensure_page(index);
1055 const auto slot = slot_index(index);
1056 const bool existed = index < m_size;
1060 if (index >= m_size)
1086 size_type index = 0;
1091 GAIA_ASSERT(index < TItemHandle::IdMask &&
"Trying to allocate too many items!");
1094 GAIA_ASSERT(
m_nextFreeIdx < m_size &&
"Item recycle list broken!");
1096 auto&
page = ensure_page(index);
1097 const auto slot = slot_index(index);
1099 page.nextFree[
slot] = TItemHandle::IdMask;
1100 generation =
page.handles[
slot].gen();
1106 auto&
page = ensure_page(index);
1107 const auto slot = slot_index(index);
1108 page.construct(
slot, TListItem::create(index, generation, ctx));
1110 page.nextFree[
slot] = TItemHandle::IdMask;
1118 size_type index = 0;
1123 GAIA_ASSERT(index < TItemHandle::IdMask &&
"Trying to allocate too many items!");
1126 GAIA_ASSERT(
m_nextFreeIdx < m_size &&
"Item recycle list broken!");
1128 auto&
page = ensure_page(index);
1129 const auto slot = slot_index(index);
1131 page.nextFree[
slot] = TItemHandle::IdMask;
1132 generation =
page.handles[
slot].gen();
1138 auto&
page = ensure_page(index);
1139 const auto slot = slot_index(index);
1142 page.nextFree[
slot] = TItemHandle::IdMask;
1150 GAIA_ASSERT(has(handle));
1152 auto&
page = ensure_page(handle.id());
1153 const auto slot = slot_index(handle.id());
1170 GAIA_ASSERT(has(handle));
1172 auto&
page = ensure_page(handle.id());
1173 const auto slot = slot_index(handle.id());
1188 GAIA_ASSERT(
nextFreeItem < m_size &&
"Item recycle list broken!");
Forward iterator used by paged_ilist. Kept outside the container template so the container body stays...
Definition ilist.h:437
Paged implicit list declaration.
Definition ilist.h:502
static GAIA_NODISCARD constexpr size_type page_capacity() noexcept
Returns the compile-time number of payload slots stored in one page.
Definition ilist.h:707
GAIA_NODISCARD reference live_unsafe(size_type index)
Returns a live payload slot without consulting list-wide size metadata.
Definition ilist.h:992
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:714
void add_free(TItemHandle handle, uint32_t nextFreeIdx)
Restores a free slot with a preassigned id/generation and free-list link.
Definition ilist.h:1052
size_type m_freeItems
Number of slots currently linked through the implicit free-list.
Definition ilist.h:689
GAIA_NODISCARD TItemHandle alloc()
Allocates a new item in the list.
Definition ilist.h:1117
void validate() const
Verifies that the implicit free-list links are well formed.
Definition ilist.h:1181
size_type m_nextFreeIdx
Head of the implicit free-list, or TItemHandle::IdMask when no slots are free.
Definition ilist.h:687
GAIA_NODISCARD TItemHandle alloc(void *ctx)
Allocates a new item in the list.
Definition ilist.h:1085
GAIA_NODISCARD const_reference live_unsafe(size_type index) const
Returns a live payload slot without consulting list-wide size metadata.
Definition ilist.h:1005
GAIA_NODISCARD iterator begin() noexcept
Returns an iterator over live payload objects only.
Definition ilist.h:936
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:1077
void reserve(size_type cap)
Reserves page-table capacity for at least cap slots.
Definition ilist.h:964
void add_live(TListItem &&item)
Restores a live slot with a preassigned id/generation.
Definition ilist.h:1029
void free(TItemHandle handle)
Frees a live item and destroys its payload immediately.
Definition ilist.h:1149
void free_keep_live(TItemHandle handle)
Frees a handle while keeping the payload alive until slot reuse or clear().
Definition ilist.h:1169
void reserve_slot_table(size_type cap)
Ensures the page pointer table can address cap slots without resizing later.
Definition ilist.h:978