sandbox
Loading...
Searching...
No Matches
stable_vector.hpp
1// SPDX-License-Identifier: MIT
2#ifndef LIBSBX_CONTAINERS_STABLE_VECTOR_HPP_
3#define LIBSBX_CONTAINERS_STABLE_VECTOR_HPP_
4
5#include <cstring>
6
7#include <array>
8#include <atomic>
9#include <functional>
10#include <list>
11#include <memory>
12#include <mutex>
13#include <shared_mutex>
14#include <vector>
15
16namespace sbx::containers {
17
18inline constexpr auto pointer_size = sizeof(void*);
19
20template<typename Type, std::size_t PageSize = 256>
22
23public:
24
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;
30
31 inline static constexpr auto page_size = PageSize;
32
34 : _page_table{nullptr},
35 _element_count{0},
36 _capacity{0},
37 _page_count{0} { }
38
39
40 stable_vector(const stable_vector& other) {
41 other.for_each([this](const_reference element) mutable {
42 emplace_back_no_lock().second = element;
43 });
44 }
45
47 for (auto i = 0u; i < _page_count; i++) {
48 delete _page_table[i];
49 }
50 }
51
52 auto operator=(const stable_vector& other) -> stable_vector& {
53 if (this == &other) {
54 return *this;
55 }
56
57 clear();
58
59 other.for_each([this](const_reference element) mutable {
60 emplace_back_no_lock().second = element;
61 });
62
63 return *this;
64 }
65
66 auto clear() -> void {
67 for (auto i = 0u; i < _page_count; i++) {
68 delete _page_table[i];
69 }
70
71 _element_count = 0;
72 _capacity = 0;
73 _page_count = 0;
74
75 _page_tables.clear();
76 _page_table.store(nullptr);
77 }
78
79 auto operator[](const size_type index) -> reference {
80 const auto page_index = index / page_size;
81
82 return _page_table[page_index]->elements[index - (page_index * page_size)];
83 }
84
85 auto operator[](const size_type index) const -> const_reference {
86 const auto page_index = index / page_size;
87
88 return _page_table[page_index]->elements[index - (page_index * page_size)];
89 }
90
91 auto get_element_count() const -> size_type {
92 return _element_count;
93 }
94
95 auto insert(value_type&& element) -> std::pair<std::uint32_t, reference> {
96 const auto page_index = _element_count / page_size;
97
98 if (_element_count >= _capacity) {
99 auto lock = std::scoped_lock{_mutex};
100
101 if (_element_count >= _capacity) {
102 auto* new_page = new page();
103
104 if (page_index >= _page_count) {
105 auto old_pages = _page_count;
106
107 _page_count = (std::max)(std::uint64_t(16), _page_count * 2);
108
109 auto new_page_table = std::make_unique<page*[]>(_page_count);
110
111 std::memcpy(new_page_table.get(), _page_table.load(), old_pages * pointer_size);
112
113 _page_table.exchange(new_page_table.get());
114 _page_tables.push_back(std::move(new_page_table));
115 }
116
117 _page_table[page_index] = new_page;
118
119 _capacity += page_size;
120 }
121 }
122
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)] };
126 }
127
128 auto insert_no_lock(value_type&& element) -> std::pair<std::uint32_t, reference> {
129 const auto page_index = _element_count / page_size;
130
131 if (_element_count >= _capacity) {
132 auto lock = std::scoped_lock{_mutex};
133
134 if (_element_count >= _capacity) {
135 auto* new_page = new page();
136
137 if (page_index >= _page_count) {
138 auto old_pages = _page_count;
139
140 _page_count = (std::max)(std::uint64_t(16), _page_count * 2);
141
142 auto new_page_table = std::make_unique<page*[]>(_page_count);
143
144 std::memcpy(new_page_table.get(), _page_table.load(), old_pages * pointer_size);
145
146 _page_table.exchange(new_page_table.get());
147 _page_tables.push_back(std::move(new_page_table));
148 }
149
150 _page_table[page_index] = new_page;
151
152 _capacity += page_size;
153 }
154 }
155
156 const auto index = (++_element_count - 1);
157
158 _page_table[page_index]->elements[index - (page_index * page_size)] = std::move(element);
159
160 return { index, _page_table[page_index]->elements[index - (page_index * page_size)] };
161 }
162
163 auto emplace_back() -> std::pair<std::uint32_t, reference> {
164 const auto page_index = _element_count / page_size;
165
166 if (_element_count >= _capacity) {
167 auto* new_page = new page();
168
169 if (page_index >= _page_count) {
170 auto old_pages = _page_count;
171
172 _page_count = (std::max)(std::uint64_t(16), _page_count * 2);
173
174 auto new_page_table = std::make_unique<page*[]>(_page_count);
175
176 std::memcpy(new_page_table.get(), _page_table.load(), old_pages * pointer_size);
177
178 _page_table.exchange(new_page_table.get());
179 _page_tables.push_back(std::move(new_page_table));
180 }
181
182 _page_table[page_index] = new_page;
183
184 _capacity += page_size;
185 }
186
187 const auto index = (++_element_count - 1);
188
189 return { index, _page_table[page_index]->elements[index - (page_index * page_size)] };
190 }
191
192 auto emplace_back_no_lock() -> std::pair<std::uint32_t, reference> {
193 const auto page_index = _element_count / page_size;
194
195 if (_element_count >= _capacity) {
196 auto* new_page = new page();
197
198 if (page_index >= _page_count) {
199 auto old_pages = _page_count;
200
201 _page_count = std::max(std::uint64_t{16}, _page_count * 2u);
202
203 auto new_page_table = std::make_unique<page*[]>(_page_count);
204
205 std::memcpy(new_page_table.get(), _page_table.load(), old_pages * pointer_size);
206
207 _page_table.exchange(new_page_table.get());
208 _page_tables.push_back(std::move(new_page_table));
209 }
210
211 _page_table[page_index] = new_page;
212
213 _capacity += page_size;
214 }
215
216 const auto index = (++_element_count - 1);
217
218 return { index, _page_table[page_index]->elements[index - (page_index * page_size)] };
219 }
220
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]);
226 }
227 }
228
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]);
234 }
235 }
236
237private:
238
239 struct page {
240 std::array<value_type, page_size> elements;
241 }; // struct page
242
243 std::shared_mutex _mutex;
244 std::list<std::unique_ptr<page*[]>> _page_tables;
245
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;
250
251}; // class stable_vector
252
253} // namespace sbx::containers
254
255#endif // LIBSBX_CONTAINERS_STABLE_VECTOR_HPP_
Definition: stable_vector.hpp:21