summaryrefslogtreecommitdiff
path: root/asl/handle_pool/dense_handle_pool.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'asl/handle_pool/dense_handle_pool.hpp')
-rw-r--r--asl/handle_pool/dense_handle_pool.hpp162
1 files changed, 162 insertions, 0 deletions
diff --git a/asl/handle_pool/dense_handle_pool.hpp b/asl/handle_pool/dense_handle_pool.hpp
new file mode 100644
index 0000000..34cd04f
--- /dev/null
+++ b/asl/handle_pool/dense_handle_pool.hpp
@@ -0,0 +1,162 @@
+// Copyright 2025 Steven Le Rouzic
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#pragma once
+
+#include "asl/handle_pool/index_pool.hpp"
+#include "asl/memory/allocator.hpp"
+#include "asl/containers/chunked_buffer.hpp"
+
+
+namespace asl
+{
+
+// @Todo If we want the allocator to be non-copyable, we could
+// introduce a reference allocator type that is copyable, and store
+// the "main" allocator in the pool.
+
+template<
+ is_object T,
+ int kIndexBits,
+ int kGenBits,
+ typename UserType = empty,
+ int kUserBits = 0,
+ isize_t kChunkSize = 32,
+ allocator Allocator = DefaultAllocator>
+requires moveable<T> && copyable<Allocator>
+class DenseHandlePool
+{
+ using ThisIndexPool = IndexPool<kIndexBits, kGenBits, UserType, kUserBits, isize_t, Allocator>;
+
+ struct Slot
+ {
+ ThisIndexPool::handle h;
+ T obj;
+
+ template<typename... Args>
+ explicit Slot(ThisIndexPool::handle h, Args&&... args)
+ : h{h}
+ , obj(std::forward<Args>(args)...)
+ {}
+ };
+
+ using Buffer = chunked_buffer<Slot, kChunkSize, Allocator>;
+
+ ThisIndexPool m_index_pool{};
+ Buffer m_buffer{};
+
+ using config = ThisIndexPool::handle::config;
+
+ template<typename... Args>
+ isize_t push(Args&&... args)
+ requires constructible_from<T, Args&&...>
+ {
+ m_buffer.push(typename ThisIndexPool::handle{}, std::forward<Args>(args)...);
+ return m_buffer.size() - 1;
+ }
+
+public:
+ using handle = ThisIndexPool::handle;
+
+ DenseHandlePool() requires default_constructible<Allocator> = default;
+
+ explicit DenseHandlePool(const Allocator& allocator)
+ : m_index_pool(allocator)
+ , m_buffer(allocator)
+ {}
+
+ ASL_DELETE_COPY(DenseHandlePool);
+ ASL_DEFAULT_MOVE(DenseHandlePool);
+ ~DenseHandlePool() = default;
+
+ [[nodiscard]] bool is_full() const
+ {
+ return m_index_pool.is_full();
+ }
+
+ bool is_valid(handle h) const
+ {
+ return m_index_pool.is_valid(h);
+ }
+
+ template<typename... Args>
+ option<handle> acquire(config::UserType user, Args&&... args)
+ requires config::kHasUser && constructible_from<T, Args&&...>
+ {
+ if (is_full()) { return nullopt; }
+ const isize_t obj_index = push(std::forward<Args>(args)...);
+ const auto handle = m_index_pool.acquire_ensure(user, obj_index);
+ m_buffer[obj_index].h = handle;
+ return handle;
+ }
+
+ template<typename... Args>
+ option<handle> acquire(Args&&... args)
+ requires (!config::kHasUser) && constructible_from<T, Args&&...>
+ {
+ if (is_full()) { return nullopt; }
+ const isize_t obj_index = push(std::forward<Args>(args)...);
+ const auto handle = m_index_pool.acquire_ensure(obj_index);
+ m_buffer[obj_index].h = handle;
+ return handle;
+ }
+
+ template<typename... Args>
+ handle acquire_ensure(config::UserType user, Args&&... args)
+ requires config::kHasUser && constructible_from<T, Args&&...>
+ {
+ ASL_ASSERT_RELEASE(!is_full());
+ const isize_t obj_index = push(std::forward<Args>(args)...);
+ const auto handle = m_index_pool.acquire_ensure(user, obj_index);
+ m_buffer[obj_index].h = handle;
+ return handle;
+ }
+
+ template<typename... Args>
+ handle acquire_ensure(Args&&... args)
+ requires (!config::kHasUser) && constructible_from<T, Args&&...>
+ {
+ ASL_ASSERT_RELEASE(!is_full());
+ const isize_t obj_index = push(std::forward<Args>(args)...);
+ const auto handle = m_index_pool.acquire_ensure(obj_index);
+ m_buffer[obj_index].h = handle;
+ return handle;
+ }
+
+ void release(handle to_release_handle)
+ {
+ if (!is_valid(to_release_handle)) { return; }
+
+ const auto to_release_index = *m_index_pool.get_payload(to_release_handle);
+ if (to_release_index < m_buffer.size() - 1)
+ {
+ const auto to_swap_index = m_buffer.size() - 1;
+ const auto to_swap_handle = m_buffer[to_swap_index].h;
+
+ m_buffer[to_release_index] = std::move(m_buffer[to_swap_index]);
+ m_index_pool.exchange_payload(to_swap_handle, to_release_index);
+ }
+
+ m_buffer.pop();
+ m_index_pool.release(to_release_handle);
+ }
+
+ auto get(this auto&& self, handle h)
+ -> copy_const_t<un_ref_t<decltype(self)>, T>*
+ {
+ if (!self.is_valid(h)) { return nullptr; }
+ const auto index = *self.m_index_pool.get_payload(h);
+ return &self.m_buffer[index].obj;
+ }
+
+ auto get_ensure(this auto&& self, handle h)
+ -> copy_cref_t<decltype(self), T>
+ {
+ ASL_ASSERT_RELEASE(self.is_valid(h));
+ const auto index = *self.m_index_pool.get_payload(h);
+ return std::forward<decltype(self)>(self).m_buffer[index].obj;
+ }
+};
+
+} // namespace asl