2#ifndef LIBSBX_CONTAINERS_STABLE_VECTOR_HPP_
3#define LIBSBX_CONTAINERS_STABLE_VECTOR_HPP_
13#include <shared_mutex>
16namespace sbx::containers {
18inline constexpr auto pointer_size =
sizeof(
void*);
20template<
typename Type, std::
size_t PageSize = 256>
25 using value_type = Type;
26 using reference = value_type&;
27 using const_reference =
const value_type&;
28 using pointer = value_type*;
29 using size_type = std::size_t;
31 inline static constexpr auto page_size = PageSize;
34 : _page_table{
nullptr},
41 other.for_each([
this](const_reference element)
mutable {
42 emplace_back_no_lock().second = element;
47 for (
auto i = 0u; i < _page_count; i++) {
48 delete _page_table[i];
59 other.for_each([
this](const_reference element)
mutable {
60 emplace_back_no_lock().second = element;
66 auto clear() ->
void {
67 for (
auto i = 0u; i < _page_count; i++) {
68 delete _page_table[i];
76 _page_table.store(
nullptr);
79 auto operator[](
const size_type index) -> reference {
80 const auto page_index = index / page_size;
82 return _page_table[page_index]->elements[index - (page_index * page_size)];
85 auto operator[](
const size_type index)
const -> const_reference {
86 const auto page_index = index / page_size;
88 return _page_table[page_index]->elements[index - (page_index * page_size)];
91 auto get_element_count()
const -> size_type {
92 return _element_count;
95 auto insert(value_type&& element) -> std::pair<std::uint32_t, reference> {
96 const auto page_index = _element_count / page_size;
98 if (_element_count >= _capacity) {
99 auto lock = std::scoped_lock{_mutex};
101 if (_element_count >= _capacity) {
102 auto* new_page =
new page();
104 if (page_index >= _page_count) {
105 auto old_pages = _page_count;
107 _page_count = (std::max)(std::uint64_t(16), _page_count * 2);
109 auto new_page_table = std::make_unique<page*[]>(_page_count);
111 std::memcpy(new_page_table.get(), _page_table.load(), old_pages * pointer_size);
113 _page_table.exchange(new_page_table.get());
114 _page_tables.push_back(std::move(new_page_table));
117 _page_table[page_index] = new_page;
119 _capacity += page_size;
123 std::uint32_t index = (++_element_count - 1);
124 _page_table[page_index]->elements[index - (page_index * page_size)] = std::move(element);
125 return { index, _page_table[page_index]->elements[index - (page_index * page_size)] };
128 auto insert_no_lock(value_type&& element) -> std::pair<std::uint32_t, reference> {
129 const auto page_index = _element_count / page_size;
131 if (_element_count >= _capacity) {
132 auto lock = std::scoped_lock{_mutex};
134 if (_element_count >= _capacity) {
135 auto* new_page =
new page();
137 if (page_index >= _page_count) {
138 auto old_pages = _page_count;
140 _page_count = (std::max)(std::uint64_t(16), _page_count * 2);
142 auto new_page_table = std::make_unique<page*[]>(_page_count);
144 std::memcpy(new_page_table.get(), _page_table.load(), old_pages * pointer_size);
146 _page_table.exchange(new_page_table.get());
147 _page_tables.push_back(std::move(new_page_table));
150 _page_table[page_index] = new_page;
152 _capacity += page_size;
156 const auto index = (++_element_count - 1);
158 _page_table[page_index]->elements[index - (page_index * page_size)] = std::move(element);
160 return { index, _page_table[page_index]->elements[index - (page_index * page_size)] };
163 auto emplace_back() -> std::pair<std::uint32_t, reference> {
164 const auto page_index = _element_count / page_size;
166 if (_element_count >= _capacity) {
167 auto* new_page =
new page();
169 if (page_index >= _page_count) {
170 auto old_pages = _page_count;
172 _page_count = (std::max)(std::uint64_t(16), _page_count * 2);
174 auto new_page_table = std::make_unique<page*[]>(_page_count);
176 std::memcpy(new_page_table.get(), _page_table.load(), old_pages * pointer_size);
178 _page_table.exchange(new_page_table.get());
179 _page_tables.push_back(std::move(new_page_table));
182 _page_table[page_index] = new_page;
184 _capacity += page_size;
187 const auto index = (++_element_count - 1);
189 return { index, _page_table[page_index]->elements[index - (page_index * page_size)] };
192 auto emplace_back_no_lock() -> std::pair<std::uint32_t, reference> {
193 const auto page_index = _element_count / page_size;
195 if (_element_count >= _capacity) {
196 auto* new_page =
new page();
198 if (page_index >= _page_count) {
199 auto old_pages = _page_count;
201 _page_count = std::max(std::uint64_t{16}, _page_count * 2u);
203 auto new_page_table = std::make_unique<page*[]>(_page_count);
205 std::memcpy(new_page_table.get(), _page_table.load(), old_pages * pointer_size);
207 _page_table.exchange(new_page_table.get());
208 _page_tables.push_back(std::move(new_page_table));
211 _page_table[page_index] = new_page;
213 _capacity += page_size;
216 const auto index = (++_element_count - 1);
218 return { index, _page_table[page_index]->elements[index - (page_index * page_size)] };
221 template<
typename Callable>
222 requires (std::is_invocable_v<Callable, reference>)
223 auto for_each(Callable&& callable) ->
void {
224 for (
auto i = 0u; i < _element_count; ++i) {
225 std::invoke(callable, (*
this)[i]);
229 template<
typename Callable>
230 requires (std::is_invocable_v<Callable, const_reference>)
231 auto for_each(Callable&& callable)
const ->
void {
232 for (
auto i = 0u; i < _element_count; ++i) {
233 std::invoke(callable, (*
this)[i]);
240 std::array<value_type, page_size> elements;
243 std::shared_mutex _mutex;
244 std::list<std::unique_ptr<page*[]>> _page_tables;
246 std::atomic<page**> _page_table;
247 std::atomic<std::uint32_t> _element_count;
248 std::atomic<std::uint32_t> _capacity;
249 std::uint64_t _page_count;
Definition: stable_vector.hpp:21