summaryrefslogtreecommitdiff
path: root/asl/handle_pool/index_pool.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'asl/handle_pool/index_pool.hpp')
-rw-r--r--asl/handle_pool/index_pool.hpp111
1 files changed, 110 insertions, 1 deletions
diff --git a/asl/handle_pool/index_pool.hpp b/asl/handle_pool/index_pool.hpp
index 1d359d5..ece61c3 100644
--- a/asl/handle_pool/index_pool.hpp
+++ b/asl/handle_pool/index_pool.hpp
@@ -11,6 +11,7 @@ namespace asl
{
// @Todo Uniquely represented for the handle?
+// @Todo niche for handles
template<
int kIndexBits_,
@@ -27,6 +28,7 @@ struct index_pool_config
using PrimitiveUserType = smallest_unsigned_integer_type_for_width<size_of<UserType> * 8>;
static_assert(trivially_copy_constructible<UserType>);
+ static_assert(trivially_destructible<UserType>);
static_assert(size_of<UserType> == size_of<PrimitiveUserType>, "UserType should be of size 1, 2 or 4");
static constexpr int kUserBits = []() static -> int {
@@ -60,9 +62,10 @@ template<
>
class index_pool_handle
{
- // using config = index_pool_config<5, 5>;
+public:
using config = index_pool_config<kIndexBits_, kGenBits_, UserType_, kUserBits_>;
+private:
config::HandleType m_handle{};
public:
@@ -116,5 +119,111 @@ public:
constexpr bool operator==(this index_pool_handle self, index_pool_handle other) = default;
};
+template<
+ int kIndexBits_,
+ int kGenBits_,
+ typename UserType_ = empty,
+ int kUserBits_ = 0,
+ typename Payload = empty,
+ allocator Allocator = DefaultAllocator
+>
+class IndexPool
+{
+public:
+ using handle = index_pool_handle<kIndexBits_, kGenBits_, UserType_, kUserBits_>;
+
+private:
+ using config = handle::config;
+
+ static constexpr bool kHasPayload = !same_as<Payload, empty>;
+
+ // @Todo Remove need for default constructible & trivially destructible for payload
+ // Use union in slot to store the variants
+
+ // @Todo Use dummy user type with inactive handle type -> no need to set user data when allocating handle
+
+ static_assert(default_constructible<Payload>);
+ static_assert(copy_constructible<Payload>);
+ static_assert(trivially_destructible<Payload>);
+
+ struct Slot
+ {
+ bool is_end_of_list : 1;
+ bool is_active : 1;
+
+ handle handle;
+
+ ASL_NO_UNIQUE_ADDRESS Payload payload;
+ };
+
+ chunked_buffer<Slot, 256, Allocator> m_slots;
+
+ // We only use the index, this is essentially the head of the linked
+ // list to the first available slot.
+ // Then the index of each slot points to the next available one.
+ handle m_first_available;
+
+ static constexpr handle make_handle(uint64_t index, uint64_t gen, [[maybe_unused]] config::UserType user_data)
+ {
+ if constexpr (config::kHasUser)
+ {
+ return handle(index, gen, user_data);
+ }
+ else
+ {
+ return handle(index, gen);
+ }
+ };
+
+ void allocate_new_slot(config::UserType user)
+ {
+ // @Todo Check that we don't go past capacity.
+
+ const auto new_index = static_cast<uint64_t>(m_slots.size());
+ const handle new_handle = make_handle(new_index, 0, user);
+
+ m_slots.push(Slot{
+ .is_end_of_list = true,
+ .is_active = false,
+ .handle = new_handle,
+ .payload = Payload{}
+ });
+
+ m_first_available = new_handle;
+ }
+
+ handle acquire_handle(const Payload& payload, config::UserType user)
+ {
+ if (!m_first_available.is_valid())
+ {
+ allocate_new_slot(user);
+ }
+
+ ASL_ASSERT(m_first_available.is_valid());
+
+ auto index = static_cast<isize_t>(m_first_available.index());
+
+ Slot& slot = m_slots[index];
+ ASL_ASSERT(!slot.is_active);
+
+ m_first_available = slot.is_end_of_line ? handle{} : slot.handle;
+
+ slot.is_active = true;
+ slot.payload = payload;
+
+ return make_handle(index, slot.handle.gen(), user);
+ }
+
+public:
+ IndexPool() requires default_constructible<Allocator> = default;
+
+ explicit IndexPool(Allocator allocator) : m_slots{std::move(allocator)} {}
+
+ handle acquire(const Payload& payload)
+ {
+ return acquire_handle(payload);
+ }
+};
+
} // namespace asl