1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
// 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 copyable<Allocator>
class SparseHandlePool
{
using ThisIndexPool = IndexPool<kIndexBits, kGenBits, UserType, kUserBits, empty, Allocator>;
struct Slot
{
ThisIndexPool::handle h;
maybe_uninit<T> obj;
Slot() = default;
ASL_DELETE_COPY_MOVE(Slot);
~Slot()
{
if (!h.is_null())
{
obj.destroy_unsafe();
}
}
};
using Buffer = chunked_buffer<Slot, kChunkSize, Allocator>;
ThisIndexPool m_index_pool{};
Buffer m_buffer{};
using config = ThisIndexPool::handle::config;
template<typename... Args>
void set_object(ThisIndexPool::handle h, Args&&... args)
{
const auto index = static_cast<isize_t>(h.index());
if (m_buffer.size() <= index)
{
m_buffer.resize(index + 1);
}
m_buffer[index].h = h;
m_buffer[index].obj.construct_unsafe(std::forward<Args>(args)...);
}
public:
using handle = ThisIndexPool::handle;
SparseHandlePool() requires default_constructible<Allocator> = default;
explicit SparseHandlePool(const Allocator& allocator)
: m_index_pool(allocator)
, m_buffer(allocator)
{}
ASL_DELETE_COPY(SparseHandlePool);
ASL_DEFAULT_MOVE(SparseHandlePool);
~SparseHandlePool() = 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 auto handle = m_index_pool.acquire_ensure(user);
set_object(handle, std::forward<Args>(args)...);
return handle;
}
template<typename... Args>
option<handle> acquire(Args&&... args)
requires (!config::kHasUser) && constructible_from<T, Args&&...>
{
if (is_full()) { return nullopt; }
const auto handle = m_index_pool.acquire_ensure();
set_object(handle, std::forward<Args>(args)...);
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 auto handle = m_index_pool.acquire_ensure(user);
set_object(handle, std::forward<Args>(args)...);
return handle;
}
template<typename... Args>
handle acquire_ensure(Args&&... args)
requires (!config::kHasUser) && constructible_from<T, Args&&...>
{
ASL_ASSERT_RELEASE(!is_full());
const auto handle = m_index_pool.acquire_ensure();
set_object(handle, std::forward<Args>(args)...);
return handle;
}
void release(handle h)
{
if (!is_valid(h)) { return; }
auto& slot = m_buffer[static_cast<isize_t>(h.index())];
slot.h = {};
slot.obj.destroy_unsafe();
m_index_pool.release(h);
}
auto get(this auto&& self, handle h)
-> copy_const_t<un_ref_t<decltype(self)>, T>*
{
if (!self.is_valid(h)) { return nullptr; }
return &std::forward<decltype(self)>(self).m_buffer[static_cast<isize_t>(h.index())]
.obj.as_init_unsafe();
}
auto get_ensure(this auto&& self, handle h)
-> copy_cref_t<decltype(self), T>
{
ASL_ASSERT_RELEASE(self.is_valid(h));
return std::forward<decltype(self)>(self).m_buffer[static_cast<isize_t>(h.index())]
.obj.as_init_unsafe();
}
};
} // namespace asl
|