36 static_assert(std::is_nothrow_destructible_v<T>,
37 "ObjectPool requires nothrow destructible types");
44 : capacity_(0), size_(0), free_list_(nullptr) {
45 if (initial_capacity > 0) {
46 reserve(initial_capacity);
53 for (Block& block : blocks_) {
54 ::operator
delete(block.memory, std::align_val_t{
alignof(T)});
64 : blocks_(std::move(other.blocks_)),
65 capacity_(other.capacity_),
67 free_list_(other.free_list_) {
70 other.free_list_ =
nullptr;
76 for (Block& block : blocks_) {
77 ::operator
delete(block.memory, std::align_val_t{
alignof(T)});
79 blocks_ = std::move(other.blocks_);
80 capacity_ = other.capacity_;
82 free_list_ = other.free_list_;
85 other.free_list_ =
nullptr;
95 if (free_list_ !=
nullptr) {
96 FreeNode* node = free_list_;
97 free_list_ = node->next;
99 return reinterpret_cast<T*
>(node);
108 if (free_list_ !=
nullptr) {
109 FreeNode* node = free_list_;
110 free_list_ = node->next;
112 return reinterpret_cast<T*
>(node);
121 template<
typename... Args>
124 if (ptr !=
nullptr) {
126 new (ptr) T(std::forward<Args>(args)...);
138 if (ptr ==
nullptr) {
143 FreeNode* node =
reinterpret_cast<FreeNode*
>(ptr);
144 node->next = free_list_;
152 if (ptr !=
nullptr) {
162 if (n <= capacity_) {
166 std::size_t to_allocate = n - capacity_;
167 return grow(to_allocate);
171 [[nodiscard]] std::size_t
capacity() const noexcept {
176 [[nodiscard]] std::size_t
size() const noexcept {
182 return capacity_ - size_;
186 [[nodiscard]]
bool empty() const noexcept {
194 free_list_ =
nullptr;
195 for (Block& block : blocks_) {
196 for (std::size_t i = 0; i < block.count; ++i) {
197 T* obj =
reinterpret_cast<T*
>(
198 static_cast<char*
>(block.memory) + i *
sizeof(T));
199 FreeNode* node =
reinterpret_cast<FreeNode*
>(obj);
200 node->next = free_list_;
213 static_assert(
sizeof(FreeNode) <=
sizeof(T),
214 "ObjectPool requires sizeof(T) >= sizeof(void*)");
225 bool grow(std::size_t min_count = 0) {
228 constexpr std::size_t kMinBlockSize = 64;
229 constexpr std::size_t kMaxBlockSize = 8192;
231 std::size_t block_size = kMinBlockSize;
232 if (!blocks_.empty()) {
233 block_size = std::min(blocks_.back().count * 2, kMaxBlockSize);
237 if (block_size < min_count) {
238 block_size = min_count;
242 void* memory = ::operator
new(block_size *
sizeof(T),
243 std::align_val_t{
alignof(T)},
245 if (memory ==
nullptr) {
250 blocks_.push_back({memory, block_size});
251 capacity_ += block_size;
254 for (std::size_t i = 0; i < block_size; ++i) {
255 T* obj =
reinterpret_cast<T*
>(
256 static_cast<char*
>(memory) + i *
sizeof(T));
257 FreeNode* node =
reinterpret_cast<FreeNode*
>(obj);
258 node->next = free_list_;
265 std::vector<Block> blocks_;
266 std::size_t capacity_;
268 FreeNode* free_list_;
Object pool for zero-allocation management of frequently created/destroyed objects.
void deallocate(T *ptr) noexcept
Deallocate an object (does not call destructor)
ObjectPool & operator=(const ObjectPool &)=delete
ObjectPool(const ObjectPool &)=delete
bool empty() const noexcept
Check if pool is empty.
T * construct(Args &&... args)
Construct an object in-place with arguments.
ObjectPool & operator=(ObjectPool &&other) noexcept
T * allocate()
Allocate an object from the pool Returns a pointer to uninitialized memory.
bool reserve(std::size_t n)
Reserve capacity for at least n objects.
~ObjectPool() noexcept
Destructor - frees all memory blocks.
void destroy(T *ptr) noexcept
Destroy and deallocate an object.
ObjectPool(std::size_t initial_capacity=1024)
Constructor - pre-allocates initial capacity.
ObjectPool(ObjectPool &&other) noexcept
std::size_t capacity() const noexcept
Get current capacity (total objects that can be stored)
std::size_t size() const noexcept
Get current size (objects currently allocated)
void clear() noexcept
Clear all allocated objects (calls destructors if any are alive) Warning: This assumes all objects ha...
std::size_t available() const noexcept
Get number of available objects in free list.
#define SLICK_DETAIL_NAMESPACE_BEGIN
#define SLICK_DETAIL_NAMESPACE_END