476 using value_type = TListItem;
477 using reference = TListItem&;
478 using const_reference =
const TListItem&;
479 using pointer = TListItem*;
480 using const_pointer =
const TListItem*;
481 using difference_type = std::ptrdiff_t;
482 using size_type = uint32_t;
493 "paged_ilist item type must expose static create(index, generation, ctx)");
496 "paged_ilist item type must expose static handle(const item) returning the handle type");
502 static constexpr size_type target_payload_bytes()
noexcept {
506 static constexpr size_type min_page_capacity()
noexcept {
510 static constexpr size_type max_page_capacity()
noexcept {
514 static constexpr size_type calc_page_capacity()
noexcept {
515 const size_type payloadItemBytes =
sizeof(TListItem) == 0 ? 1U : (size_type)
sizeof(TListItem);
516 const size_type desired = target_payload_bytes() / payloadItemBytes;
517 if (desired < min_page_capacity())
518 return min_page_capacity();
519 if (desired > max_page_capacity())
520 return max_page_capacity();
524 static constexpr size_type PageCapacity = calc_page_capacity();
530 alive_mask_type aliveMask;
531 TItemHandle handles[PageCapacity]{};
532 uint32_t nextFree[PageCapacity];
533 uint32_t liveCount = 0;
534 storage_type* pStorage =
nullptr;
537 GAIA_FOR(PageCapacity)
538 nextFree[i] = TItemHandle::IdMask;
545 page_type(
const page_type&) =
delete;
546 page_type& operator=(
const page_type&) =
delete;
548 page_type(page_type&& other)
noexcept:
549 aliveMask(other.aliveMask), liveCount(other.liveCount), pStorage(other.pStorage) {
550 GAIA_FOR(PageCapacity) {
551 handles[i] = other.handles[i];
552 nextFree[i] = other.nextFree[i];
555 other.aliveMask.reset();
557 other.pStorage =
nullptr;
560 page_type& operator=(page_type&& other)
noexcept {
561 GAIA_ASSERT(core::addressof(other) !=
this);
564 aliveMask = other.aliveMask;
565 liveCount = other.liveCount;
566 pStorage = other.pStorage;
567 GAIA_FOR(PageCapacity) {
568 handles[i] = other.handles[i];
569 nextFree[i] = other.nextFree[i];
572 other.aliveMask.reset();
574 other.pStorage =
nullptr;
578 GAIA_NODISCARD pointer data()
noexcept {
579 return pStorage ==
nullptr ? nullptr :
reinterpret_cast<pointer
>((uint8_t*)*pStorage);
582 GAIA_NODISCARD const_pointer data()
const noexcept {
583 return pStorage ==
nullptr ? nullptr :
reinterpret_cast<const_pointer
>((
const uint8_t*)*pStorage);
586 GAIA_NODISCARD pointer ptr(size_type slot)
noexcept {
587 GAIA_ASSERT(slot < PageCapacity);
588 GAIA_ASSERT(pStorage !=
nullptr);
589 return data() + slot;
592 GAIA_NODISCARD const_pointer ptr(size_type slot)
const noexcept {
593 GAIA_ASSERT(slot < PageCapacity);
594 GAIA_ASSERT(pStorage !=
nullptr);
595 return data() + slot;
598 void ensure_storage() {
599 if GAIA_LIKELY (pStorage !=
nullptr)
602 constexpr auto StorageAlignment =
603 alignof(storage_type) <
sizeof(
void*) ?
sizeof(
void*) :
alignof(storage_type);
604 pStorage = mem::AllocHelper::alloc_alig<storage_type>(
"PagedIListPage", StorageAlignment);
607 void maybe_release_storage() {
608 if (liveCount != 0 || pStorage ==
nullptr)
611 mem::AllocHelper::free_alig(
"PagedIListPage", pStorage);
616 if (pStorage !=
nullptr) {
617 for (
auto slot: aliveMask)
618 core::call_dtor(ptr(slot));
619 mem::AllocHelper::free_alig(
"PagedIListPage", pStorage);
625 GAIA_FOR(PageCapacity)
626 nextFree[i] = TItemHandle::IdMask;
629 template <
typename... Args>
630 void construct(size_type slot, Args&&... args) {
631 GAIA_ASSERT(slot < PageCapacity);
633 core::call_ctor(ptr(slot), GAIA_FWD(args)...);
638 void destroy(size_type slot) {
639 GAIA_ASSERT(slot < PageCapacity);
640 GAIA_ASSERT(aliveMask.test(slot));
641 core::call_dtor(ptr(slot));
642 aliveMask.reset(slot);
643 GAIA_ASSERT(liveCount > 0);
645 maybe_release_storage();
650 size_type m_size = 0;
653 size_type m_nextFreeIdx = (size_type)-1;
654 size_type m_freeItems = 0;
657 GAIA_NODISCARD
static constexpr size_type page_index(size_type index)
noexcept {
658 return index / PageCapacity;
661 GAIA_NODISCARD
static constexpr size_type slot_index(size_type index)
noexcept {
662 return index % PageCapacity;
665 GAIA_NODISCARD
static constexpr size_type page_count_for_slots(size_type slotCnt)
noexcept {
666 return slotCnt == 0 ? 0U : (slotCnt + PageCapacity - 1) / PageCapacity;
670 for (
auto* pPage: m_pages) {
671 if (pPage ==
nullptr)
678 void ensure_page_count(size_type slotCnt) {
679 const auto pageCnt = page_count_for_slots(slotCnt);
680 if (pageCnt > (size_type)m_pages.size())
681 m_pages.resize(pageCnt,
nullptr);
684 GAIA_NODISCARD page_type& ensure_page(size_type index) {
685 ensure_page_count(index + 1);
686 auto*& pPage = m_pages[page_index(index)];
687 if (pPage ==
nullptr)
688 pPage =
new page_type();
692 GAIA_NODISCARD page_type* try_page(size_type index)
noexcept {
693 const auto pageIdx = page_index(index);
694 if (pageIdx >= (size_type)m_pages.size())
696 return m_pages[pageIdx];
699 GAIA_NODISCARD
const page_type* try_page(size_type index)
const noexcept {
700 const auto pageIdx = page_index(index);
701 if (pageIdx >= (size_type)m_pages.size())
703 return m_pages[pageIdx];
706 GAIA_NODISCARD reference slot_ref(size_type index) {
707 auto* pPage = try_page(index);
708 GAIA_ASSERT(pPage !=
nullptr);
709 return *pPage->ptr(slot_index(index));
712 GAIA_NODISCARD const_reference slot_ref(size_type index)
const {
713 auto* pPage = try_page(index);
714 GAIA_ASSERT(pPage !=
nullptr);
715 return *pPage->ptr(slot_index(index));
730 m_pages(GAIA_MOV(other.m_pages)), m_size(other.m_size), m_nextFreeIdx(other.m_nextFreeIdx),
731 m_freeItems(other.m_freeItems) {
733 other.m_nextFreeIdx = (size_type)-1;
734 other.m_freeItems = 0;
737 GAIA_ASSERT(core::addressof(other) !=
this);
739 m_pages = GAIA_MOV(other.m_pages);
740 m_size = other.m_size;
741 m_nextFreeIdx = other.m_nextFreeIdx;
742 m_freeItems = other.m_freeItems;
745 other.m_nextFreeIdx = (size_type)-1;
746 other.m_freeItems = 0;
750 GAIA_NODISCARD pointer data()
noexcept {
754 GAIA_NODISCARD const_pointer data()
const noexcept {
758 GAIA_NODISCARD
bool has(size_type index)
const noexcept {
762 const auto* pPage = try_page(index);
763 return pPage !=
nullptr && pPage->aliveMask.test(slot_index(index));
766 GAIA_NODISCARD
bool has(TItemHandle handle)
const noexcept {
767 return has(handle.id()) && this->handle(handle.id()) == handle;
770 GAIA_NODISCARD TItemHandle handle(size_type index)
const noexcept {
771 GAIA_ASSERT(index < m_size);
772 const auto* pPage = try_page(index);
773 GAIA_ASSERT(pPage !=
nullptr);
774 return pPage->handles[slot_index(index)];
777 GAIA_NODISCARD uint32_t generation(size_type index)
const noexcept {
778 return handle(index).gen();
781 GAIA_NODISCARD uint32_t next_free(size_type index)
const noexcept {
782 GAIA_ASSERT(index < m_size);
783 const auto* pPage = try_page(index);
784 GAIA_ASSERT(pPage !=
nullptr);
785 return pPage->nextFree[slot_index(index)];
788 GAIA_NODISCARD reference operator[](size_type index) {
789 GAIA_ASSERT(has(index));
790 return slot_ref(index);
793 GAIA_NODISCARD const_reference operator[](size_type index)
const {
794 GAIA_ASSERT(has(index));
795 return slot_ref(index);
801 m_nextFreeIdx = (size_type)-1;
805 GAIA_NODISCARD size_type get_next_free_item()
const noexcept {
806 return m_nextFreeIdx;
809 GAIA_NODISCARD size_type get_free_items()
const noexcept {
813 GAIA_NODISCARD size_type item_count()
const noexcept {
814 return m_size - m_freeItems;
817 GAIA_NODISCARD size_type size()
const noexcept {
821 GAIA_NODISCARD
bool empty()
const noexcept {
825 GAIA_NODISCARD size_type capacity()
const noexcept {
826 return (size_type)m_pages.capacity() * PageCapacity;
838 GAIA_NODISCARD const_iterator cbegin() const noexcept {
839 return const_iterator(
this, 0);
842 GAIA_NODISCARD iterator end() noexcept {
843 return iterator(
this, m_size);
846 GAIA_NODISCARD const_iterator end() const noexcept {
847 return const_iterator(
this, m_size);
850 GAIA_NODISCARD const_iterator cend() const noexcept {
851 return const_iterator(
this, m_size);
854 void reserve(size_type cap) {
855 m_pages.reserve(page_count_for_slots(cap));
858 GAIA_NODISCARD pointer try_get(size_type index)
noexcept {
861 return &slot_ref(index);
864 GAIA_NODISCARD const_pointer try_get(size_type index)
const noexcept {
867 return &slot_ref(index);
873 auto& page = ensure_page(index);
874 const auto slot = slot_index(index);
875 const bool existed = index < m_size;
876 const bool wasAlive = existed && page.aliveMask.test(slot);
877 const bool wasFree = existed && !wasAlive;
883 else if (wasFree && m_freeItems > 0)
886 page.construct(slot, GAIA_MOV(item));
887 page.handles[slot] = TListItem::handle(*page.ptr(slot));
888 page.nextFree[slot] = TItemHandle::IdMask;
892 void add_free(TItemHandle handle, uint32_t nextFreeIdx) {
893 const auto index = (size_type)handle.id();
894 auto& page = ensure_page(index);
895 const auto slot = slot_index(index);
896 const bool existed = index < m_size;
897 const bool wasAlive = existed && page.aliveMask.test(slot);
898 const bool wasFree = existed && !wasAlive;
905 page.handles[slot] = handle;
906 page.nextFree[slot] = nextFreeIdx;
909 if (m_nextFreeIdx == (size_type)-1)
910 m_nextFreeIdx = index;
914 void add_free(size_type index, uint32_t generation, uint32_t nextFreeIdx) {
919 GAIA_NODISCARD TItemHandle
alloc(
void* ctx) {
921 uint32_t generation = 0;
923 if GAIA_UNLIKELY (m_freeItems == 0U) {
925 GAIA_ASSERT(index < TItemHandle::IdMask &&
"Trying to allocate too many items!");
928 GAIA_ASSERT(m_nextFreeIdx < m_size &&
"Item recycle list broken!");
929 index = m_nextFreeIdx;
930 auto& page = ensure_page(index);
931 const auto slot = slot_index(index);
932 m_nextFreeIdx = page.nextFree[slot];
933 page.nextFree[slot] = TItemHandle::IdMask;
934 generation = page.handles[slot].gen();
938 auto& page = ensure_page(index);
939 const auto slot = slot_index(index);
940 page.construct(slot, TListItem::create(index, generation, ctx));
941 page.handles[slot] = TListItem::handle(*page.ptr(slot));
942 page.nextFree[slot] = TItemHandle::IdMask;
943 return page.handles[slot];
947 GAIA_NODISCARD TItemHandle
alloc() {
949 uint32_t generation = 0;
951 if GAIA_UNLIKELY (m_freeItems == 0U) {
953 GAIA_ASSERT(index < TItemHandle::IdMask &&
"Trying to allocate too many items!");
956 GAIA_ASSERT(m_nextFreeIdx < m_size &&
"Item recycle list broken!");
957 index = m_nextFreeIdx;
958 auto& page = ensure_page(index);
959 const auto slot = slot_index(index);
960 m_nextFreeIdx = page.nextFree[slot];
961 page.nextFree[slot] = TItemHandle::IdMask;
962 generation = page.handles[slot].gen();
966 auto& page = ensure_page(index);
967 const auto slot = slot_index(index);
968 page.construct(slot, TListItem(index, generation));
970 page.nextFree[slot] = TItemHandle::IdMask;
971 return page.handles[slot];
974 void free(TItemHandle handle) {
975 GAIA_ASSERT(has(handle));
977 auto& page = ensure_page(handle.id());
978 const auto slot = slot_index(handle.id());
981 page.nextFree[slot] = m_freeItems == 0 ? TItemHandle::IdMask : m_nextFreeIdx;
982 m_nextFreeIdx = handle.id();
986 void validate()
const {
987 if (m_freeItems == 0)
990 auto freeItems = m_freeItems;
991 auto nextFreeItem = m_nextFreeIdx;
992 while (freeItems > 0) {
993 GAIA_ASSERT(nextFreeItem < m_size &&
"Item recycle list broken!");
994 GAIA_ASSERT(!has(nextFreeItem));
996 nextFreeItem = next_free(nextFreeItem);
1000 GAIA_ASSERT(nextFreeItem == TItemHandle::IdMask);
void add_free(size_type index, uint32_t generation, uint32_t nextFreeIdx)
Restores a free slot with a preassigned id/generation and freelist link.
Definition ilist.h:914
void add_free(TItemHandle handle, uint32_t nextFreeIdx)
Restores a free slot with a preassigned id/generation and freelist link.
Definition ilist.h:892