MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
segmented_list.hpp
Go to the documentation of this file.
1/*
2
3Copyright 2025 Matthew Tolman
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16
17*/
18
19#ifndef MTCORE_STABLE_VEC_HPP
20#define MTCORE_STABLE_VEC_HPP
21
22#include "../alloc.hpp"
23#include "../core.hpp"
24#include "../traits.hpp"
25#include "slice.hpp"
26
27namespace mtcore {
44 template<typename T>
46 using Elem = T;
47
48 private:
49 struct Segment {
50 Slice<T> elements = {};
51 Segment *next = nullptr;
52 };
53
54 Segment start = {};
55 Segment *end = &start;
56 size_t endIndex = 0;
57 size_t len = 0;
58 size_t totalCapacity = 0;
59
60 public:
65 struct PtrIter {
66 size_t index = 0;
68 size_t segmentIndex = 0;
69 Segment *s;
70
76 if (s == nullptr || index >= r.size()) {
77 return nullopt;
78 }
79 if (s->elements.size() <= segmentIndex) {
80 s = s->next;
81 segmentIndex = 0;
82 }
83 if (s == nullptr) {
84 return nullopt;
85 }
86 ++index;
87 return &s->elements[segmentIndex++];
88 }
89 };
90
95 struct ConstPtrIter {
96 size_t index = 0;
98 size_t segmentIndex = 0;
99 const Segment *s;
100
106 if (s == nullptr || index >= r.size()) {
107 return nullopt;
108 }
109 if (s->elements.size() <= segmentIndex) {
110 s = s->next;
111 segmentIndex = 0;
112 }
113 if (s == nullptr) {
114 return nullopt;
115 }
116 ++index;
117 return &s->elements[segmentIndex++];
118 }
119 };
120
125 struct ConstIter {
126 size_t index = 0;
128 size_t segmentIndex = 0;
129 const Segment *s;
130
136 if (s == nullptr || index >= r.size()) {
137 return nullopt;
138 }
139 if (s->elements.size() <= segmentIndex) {
140 s = s->next;
141 segmentIndex = 0;
142 }
143 if (s == nullptr) {
144 return nullopt;
145 }
146 ++index;
147 return s->elements[segmentIndex++];
148 }
149 };
150
155 [[nodiscard]] PtrIter ptr_iter() noexcept {
156 ensure(start.elements.head, "NOT INITIALIZED");
157 return {0, *this, 0, &start};
158 }
159
164 [[nodiscard]] ConstPtrIter ptr_iter() const noexcept {
165 ensure(start.elements.head, "NOT INITIALIZED");
166 return {0, *this, 0, &start};
167 }
168
173 [[nodiscard]] ConstIter iter() const noexcept {
174 ensure(start.elements.head, "NOT INITIALIZED");
175 return {0, *this, 0, &start};
176 }
177
182 [[nodiscard]] size_t size() const noexcept { return len; }
183
190 [[nodiscard]] Result<void, AllocationError> init(Allocator &alloc, size_t initCapacity) {
191 ensure(initCapacity > 0, "NO INITIAL CAPACITY");
192 ensure(start.elements.head == nullptr, "ALREADY INITIALIZED");
193 ensure(start.elements.size() == 0, "ALREADY INITIALIZED");
194 ensure(start.next == nullptr, "ALREADY INITIALIZED");
195
196 auto allocRes = alloc.create_many<T>(initCapacity);
197 if (allocRes.is_error()) {
198 return allocRes.error();
199 }
200
201 start.elements = *allocRes;
202 totalCapacity = initCapacity;
203 return success();
204 }
205
212 [[nodiscard]] Result<void, AllocationError> init(Allocator &alloc, std::initializer_list<T> list) {
213 ensure(start.elements.head == nullptr, "ALREADY INITIALIZED");
214 ensure(list.size() > 0, "NO INITIAL CAPACITY");
215 ensure(!start.elements.head, "ALREADY INITIALIZED");
216 ensure(!start.elements.size(), "ALREADY INITIALIZED");
217 ensure(!start.next, "ALREADY INITIALIZED");
218
219 auto allocRes = alloc.create_many<T>(list.size());
220 if (allocRes.is_error()) {
221 return allocRes.error();
222 }
223 totalCapacity = list.size();
224
225 start.elements = *allocRes;
226
227 size_t i = 0;
228 for (const auto &elem: list) {
229 start.elements[i++] = elem;
230 }
231 len = list.size();
232 end = &start;
233
234 return success();
235 }
236
241 void deinit(Allocator &alloc) {
242 if (start.elements.head != nullptr) {
243 alloc.destroy_many(start.elements);
244 }
245 if (start.next != nullptr) {
246 Segment *cur = start.next;
247 while (cur) {
248 Segment *next = cur->next;
249 alloc.destroy_many(cur->elements);
250 alloc.destroy(cur);
251 cur = next;
252 }
253 }
254 totalCapacity = 0;
255 end = nullptr;
256 }
257
263 ensure(start.elements.head, "NOT INITIALIZED");
264 Segment *prev = &start;
265 Segment *cur = start.next;
266 end = &start;
267 size_t remaining = size();
268 while (cur) {
269 if (remaining > prev->elements.len) {
270 remaining -= prev->elements.len;
271 auto next = cur->next;
272 end = cur;
273 prev = cur;
274 cur = next;
275 }
276 else {
277 prev->next = nullptr;
278 while (cur) {
279 Segment *next = cur->next;
280 totalCapacity -= cur->elements.size();
281 alloc.destroy_many(cur->elements);
282 alloc.destroy(cur);
283 cur = next;
284 }
285 break;
286 }
287 }
288 }
289
295 [[nodiscard]] Result<void, CollectionAddNoAllocationError> push(const T &elem) noexcept {
296 ensure(start.elements.head != nullptr, "NOT INITIALIZED");
297
298 Segment *cur = end;
299 ensure(cur);
300 if (endIndex < cur->elements.size()) {
301 mtdefer { ++len; };
302 cur->elements[endIndex++] = elem;
303 return success();
304 }
306 }
307
314 [[nodiscard]] Result<void, AllocationError> push(Allocator &alloc, const T &elem) noexcept {
315 ensure(start.elements.head != nullptr, "NOT INITIALIZED");
316
317 Segment *cur = end;
318 if (endIndex < cur->elements.size()) {
319 mtdefer { ++len; };
320 cur->elements[endIndex++] = elem;
321 return success();
322 }
323
324 auto allocSegRes = alloc.create<Segment>();
325
326 if (allocSegRes.is_error()) {
327 return allocSegRes.error();
328 }
329
330 auto allocElemRes = alloc.create_many<T>(cur->elements.size() + cur->elements.size() / 2);
331 if (allocElemRes.is_error()) {
332 alloc.destroy(*allocSegRes);
333 return allocElemRes.error();
334 }
335 mtdefer { ++len; };
336 cur->next = *allocSegRes;
337 end = cur->next;
338 endIndex = 0;
339 cur = cur->next;
340 cur->elements = *allocElemRes;
341 cur->elements[endIndex++] = elem;
342 end = cur;
343 totalCapacity += cur->elements.size();
344 return success();
345 }
346
352 [[nodiscard]] const T &operator[](size_t index) const noexcept {
353 ensure(start.elements.head != nullptr, "NOT INITIALIZED");
354 return at(index);
355 }
356
362 [[nodiscard]] T &operator[](size_t index) noexcept {
363 ensure(start.elements.head != nullptr, "NOT INITIALIZED");
364 return at(index);
365 }
366
372 [[nodiscard]] const T &at(size_t index) const noexcept {
373 ensure(start.elements.head != nullptr, "NOT INITIALIZED");
374 ensure(index < len, "OUT OF BOUNDS ACCESS");
375 const Segment *cur = &start;
376 while (cur) {
377 if (index < cur->elements.len) {
378 return cur->elements[index];
379 }
380 index -= cur->elements.len;
381 cur = cur->next;
382 }
383 unreachable("OUT OF BOUNDS ACCESS");
384 }
385
391 [[nodiscard]] T &at(size_t index) noexcept {
392 ensure(start.elements.head != nullptr, "NOT INITIALIZED");
393 ensure(index < len, "OUT OF BOUNDS ACCESS");
394 Segment *cur = &start;
395 while (cur) {
396 if (index < cur->elements.size()) {
397 return cur->elements[index];
398 }
399 index -= cur->elements.size();
400 cur = cur->next;
401 }
402 unreachable("OUT OF BOUNDS ACCESS");
403 }
404
409 [[nodiscard]] bool operator==(const SegmentedList &other) {
410 ensure(start.elements.head != nullptr, "NOT INITIALIZED");
411 ensure(other.start.elements.head != nullptr, "NOT INITIALIZED");
412 if (len != other.len) {
413 return false;
414 }
415 for (size_t i = 0; i < len; ++i) {
416 if (at(i) != other.at(i)) {
417 return false;
418 }
419 }
420 return true;
421 }
422
427 [[nodiscard]] bool operator!=(const SegmentedList &other) { return !(*this == other); }
428
432 void pop() {
433 ensure(start.elements.head != nullptr, "NOT INITIALIZED");
434 if (len > 0) {
435 --len;
436 }
437 }
438
439 [[nodiscard]] size_t capacity() const { return totalCapacity; }
440 };
441
442 static_assert(is_mutable_addressable<SegmentedList<int>>, "segmented_list<int> is not mutable addressable");
443} // namespace mtcore
444
445#endif // MTCORE_STABLE_VEC_HPP
constexpr auto nullopt
Placeholder value for an empty Optional.
Definition optional.hpp:409
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
#define unreachable(...)
Marks code as unreachable.
#define mtdefer
Defer statement that will mtdefer execution until the scope is left, at which point the code will run...
Success< void > success()
Creates a successful void Result object.
Definition result.hpp:398
Error< Underlying > error(Underlying err)
Creates an error.
Definition result.hpp:425
constexpr bool is_mutable_addressable
Boolean check for if something has the trait Addressable.
Core library for C++ with Zig-related functionality.
Represents a memory allocator Exact behavior depends on the underlying VTable used Should use the a_*...
void destroy_many(Slice< T > s)
Destroys objects allocated by this allocator by calling the destructors and freeing memory.
Result< Slice< T >, AllocationError > create_many(size_t count=1)
Allocates some block of memory with an allocator with a known type Will call default constructor on t...
void destroy(T *&ptr)
Destroys an object allocated by this allocator by calling the destructor and freeing memory.
Represents a value that may or may not exist (an "Optional" value) Similar concept to std::optional,...
Definition optional.hpp:235
Represents a Result that may have an error (error code) or a success value A type of "void" means the...
Definition result.hpp:170
Const reference for mutating elements Using custom iterator for speed.
Optional< T > next()
Gets next item.
Pointer iterator for mutating elements Using custom iterator for speed.
Optional< const T * > next()
Gets next item.
Pointer iterator for mutating elements Using custom iterator for speed.
Optional< T * > next()
Gets next item.
Segmented list where each segment contains multiple nodes Allows growing the list without having to i...
T & at(size_t index) noexcept
Access element at index.
ConstPtrIter ptr_iter() const noexcept
Const iterator that gives const element pointers.
Result< void, CollectionAddNoAllocationError > push(const T &elem) noexcept
Pushes a new element.
Result< void, AllocationError > init(Allocator &alloc, std::initializer_list< T > list)
Initializes a segmented list.
const T & operator[](size_t index) const noexcept
Access element at index.
void deinit(Allocator &alloc)
Cleans up memory tied to segmented list.
Result< void, AllocationError > push(Allocator &alloc, const T &elem) noexcept
Pushes a new element.
size_t size() const noexcept
Gets the size of the list.
bool operator!=(const SegmentedList &other)
void pop()
remove last element
Result< void, AllocationError > init(Allocator &alloc, size_t initCapacity)
Initializes a segmented list.
PtrIter ptr_iter() noexcept
Mutable iterator that gives element pointers.
const T & at(size_t index) const noexcept
Access element at index.
T & operator[](size_t index) noexcept
Access element at index.
void drop_unused_segments(Allocator &alloc)
Lowers memory footprint by dropping unused segments.
bool operator==(const SegmentedList &other)
ConstIter iter() const noexcept
Const iterator that gives element references.
A Slice which is just a pointer + length Accessing elements through the array operator will do bounds...