8#include <Common/assertion.h>
9#include <Common/primitive_type.h>
10#include <Common/memory.h>
12#include <Common/math_basics.h>
13#include <Common/exceptions.h>
14#include <Common/config.h>
15#include <Utility/utility.h>
38 CHandleDispatcher Dispatcher = THandleDispatcher<TWeakHandle<Item>>,
39 typename ItemInterface = Item>
42 static_assert(std::is_move_constructible_v<Item>,
43 "Item must be move constructible.");
50 using Index =
typename HandleType::IndexType;
51 using Generation =
typename HandleType::GenerationType;
52 using NonConstItem = std::remove_const_t<Item>;
59 template<
typename ItemType>
requires std::is_scalar_v<Item> || CBase<ItemType, Item>
62 static_assert(std::is_same_v<typename Base::HandleType, TCompatibleHandleType<Item>>,
63 "TItemPoolInterface is not using a compatible HandleType.");
66 template<
bool IS_CONST>
70 using ItemType = std::conditional_t<IS_CONST, const Item, Item>;
71 using PoolType = std::conditional_t<IS_CONST, const TItemPool, TItemPool>;
85 , m_currentIdx(currentIdx)
92 PH_ASSERT_LT(m_currentIdx, m_pool->m_storageStates.size());
93 PH_ASSERT(!m_pool->m_storageStates[m_currentIdx].isFreed);
95 return *getItemPtr(m_pool->m_storageMemory, m_currentIdx);
104 m_currentIdx = m_pool->nextItemBeginIndex(m_currentIdx);
122 m_currentIdx = m_pool->previousItemEndIndex(m_currentIdx);
123 PH_ASSERT_GT(m_currentIdx, 0);
139 return m_currentIdx == rhs.m_currentIdx && m_pool == rhs.m_pool;
145 return !(*
this == rhs);
154 PH_ASSERT(!m_pool->m_storageStates[m_currentIdx].isFreed);
156 return m_pool->getHandleByIndex(m_currentIdx);
164 Index m_currentIdx = HandleType::INVALID_INDEX;
182 requires std::is_copy_constructible_v<Item> &&
183 std::is_copy_constructible_v<Dispatcher>
187 , m_dispatcher(other.m_dispatcher)
190 grow(other.capacity());
192 other.forEachItemStorage(
193 [
this, &other](
const Item* otherItem, Index idx)
196 m_storageStates[idx].generation = other.m_storageStates[idx].generation;
198 if(!other.m_storageStates[idx].isFreed)
200 createItemAtIndex(idx, *otherItem);
204 PH_ASSERT_EQ(m_numItems, other.m_numItems);
225 PH_ASSERT_EQ(m_numItems, 0);
250 template<
typename ItemType>
261 template<
typename ItemType>
264 constexpr auto initialGeneration = HandleType::nextGeneration(HandleType::INVALID_GENERATION);
269 if(
isEmpty || isInvalidOutOfBound || isStale)
271 throw_formatted<IllegalOperationException>(
272 "creating item with bad handle {}", handle);
276 const Index itemIdx = handle.
getIndex();
281 throw_formatted<OverflowException>(
282 "Storage size will exceed the maximum amount Index type can hold (max={})",
287 const Index newCapacity = std::max(nextCapacity(
capacity()), itemIdx + 1);
292 PH_ASSERT_LT(itemIdx, m_storageStates.size());
295 if(!m_storageStates[itemIdx].isFreed)
297 throw_formatted<IllegalOperationException>(
298 "attempting to create item at an occupied slot (handle: {})", handle);
301 createItemAtIndex(itemIdx, std::move(item));
302 return getHandleByIndex(itemIdx);
311 template<
typename ItemType>
317 throw_formatted<IllegalOperationException>(
318 "removing item with stale handle {}", handle);
321 const Index itemIdx = handle.
getIndex();
327 if(m_storageStates[itemIdx].isFreed)
329 throw_formatted<IllegalOperationException>(
330 "attempting to remove item at an emptied slot (handle: {})", handle);
333 removeItemAtIndex(itemIdx);
334 return getHandleByIndex(itemIdx);
345 return m_dispatcher.dispatchOne();
351 template<
typename ItemType>
358 m_dispatcher.returnOne(nativeHandle);
369 [
this](Item* , Index idx)
371 removeItemAtIndex(idx);
381 template<
typename ItemType>
385 ? getItemPtr(m_storageMemory, handle.
getIndex())
394 template<
typename ItemType>
398 ? getItemPtr(m_storageMemory, handle.
getIndex())
402 template<
typename ItemType>
407 using EmbeddedWeakHandle =
typename StrongHandle::WeakHandleType;
409 static_assert(std::convertible_to<
decltype(handle), EmbeddedWeakHandle>,
410 "Input handle type cannot be converted to a strong handle.");
412 return StrongHandle(EmbeddedWeakHandle(handle),
this);
417 PH_ASSERT_LE(m_numItems,
capacity());
423 PH_ASSERT_LE(m_numItems,
capacity());
429 PH_ASSERT_LE(m_storageStates.size(),
maxCapacity());
430 return static_cast<Index
>(m_storageStates.size());
450 template<
typename ItemType>
453 return handle.
getIndex() < m_storageStates.size() &&
483 return std::numeric_limits<Index>::max();
491 swap(first.m_storageMemory, second.m_storageMemory);
492 swap(first.m_storageStates, second.m_storageStates);
493 swap(first.m_dispatcher, second.m_dispatcher);
494 swap(first.m_numItems, second.m_numItems);
498 void createItemAtIndex(
const Index itemIdx, Item item)
500 PH_ASSERT_LT(itemIdx, m_storageStates.size());
501 PH_ASSERT(m_storageStates[itemIdx].isFreed);
506 std::construct_at(getItemPtrDirectly(m_storageMemory, itemIdx), std::move(item));
508 m_storageStates[itemIdx].isFreed =
false;
512 void removeItemAtIndex(
const Index itemIdx)
515 PH_ASSERT(!m_storageStates[itemIdx].isFreed);
517 std::destroy_at(getItemPtr(m_storageMemory, itemIdx));
519 m_storageStates[itemIdx].isFreed =
true;
520 m_storageStates[itemIdx].generation = HandleType::nextGeneration(m_storageStates[itemIdx].generation);
524 HandleType getHandleByIndex(
const Index itemIdx)
const
526 return HandleType(itemIdx, m_storageStates[itemIdx].generation);
529 void grow(
const Index newCapacity)
531 const Index oldCapacity =
capacity();
532 PH_ASSERT_GT(newCapacity, oldCapacity);
534 const auto requiredMemorySize = newCapacity *
sizeof(Item);
535 const auto alignmentSize = std::lcm(
alignof(Item), os::get_L1_cache_line_size_in_bytes());
536 const auto totalMemorySize = math::next_multiple(requiredMemorySize, alignmentSize);
540 TAlignedMemoryUniquePtr<NonConstItem> newStorageMemory =
541 make_aligned_memory<NonConstItem>(totalMemorySize, alignmentSize);
542 if(!newStorageMemory)
544 throw std::bad_alloc{};
548 [&newStorageMemory](Item* oldItem, Index idx)
550 std::construct_at(getItemPtrDirectly(newStorageMemory, idx), std::move(*oldItem));
551 std::destroy_at(oldItem);
555 m_storageStates.resize(newCapacity, StorageState{});
558 m_storageMemory = std::move(newStorageMemory);
561 template<
typename PerItemStorageOp>
562 void forEachItemStorage(PerItemStorageOp op)
564 for(Index itemIdx = 0; itemIdx <
capacity(); ++itemIdx)
566 Item* item = m_storageStates[itemIdx].isFreed
567 ? getItemPtrDirectly(m_storageMemory, itemIdx)
568 : getItemPtr(m_storageMemory, itemIdx);
573 template<
typename PerItemStorageOp>
574 void forEachItemStorage(PerItemStorageOp op)
const
576 for(Index itemIdx = 0; itemIdx <
capacity(); ++itemIdx)
578 const Item* item = m_storageStates[itemIdx].isFreed
579 ? getItemPtrDirectly(m_storageMemory, itemIdx)
580 : getItemPtr(m_storageMemory, itemIdx);
585 template<
typename PerItemOp>
586 void forEachItem(PerItemOp op)
589 [op,
this](Item* item, Index idx)
591 if(m_storageStates[idx].isFreed)
600 template<
typename PerItemOp>
601 void forEachItem(PerItemOp op)
const
604 [op,
this](
const Item* item, Index idx)
606 if(m_storageStates[idx].isFreed)
615 Index nextItemBeginIndex(
const Index beginIdx)
const
618 PH_ASSERT_IN_RANGE_INCLUSIVE(beginIdx, 0,
capacity());
620 Index itemBeginIdx = beginIdx;
623 if(m_storageStates[itemBeginIdx].isFreed)
635 Index previousItemEndIndex(
const Index endIdx)
const
638 PH_ASSERT_IN_RANGE_INCLUSIVE(endIdx, 0,
capacity());
640 Index itemEndIdx = endIdx;
641 while(itemEndIdx > 0)
643 if(m_storageStates[itemEndIdx - 1].isFreed)
659 static auto getItemPtr(
660 const TAlignedMemoryUniquePtr<NonConstItem>& storage,
661 const std::size_t index)
667 if constexpr( PH_STRICT_OBJECT_LIFETIME)
670 return std::launder(getItemPtrDirectly(storage, index));
676 return getItemPtrDirectly(storage, index);
680 static auto getItemPtrDirectly(
681 const TAlignedMemoryUniquePtr<NonConstItem>& storage,
682 const std::size_t index)
685 return storage.get() + index;
688 static constexpr Index nextCapacity(
const Index currentCapacity)
691 const Index oldCapacity = currentCapacity;
692 const Index additionalCapacity = oldCapacity / 2 + 1;
693 const Index newCapacity = (
maxCapacity() - oldCapacity >= additionalCapacity)
694 ? oldCapacity + additionalCapacity :
maxCapacity();
701 Generation generation = HandleType::nextGeneration(HandleType::INVALID_GENERATION);
702 uint8 isFreed : 1 =
true;
712 TAlignedMemoryUniquePtr<NonConstItem> m_storageMemory;
714 std::vector<StorageState> m_storageStates;
715 Dispatcher m_dispatcher;
Definition TItemPool.h:68
reference operator*() const
Definition TItemPool.h:89
std::bidirectional_iterator_tag iterator_category
Definition TItemPool.h:74
bool operator!=(const TIterator &rhs) const
Definition TItemPool.h:143
HandleType getHandle() const
Definition TItemPool.h:151
bool operator==(const TIterator &rhs) const
Definition TItemPool.h:137
ItemType * pointer
Definition TItemPool.h:77
ItemType value_type
Definition TItemPool.h:75
TIterator(PoolType *const pool, Index currentIdx)
Definition TItemPool.h:83
std::conditional_t< IS_CONST, const TItemPool, TItemPool > PoolType
Definition TItemPool.h:71
TIterator & operator++()
Definition TItemPool.h:99
TIterator & operator--()
Definition TItemPool.h:117
ItemType & reference
Definition TItemPool.h:78
std::ptrdiff_t difference_type
Definition TItemPool.h:76
std::conditional_t< IS_CONST, const Item, Item > ItemType
Definition TItemPool.h:70
A general item pool.
Definition TItemPool.h:41
~TItemPool() override
Definition TItemPool.h:220
friend void swap(TItemPool &first, TItemPool &second) noexcept
Definition TItemPool.h:486
Index capacity() const
Definition TItemPool.h:427
HandleType add(Item item)
Definition TItemPool.h:242
Index numFreeSpace() const
Definition TItemPool.h:421
const ItemInterface * viewItem(const HandleType &handle) const override
Definition TItemPool.h:233
ConstIteratorType cend()
Definition TItemPool.h:475
HandleType createAt(const TCompatibleHandleType< ItemType > &handle, Item item)
Place item at the storage slot indicated by handle. Manual handle management API. Will not invalidate...
Definition TItemPool.h:262
bool isEmpty() const
Definition TItemPool.h:436
IteratorType end()
Definition TItemPool.h:465
const ItemType * get(const TCompatibleHandleType< ItemType > &handle) const
Get item by handle. Complexity: O(1).
Definition TItemPool.h:395
TIterator< false > IteratorType
Definition TItemPool.h:168
void returnOneHandle(const TCompatibleHandleType< ItemType > &handle)
Definition TItemPool.h:352
ItemType * get(const TCompatibleHandleType< ItemType > &handle)
Get item by handle. Complexity: O(1).
Definition TItemPool.h:382
static constexpr Index maxCapacity()
Definition TItemPool.h:481
TItemPool()
Definition TItemPool.h:171
auto getStrong(const TCompatibleHandleType< ItemType > &handle) -> TStrongHandle< ItemInterface, Index, Generation >
Definition TItemPool.h:403
TItemPool(TItemPool &&other) noexcept
Definition TItemPool.h:207
typename Dispatcher::HandleType HandleType
Definition TItemPool.h:46
IteratorType begin()
Iterators for all created items in the pool.
Definition TItemPool.h:460
HandleType removeAt(const TCompatibleHandleType< ItemType > &handle)
Remove the item at the storage slot indicated by handle. Manual handle management API....
Definition TItemPool.h:313
bool isFresh(const TCompatibleHandleType< ItemType > &handle) const
Definition TItemPool.h:451
void clear()
Definition TItemPool.h:361
bool hasFreeSpace() const
Definition TItemPool.h:441
TItemPool(const TItemPool &other)
Copy items stored in other into this pool. Handles that were originally valid for other will also be ...
Definition TItemPool.h:181
ItemInterface * accessItem(const HandleType &handle) override
Definition TItemPool.h:228
ConstIteratorType cbegin()
Definition TItemPool.h:470
TItemPool & operator=(TItemPool rhs) noexcept
Definition TItemPool.h:213
void remove(const TCompatibleHandleType< ItemType > &handle)
Remove the item at the storage slot indicated by handle. Will not invalidate iterators....
Definition TItemPool.h:251
Index numItems() const
Definition TItemPool.h:415
HandleType dispatchOneHandle()
Definition TItemPool.h:341
Definition TItemPoolInterface.h:10
Handle with strong reference semantics. Default constructor creates empty handle.
Definition TStrongHandle.h:18
Handle with weak reference semantics. Default constructor creates empty handle.
Definition TWeakHandle.h:23
Index getIndex() const
Definition TWeakHandle.h:49
Generation getGeneration() const
Definition TWeakHandle.h:54
bool isEmpty() const
Definition TWeakHandle.h:62
Definition ph_editor.h:10