Maipa's Standard Library Extension 1.5.6
mstd
Loading...
Searching...
No Matches
stable_vector.hpp
1/*
2 * mstd - Maipa's Standard Library
3 *
4 * Licensed under the BSD 3-Clause License with Attribution Requirement.
5 * See the LICENSE file for details: https://github.com/MAIPA01/mstd/blob/main/LICENSE
6 *
7 * Copyright (c) 2025, Patryk Antosik (MAIPA01)
8 */
9
10#pragma once
11#ifndef _MSTD_STABLE_VECTOR_HPP_
12 #define _MSTD_STABLE_VECTOR_HPP_
13
14 #include <mstd/config.hpp>
15
17_MSTD_WARNING("this is only available for c++17 and greater!");
18 #else
19
20 #include <mstd/assert.hpp>
21 #include <mstd/containers_types.hpp>
22
23namespace mstd {
24 template<class T>
25 class stable_vector {
26 public:
27 using value_type = remove_cvref_t<T>;
28 using reference = value_type&;
29 using const_reference = const value_type&;
30
31 private:
32 using _data_type = std::vector<value_type>;
33
34 public:
35 using size_type = _MSTD_TYPENAME17 _data_type::size_type;
36 using difference_type = _MSTD_TYPENAME17 _data_type::difference_type;
37 using iterator = _MSTD_TYPENAME17 _data_type::iterator;
38 using const_iterator = _MSTD_TYPENAME17 _data_type::const_iterator;
39 using reverse_iterator = _MSTD_TYPENAME17 _data_type::reverse_iterator;
40 using const_reverse_iterator = _MSTD_TYPENAME17 _data_type::const_reverse_iterator;
41
42 private:
43 using _to_data_type = std::vector<size_type>;
44 using _to_id_type = std::vector<size_type>;
45
46 _to_data_type _toData;
47 _to_id_type _toId;
48 _data_type _data;
49
50 _MSTD_CONSTEXPR20 void _append_indexes(size_type count) {
51 size_type newSize = _toData.size() + count;
52
53 _toId.reserve(newSize);
54 _toData.reserve(newSize);
55
56 for (size_type i = _toData.size(); i < newSize; ++i) {
57 _toData.push_back(i);
58 _toId.push_back(i);
59 }
60 }
61
62 template<class U>
63 _MSTD_CONSTEXPR20 iterator _insert(U&& value) {
64 if (_data.size() == size()) { _append_indexes(1); }
65 return _data.insert(_data.cend(), std::forward<U>(value));
66 }
67
68 template<class U>
69 _MSTD_CONSTEXPR20 iterator _insert_at(const size_type id, U&& value) {
70 if (has_value(id)) {
71 _data[_toData[id]] = std::forward<U>(value);
72 return std::next(_data.begin(), _toData[id]);
73 }
74
75 _append_indexes(id + 1 - size());
76
77 // insert data
78 auto iter = _data.insert(_data.cend(), std::forward<U>(value));
79
80 if (_data.size() != size()) {
81 size_type toSwapId = _toId[_data.size() - 1];
82
83 // swap ids
84 std::swap(_toId[_data.size() - 1], _toId[id]);
85
86 // swap indexes
87 std::swap(_toData[toSwapId], _toData[id]);
88 }
89
90 return iter;
91 }
92
93 template<class U>
94 _MSTD_CONSTEXPR20 iterator _insert_at(const_iterator pos, U&& value) {
95 return _insert_at(_toId[std::distance(_data.cbegin(), pos)], std::forward<U>(value));
96 }
97
98 template<class U>
99 _MSTD_CONSTEXPR20 void _push_back(U&& value) {
100 _insert(std::forward<U>(value));
101 }
102
103 _MSTD_CONSTEXPR20 iterator _erase(const std::vector<size_t>& indexes) {
104 iterator iter = end();
105 for (const auto& idx : indexes) { iter = erase(idx); }
106 return iter;
107 }
108
109 template<class U>
110 _MSTD_CONSTEXPR20 void _resize(const size_type count, U&& value) {
111 _append_indexes(count - size());
112 _data.resize(count, std::forward<U>(value));
113 }
114
115 public:
116 _MSTD_CONSTEXPR20 stable_vector() = default;
117
118 _MSTD_CONSTEXPR20 stable_vector(const stable_vector&) = default;
119 _MSTD_CONSTEXPR20 stable_vector(stable_vector&&) noexcept = default;
120
121 _MSTD_CONSTEXPR20 explicit stable_vector(const size_type count) { resize(count); }
122
123 _MSTD_CONSTEXPR20 stable_vector(const size_type count, const value_type& value) { resize(count, value); }
124
125 _MSTD_CONSTEXPR20 stable_vector(const size_type count, value_type&& value) { resize(count, std::move(value)); }
126
127 _MSTD_CONSTEXPR20 stable_vector(std::initializer_list<value_type> init) : _data(init) { _append_indexes(_data.size()); }
128
130 template<mstd::iterator_of<value_type> Iter>
131 #else
132 template<class Iter, std::enable_if_t<is_iterator_of_v<Iter, value_type>, bool> = true>
133 #endif
134 _MSTD_CONSTEXPR20 stable_vector(Iter begin, Iter end) : _data(begin, end) {
135 _append_indexes(_data.size());
136 }
137
138 _MSTD_CONSTEXPR20 ~stable_vector() = default;
139
140 _MSTD_CONSTEXPR20 stable_vector& operator=(const stable_vector&) = default;
141 _MSTD_CONSTEXPR20 stable_vector& operator=(stable_vector&&) noexcept = default;
142
143 #pragma region INSERT_ON_FREE_SPOT
144
145 _MSTD_CONSTEXPR20 iterator insert(const value_type& value) { return _insert<const value_type&>(value); }
146
147 _MSTD_CONSTEXPR20 iterator insert(value_type&& value) { return _insert(std::move(value)); }
148
149 _MSTD_CONSTEXPR20 iterator insert(const value_type& value, const size_type count) {
150 _append_indexes(count - (size() - _data.size()));
151 return _data.insert(_data.cend(), count, value);
152 }
153
155 template<mstd::iterator_of<value_type> Iter>
156 #else
157 template<class Iter, std::enable_if_t<is_iterator_of_v<Iter, value_type>, bool> = true>
158 #endif
159 _MSTD_CONSTEXPR20 iterator insert(Iter first, Iter last) {
160 size_type count = std::distance(first, last);
161 _append_indexes(count - (size() - _data.size()));
162 return _data.insert(_data.cend(), first, last);
163 }
164
165 _MSTD_CONSTEXPR20 iterator insert(std::initializer_list<value_type> init) { return insert(init.begin(), init.end()); }
166
167 #pragma endregion INSERT_ON_FREE_SPOT
168
169 #pragma region INSERT_AT_IDX
170
171 _MSTD_CONSTEXPR20 iterator insert_at(const size_type id, const value_type& value) { return _insert_at(id, value); }
172
173 _MSTD_CONSTEXPR20 iterator insert_at(const size_type id, value_type&& value) { return _insert_at(id, std::move(value)); }
174
175 _MSTD_CONSTEXPR20 iterator insert_at(const size_type id, const value_type& value, const size_type count) {
176 iterator iter = end();
177 for (size_t i = 0; i < count; ++i) { iter = _insert_at(id + i, value); }
178 return iter;
179 }
180
182 template<iterator_of<value_type> Iter>
183 #else
184 template<class Iter, std::enable_if_t<is_iterator_of_v<Iter, value_type>, bool> = true>
185 #endif
186 _MSTD_CONSTEXPR20 iterator insert_at(size_type id, Iter first, Iter last) {
187 iterator iter = end();
188 for (Iter it = first; it != last; ++it, ++id) { iter = insert_at(id, *it); }
189 return iter;
190 }
191
192 _MSTD_CONSTEXPR20 iterator insert_at(const size_type id, std::initializer_list<value_type> init) {
193 return insert_at(id, init.begin(), init.end());
194 }
195
196 #pragma endregion INSERT_AT_IDX
197
198 #pragma region INSERT_AT_ITER
199
200 _MSTD_CONSTEXPR20 iterator insert_at(const_iterator pos, const value_type& value) { return _insert_at(pos, value); }
201
202 _MSTD_CONSTEXPR20 iterator insert_at(const_iterator pos, value_type&& value) { return _insert_at(pos, std::move(value)); }
203
204 _MSTD_CONSTEXPR20 iterator insert_at(const_iterator pos, const value_type& value, const size_type count) {
205 return _insert_at(_toId[std::distance(_data.cbegin(), pos)], value, count);
206 }
207
209 template<mstd::iterator_of<value_type> Iter>
210 #else
211 template<class Iter, std::enable_if_t<is_iterator_of_v<Iter, value_type>, bool> = true>
212 #endif
213 _MSTD_CONSTEXPR20 iterator insert_at(const_iterator pos, Iter first, Iter last) {
214 return insert_at(_toId[std::distance(_data.cbegin(), pos)], first, last);
215 }
216
217 _MSTD_CONSTEXPR20 iterator insert_at(const_iterator pos, std::initializer_list<value_type> init) {
218 return insert_at(pos, init.begin(), init.end());
219 }
220
221 #pragma endregion INSERT_AT_ITER
222
223 #pragma region EMPLACE_ON_FREE_SPOT
224
226 template<class... Args>
227 #else
228 template<class... Args, std::enable_if_t<std::is_constructible_v<value_type, Args...>, bool> = true>
229 #endif
230 _MSTD_CONSTEXPR20 iterator emplace(Args&&... args) _MSTD_REQUIRES((std::constructible_from<value_type, Args...>)) {
231 return insert(value_type(std::forward<Args>(args)...));
232 }
233
234 #pragma endregion
235
236 #pragma region EMPLACE_AT_IDX
237
239 template<class... Args>
240 #else
241 template<class... Args, std::enable_if_t<std::is_constructible_v<value_type, Args...>, bool> = true>
242 #endif
243 _MSTD_CONSTEXPR20 iterator emplace_at(const size_type id,
244 Args&&... args) _MSTD_REQUIRES((std::constructible_from<value_type, Args...>)) {
245 return insert_at(id, value_type(std::forward<Args>(args)...));
246 }
247
248 #pragma endregion
249
250 #pragma region EMPLACE_AT_ITER
251
253 template<class... Args>
254 #else
255 template<class... Args, std::enable_if_t<std::is_constructible_v<value_type, Args...>, bool> = true>
256 #endif
257 _MSTD_CONSTEXPR20 iterator emplace_at(const_iterator pos,
258 Args&&... args) _MSTD_REQUIRES((std::constructible_from<value_type, Args...>)) {
259 return insert_at(pos, value_type(std::forward<Args>(args)...));
260 }
261
262 #pragma endregion
263
265 template<class... Args>
266 #else
267 template<class... Args, std::enable_if_t<std::is_constructible_v<value_type, Args...>, bool> = true>
268 #endif
269 _MSTD_CONSTEXPR20 value_type& emplace_back(
270 Args&&... args
271 ) _MSTD_REQUIRES((std::constructible_from<value_type, Args...>)) {
272 return *emplace(std::forward<Args>(args)...);
273 }
274
275 _MSTD_CONSTEXPR20 void push_back(const value_type& value) { _push_back(value); }
276
277 _MSTD_CONSTEXPR20 void push_back(value_type&& value) { _push_back(std::move(value)); }
278
279 _MSTD_CONSTEXPR20 iterator erase(const size_type id) {
280 mstd_assert(id < size(), "Index out of bounds");
281 if (!has_value(id)) { return _data.end(); }
282
283 // get data index
284 size_t dataIndex = _toData[id];
285 size_t lastDataIndex = _data.size() - 1;
286
287 // swap data (move current to back)
288 std::swap(_data[dataIndex], _data[lastDataIndex]);
289
290 // swap ids (move to same position as data)
291 std::swap(_toId[dataIndex], _toId[lastDataIndex]);
292
293 // erase last item in data
294 _data.pop_back();
295
296 // update indexes (update to data pointers)
297 std::swap(_toData[_toId[dataIndex]], _toData[id]);
298
299 // // if last id was not freed then we don't need to shrink toData and toId
300 // if (id != _toData.size() - 1) { return end(); }
301
302 // shrink toData and toId to last possible id
303 while (!_toData.empty() && !has_value(_toData.size() - 1)) {
304 size_t lastId = _toData.size() - 1;
305
306 size_t posInToId = _toData[lastId];
307 size_t lastPosInToId = _toId.size() - 1;
308
309 if (posInToId != lastPosInToId) {
310 size_t idAtLastPos = _toId[lastPosInToId];
311
312 std::swap(_toId[posInToId], _toId[lastPosInToId]);
313 std::swap(_toData[lastId], _toData[idAtLastPos]);
314 }
315
316 _toData.pop_back();
317 _toId.pop_back();
318 }
319
320 return end();
321 }
322
323 _MSTD_CONSTEXPR20 iterator erase(const size_type firstId, const size_type endId) {
324 iterator iter = end();
325 for (size_t i = firstId; i < endId; ++i) { iter = erase(i); }
326 return iter;
327 }
328
329 _MSTD_CONSTEXPR20 iterator erase(iterator pos) {
330 mstd_assert(pos != _data.end(), "Pos out of bounds");
331 return erase(get_id(pos));
332 }
333
334 _MSTD_CONSTEXPR20 iterator erase(const_iterator pos) {
335 mstd_assert(pos != _data.cend(), "Pos out of bounds");
336 return erase(get_id(pos));
337 }
338
339 _MSTD_CONSTEXPR20 iterator erase(iterator first, iterator last) {
340 std::vector<size_t> idsToErase = {};
341 idsToErase.reserve(std::distance(first, last));
342
343 for (iterator it = first; it != last; ++it) { idsToErase.push_back(get_id(it)); }
344
345 return _erase(idsToErase);
346 }
347
348 _MSTD_CONSTEXPR20 iterator erase(const_iterator first, const_iterator last) {
349 std::vector<size_t> idsToErase = {};
350 idsToErase.reserve(std::distance(first, last));
351
352 for (const_iterator it = first; it != last; ++it) { idsToErase.push_back(get_id(it)); }
353
354 return _erase(idsToErase);
355 }
356
357 _MSTD_CONSTEXPR20 void reserve(const size_type capacity) {
358 _toData.reserve(capacity);
359 _toId.reserve(capacity);
360 _data.reserve(capacity);
361 }
362
363 _MSTD_CONSTEXPR20 void resize(const size_type count) {
364 _append_indexes(count - size());
365 _data.resize(count);
366 }
367
368 _MSTD_CONSTEXPR20 void resize(const size_type count, const value_type& value) { _resize(count, value); }
369
370 _MSTD_CONSTEXPR20 void resize(const size_type count, value_type&& value) { _resize(count, std::move(value)); }
371
372 _MSTD_CONSTEXPR20 void swap(stable_vector& other) noexcept {
373 _data.swap(other._data);
374 _toId.swap(other._toId);
375 _toData.swap(other._toData);
376 }
377
378 _MSTD_CONSTEXPR20 void clear() {
379 _toData.clear();
380 _toId.clear();
381 _data.clear();
382 }
383
384 [[nodiscard]] _MSTD_CONSTEXPR20 value_type& front() { return _data.front(); }
385
386 [[nodiscard]] _MSTD_CONSTEXPR20 const value_type& front() const { return _data.front(); }
387
388 [[nodiscard]] _MSTD_CONSTEXPR20 value_type& back() { return _data.back(); }
389
390 [[nodiscard]] _MSTD_CONSTEXPR20 const value_type& back() const { return _data.back(); }
391
392 [[nodiscard]] _MSTD_CONSTEXPR20 iterator get(const size_type id) {
393 mstd_assert(id < size(), "Index out of bounds");
394 mstd_assert(has_value(id), "Index is a pointer to empty element");
395
396 return std::next(begin(), _toData[id]);
397 }
398
399 [[nodiscard]] _MSTD_CONSTEXPR20 const_iterator get(const size_type id) const {
400 mstd_assert(id < size(), "Index out of bounds");
401 mstd_assert(has_value(id), "Index is a pointer to empty element");
402
403 return std::next(cbegin(), _toData[id]);
404 }
405
406 [[nodiscard]] _MSTD_CONSTEXPR20 iterator try_get(const size_type id) {
407 mstd_assert(id < size(), "Index out of bounds");
408 if (has_value(id)) { return get(id); }
409 return end();
410 }
411
412 [[nodiscard]] _MSTD_CONSTEXPR20 const_iterator try_get(const size_type id) const {
413 mstd_assert(id < size(), "Index out of bounds");
414 if (has_value(id)) { return get(id); }
415 return cend();
416 }
417
418 [[nodiscard]] _MSTD_CONSTEXPR20 value_type& at(const size_type id) {
419 mstd_assert(id < size(), "Index out of bounds");
420 mstd_assert(has_value(id), "Index is a pointer to empty element");
421
422 return _data[_toData[id]];
423 }
424
425 [[nodiscard]] _MSTD_CONSTEXPR20 const value_type& at(const size_type id) const {
426 mstd_assert(id < size(), "Index out of bounds");
427 mstd_assert(has_value(id), "Index is a pointer to empty element");
428
429 return _data[_toData[id]];
430 }
431
432 [[nodiscard]] _MSTD_CONSTEXPR20 value_type* try_at(const size_type id) {
433 mstd_assert(id < size(), "Index out of bounds");
434 if (has_value(id)) { return &at(id); }
435 return nullptr;
436 }
437
438 [[nodiscard]] _MSTD_CONSTEXPR20 const value_type* try_at(const size_type id) const {
439 mstd_assert(id < size(), "Index out of bounds");
440 if (has_value(id)) { return &at(id); }
441 return nullptr;
442 }
443
444 [[nodiscard]] _MSTD_CONSTEXPR20 size_type front_id() const { return get_id(0); }
445
446 [[nodiscard]] _MSTD_CONSTEXPR20 size_type back_id() const { return get_id(active_slots() - 1); }
447
448 [[nodiscard]] _MSTD_CONSTEXPR20 size_type get_next_id() const {
449 if (empty_slots() != 0) { return get_id(active_slots()); }
450 return size();
451 }
452
453 [[nodiscard]] _MSTD_CONSTEXPR20 size_type get_id(const size_type dataIndex) const {
454 mstd_assert(dataIndex < size(), "Data index out of bounds");
455 return _toId[dataIndex];
456 }
457
458 [[nodiscard]] _MSTD_CONSTEXPR20 size_type get_id(iterator pos) {
459 mstd_assert(pos != _data.end(), "Pos out of bounds");
460 return get_id(std::distance(_data.begin(), pos));
461 }
462
463 [[nodiscard]] _MSTD_CONSTEXPR20 size_type get_id(const_iterator pos) const {
464 mstd_assert(pos != _data.cend(), "Pos out of bounds");
465 return get_id(std::distance(_data.cbegin(), pos));
466 }
467
468 [[nodiscard]] _MSTD_CONSTEXPR20 size_type active_slots() const { return _data.size(); }
469
470 [[nodiscard]] _MSTD_CONSTEXPR17 size_type empty_slots() const { return size() - active_slots(); }
471
472 [[nodiscard]] _MSTD_CONSTEXPR20 size_type size() const { return _toData.size(); }
473
474 [[nodiscard]] _MSTD_CONSTEXPR20 size_type capacity() const { return _toData.capacity(); }
475
476 [[nodiscard]] _MSTD_CONSTEXPR20 size_type max_size() const { return _data.max_size(); }
477
478 [[nodiscard]] _MSTD_CONSTEXPR20 bool empty() const { return _data.empty(); }
479
480 [[nodiscard]] _MSTD_CONSTEXPR20 bool has_value(const size_type id) const {
481 return id < size() && _toData[id] < _data.size();
482 }
483
484 [[nodiscard]] _MSTD_CONSTEXPR20 bool has_value(iterator pos) const { return has_value(get_id(pos)); }
485
486 [[nodiscard]] _MSTD_CONSTEXPR20 bool has_value(const_iterator pos) const { return has_value(get_id(pos)); }
487
488 [[nodiscard]] _MSTD_CONSTEXPR20 iterator begin() { return _data.begin(); }
489
490 [[nodiscard]] _MSTD_CONSTEXPR20 const_iterator begin() const { return _data.begin(); }
491
492 [[nodiscard]] _MSTD_CONSTEXPR20 const_iterator cbegin() const { return _data.cbegin(); }
493
494 [[nodiscard]] _MSTD_CONSTEXPR20 iterator end() { return _data.end(); }
495
496 [[nodiscard]] _MSTD_CONSTEXPR20 const_iterator end() const { return _data.end(); }
497
498 [[nodiscard]] _MSTD_CONSTEXPR20 const_iterator cend() const { return _data.cend(); }
499
500 [[nodiscard]] _MSTD_CONSTEXPR20 reverse_iterator rbegin() { return _data.rbegin(); }
501
502 [[nodiscard]] _MSTD_CONSTEXPR20 const_reverse_iterator rbegin() const { return _data.rbegin(); }
503
504 [[nodiscard]] _MSTD_CONSTEXPR20 reverse_iterator rend() { return _data.rend(); }
505
506 [[nodiscard]] _MSTD_CONSTEXPR20 const_reverse_iterator rend() const { return _data.rend(); }
507
508 [[nodiscard]] _MSTD_CONSTEXPR20 const_reverse_iterator crbegin() const { return _data.crbegin(); }
509
510 [[nodiscard]] _MSTD_CONSTEXPR20 const_reverse_iterator crend() const { return _data.crend(); }
511
512 [[nodiscard]] _MSTD_CONSTEXPR20 value_type& operator[](const size_type id) { return at(id); }
513
514 [[nodiscard]] _MSTD_CONSTEXPR20 const value_type& operator[](const size_type id) const { return at(id); }
515
516 [[nodiscard]] value_type* data() { return _data.data(); }
517
518 [[nodiscard]] const value_type* data() const { return _data.data(); }
519
520 [[nodiscard]] _MSTD_CONSTEXPR20 bool operator==(const stable_vector& other) const
522 = default;
523 #else
524 {
525 return _data == other._data && _toId == other._toId && _toData == other._toData;
526 }
527 #endif
528
529 [[nodiscard]] _MSTD_CONSTEXPR20 bool operator!=(const stable_vector& other) const
531 = default;
532 #else
533 {
534 return !(*this == other);
535 }
536 #endif
537
539 [[nodiscard]] _MSTD_CONSTEXPR20 auto operator<=>(const stable_vector& other) const = default;
540 #else
541 [[nodiscard]] _MSTD_CONSTEXPR20 bool operator<(const stable_vector& other) const {
542 return _data < other._data && _toId < other._toId && _toData < other._toData;
543 }
544
545 [[nodiscard]] _MSTD_CONSTEXPR20 bool operator<=(const stable_vector& other) const {
546 return _data <= other._data && _toId <= other._toId && _toData <= other._toData;
547 }
548
549 [[nodiscard]] _MSTD_CONSTEXPR20 bool operator>(const stable_vector& other) const {
550 return _data > other._data && _toId > other._toId && _toData > other._toData;
551 }
552
553 [[nodiscard]] _MSTD_CONSTEXPR20 bool operator>=(const stable_vector& other) const {
554 return _data >= other._data && _toId >= other._toId && _toData >= other._toData;
555 }
556 #endif
557 };
558} // namespace mstd
559
560 #endif
561#endif
#define mstd_assert(expression,...)
Definition assert.hpp:29
#define _MSTD_HAS_CXX17
Definition config.hpp:45
#define _MSTD_CONSTEXPR17
Definition config.hpp:76
#define _MSTD_HAS_CXX20
Definition config.hpp:52
#define _MSTD_REQUIRES(condition)
Definition config.hpp:86
#define _MSTD_TYPENAME17
Definition config.hpp:82
#define _MSTD_CONSTEXPR20
Definition config.hpp:84