8#include <Common/assertion.h>
9#include <Common/memory.h>
11#include <Common/math_basics.h>
12#include <Common/exceptions.h>
13#include <Common/config.h>
48template<
typename Item, CHandleDispatcher Dispatcher = THandleDispatcher<TWeakHandle<Item>>>
52 static_assert(std::is_default_constructible_v<Item>,
53 "Item must be default constructible.");
56 static_assert(std::is_move_constructible_v<Item>,
57 "Item must be move constructible.");
60 static_assert(std::is_trivially_destructible_v<Item>,
61 "Item must be trivially destructible.");
69 using Index =
typename HandleType::IndexType;
70 using Generation =
typename HandleType::GenerationType;
71 using NonConstItem = std::remove_const_t<Item>;
82 requires std::is_copy_constructible_v<Item> &&
83 std::is_copy_constructible_v<Dispatcher>
87 , m_dispatcher(other.m_dispatcher)
88 , m_numItems(other.m_numItems)
90 grow(other.capacity());
93 for(Index i = 0; i < other.capacity(); ++i)
96 getItemPtrDirectly(m_storageMemory, i),
97 *getItemPtr(other.m_storageMemory, i));
101 m_generations = other.m_generations;
154 constexpr auto initialGeneration = HandleType::nextGeneration(HandleType::INVALID_GENERATION);
156 const bool isEmpty = handle.isEmpty();
157 const bool isInvalidOutOfBound = handle.getIndex() >=
capacity() && handle.getGeneration() != initialGeneration;
158 const bool isStale = handle.getIndex() <
capacity() && !
isFresh(handle);
159 if(
isEmpty || isInvalidOutOfBound || isStale)
161 throw_formatted<IllegalOperationException>(
162 "creating trivial item with bad handle {}", handle);
166 const Index itemIdx = handle.getIndex();
171 throw_formatted<OverflowException>(
172 "Storage size will exceed the maximum amount Index type can hold (max={})",
177 const Index newCapacity = std::max(nextCapacity(
capacity()), itemIdx + 1);
182 PH_ASSERT_LT(itemIdx, m_generations.size());
188 std::construct_at(getItemPtrDirectly(m_storageMemory, itemIdx), std::move(item));
205 throw_formatted<IllegalOperationException>(
206 "removing trivial item with stale handle {}", handle);
209 const Index itemIdx = handle.getIndex();
213 static_assert(std::is_trivially_destructible_v<Item>);
216 std::construct_at(getItemPtr(m_storageMemory, itemIdx));
218 const Generation newGeneration = HandleType::nextGeneration(handle.getGeneration());
219 m_generations[itemIdx] = newGeneration;
221 return HandleType(handle.getIndex(), newGeneration);
232 return m_dispatcher.dispatchOne();
242 m_dispatcher.returnOne(handle);
253 return isFresh(handle) ? getItemPtr(m_storageMemory, handle.getIndex()) :
nullptr;
264 return isFresh(handle) ? getItemPtr(m_storageMemory, handle.getIndex()) :
nullptr;
269 PH_ASSERT_LE(m_numItems,
capacity());
275 PH_ASSERT_LE(m_numItems,
capacity());
282 return static_cast<Index
>(m_generations.size());
299 return handle.getIndex() < m_generations.size() &&
300 handle.getGeneration() == m_generations[handle.getIndex()];
310 PH_ASSERT_LT(index, m_generations.size());
311 return *getItemPtr(m_storageMemory, index);
316 PH_ASSERT_LT(index, m_generations.size());
317 return *getItemPtr(m_storageMemory, index);
323 return std::numeric_limits<Index>::max();
331 swap(first.m_storageMemory, second.m_storageMemory);
332 swap(first.m_generations, second.m_generations);
333 swap(first.m_dispatcher, second.m_dispatcher);
334 swap(first.m_numItems, second.m_numItems);
338 void grow(
const Index newCapacity)
340 const Index oldCapacity =
capacity();
341 PH_ASSERT_GT(newCapacity, oldCapacity);
343 const auto requiredMemorySize = newCapacity *
sizeof(NonConstItem);
344 const auto alignmentSize = std::lcm(
alignof(NonConstItem), os::get_L1_cache_line_size_in_bytes());
345 const auto totalMemorySize = math::next_multiple(requiredMemorySize, alignmentSize);
349 TAlignedMemoryUniquePtr<NonConstItem> newStorageMemory =
350 make_aligned_memory<NonConstItem>(totalMemorySize, alignmentSize);
351 if(!newStorageMemory)
353 throw std::bad_alloc{};
359 for(Index i = 0; i < oldCapacity; ++i)
362 getItemPtrDirectly(newStorageMemory, i),
363 std::move(*getItemPtr(m_storageMemory, i)));
370 std::uninitialized_default_construct_n(
371 getItemPtrDirectly(newStorageMemory, oldCapacity),
372 newCapacity - oldCapacity);
375 constexpr auto initialGeneration = HandleType::nextGeneration(HandleType::INVALID_GENERATION);
376 m_generations.resize(newCapacity, initialGeneration);
379 m_storageMemory = std::move(newStorageMemory);
386 static auto getItemPtr(
387 const TAlignedMemoryUniquePtr<NonConstItem>& storage,
388 const std::size_t index)
394 if constexpr( PH_STRICT_OBJECT_LIFETIME)
397 return std::launder(getItemPtrDirectly(storage, index));
403 return getItemPtrDirectly(storage, index);
407 static auto getItemPtrDirectly(
408 const TAlignedMemoryUniquePtr<NonConstItem>& storage,
409 const std::size_t index)
412 return storage.get() + index;
415 static constexpr Index nextCapacity(
const Index currentCapacity)
418 const Index oldCapacity = currentCapacity;
419 const Index additionalCapacity = oldCapacity / 2 + 1;
420 const Index newCapacity = (
maxCapacity() - oldCapacity >= additionalCapacity)
421 ? oldCapacity + additionalCapacity :
maxCapacity();
430 TAlignedMemoryUniquePtr<NonConstItem> m_storageMemory;
432 std::vector<Generation> m_generations;
433 Dispatcher m_dispatcher;
Definition TItemPoolInterface.h:10
Item pool for simple types. This item pool is designed to minimize execution time and memory footprin...
Definition TTrivialItemPool.h:50
TTrivialItemPool(TTrivialItemPool &&other) noexcept
Definition TTrivialItemPool.h:104
friend void swap(TTrivialItemPool &first, TTrivialItemPool &second) noexcept
Definition TTrivialItemPool.h:326
bool isEmpty() const
Definition TTrivialItemPool.h:288
void remove(const HandleType &handle)
Remove the item at the storage slot indicated by handle. Complexity: O(1).
Definition TTrivialItemPool.h:143
Index numItems() const
Definition TTrivialItemPool.h:267
HandleType dispatchOneHandle()
Definition TTrivialItemPool.h:228
static constexpr Index maxCapacity()
Definition TTrivialItemPool.h:321
HandleType add(Item item)
Definition TTrivialItemPool.h:135
Item & operator[](const std::size_t index)
Access item by index.
Definition TTrivialItemPool.h:308
Item * get(const HandleType &handle)
Get item by handle. Note that accessing items before construction is also allowed....
Definition TTrivialItemPool.h:251
TTrivialItemPool()
Definition TTrivialItemPool.h:74
TTrivialItemPool & operator=(TTrivialItemPool rhs) noexcept
Definition TTrivialItemPool.h:110
TTrivialItemPool(const TTrivialItemPool &other)
Definition TTrivialItemPool.h:81
void returnOneHandle(const HandleType &handle)
Definition TTrivialItemPool.h:238
Item * accessItem(const HandleType &handle) override
Definition TTrivialItemPool.h:119
HandleType removeAt(const HandleType &handle)
Remove the item at the storage slot indicated by handle. Manual handle management API....
Definition TTrivialItemPool.h:201
Index capacity() const
Definition TTrivialItemPool.h:279
HandleType createAt(const HandleType &handle, Item item)
Place item at the storage slot indicated by handle. Manual handle management API. Complexity: Amortiz...
Definition TTrivialItemPool.h:152
~TTrivialItemPool() override=default
Index numFreeSpace() const
Definition TTrivialItemPool.h:273
bool isFresh(const HandleType &handle) const
Definition TTrivialItemPool.h:297
const Item * get(const HandleType &handle) const
Get item by handle. Note that accessing items before construction is also allowed....
Definition TTrivialItemPool.h:262
typename Dispatcher::HandleType HandleType
Definition TTrivialItemPool.h:66
const Item * viewItem(const HandleType &handle) const override
Definition TTrivialItemPool.h:124
Definition ph_editor.h:10