MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
array_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
23
24#ifndef MTCORE_ARRAY_LIST_HPP
25#define MTCORE_ARRAY_LIST_HPP
26
27#include "../alloc.hpp"
28#include "../traits.hpp"
29#include "slice.hpp"
30
34namespace mtcore {
45 template<typename T>
46 struct ArrayList {
47 private:
48 Slice<T> elements = {};
49 size_t count = 0;
50
51 public:
52 using Elem = T;
53
58 [[nodiscard]] decltype(auto) ptr_iter() noexcept { return iter::ptr(*this); }
63 [[nodiscard]] decltype(auto) ptr_iter() const noexcept { return iter::const_ptr(*this); }
68 [[nodiscard]] decltype(auto) iter() const noexcept { return iter::val(*this); }
69
76 [[nodiscard]] Result<void, AllocationError> init(Allocator &alloc, size_t initCapacity = 10) noexcept {
77 ensure(elements.head == nullptr && elements.size() == 0, "ALREADY INITIALIZED!");
78 initCapacity = initCapacity ? initCapacity : 10;
79 auto allocRes = alloc.create_many<T>(initCapacity);
80 if (allocRes.is_error()) {
81 return allocRes.error();
82 }
83
84 elements = *allocRes;
85 return success();
86 }
87
96 [[nodiscard]] Result<void, AllocationError> init(Allocator &alloc, std::initializer_list<T> init,
97 size_t capacity = 0) noexcept {
98 ensure(elements.head == nullptr && elements.size() == 0, "ALREADY INITIALIZED!");
99 if (capacity < init.size()) {
100 capacity = init.size();
101 }
102 capacity = capacity ? capacity : 10;
103 auto allocRes = alloc.create_many<T>(init.size());
104 if (allocRes.is_error()) {
105 return allocRes.error();
106 }
107
108 elements = *allocRes;
109 size_t index = 0;
110 for (const auto &elem: init) {
111 elements[index++] = elem;
112 }
113 count = index;
114
115 return success();
116 }
117
122 void deinit(Allocator &alloc) noexcept {
123 if (elements.head != nullptr) {
124 alloc.destroy_many(elements);
125 elements.head = nullptr;
126 }
127 elements.len = 0;
128 }
129
139 const Slice<std::add_const_t<T>> &elems) noexcept {
140 ensure(elements.head, "NOT INITIALIZED");
141 if (count + elems.size() >= elements.len) {
142 size_t newSize = elements.len + std::max(((elements.len + 1) / 2), elems.size());
143 if (auto res = reserve(alloc, newSize); res.is_error()) {
144 return res;
145 }
146 }
147
148 ensure(push(elems).is_success(), "SOMEHOW GIVING MORE SPACE DIDN'T WORK");
149 return success();
150 }
151
161 const Slice<std::remove_const_t<T>> &elems) noexcept {
162 return push(alloc, elems.to_const());
163 }
164
173 [[nodiscard]] Result<void, AllocationError> push(Allocator &alloc, const T &elem) noexcept {
174 ensure(elements.head, "NOT INITIALIZED");
175 if (count >= elements.len) {
176 size_t newSize = elements.len + ((elements.len + 1) / 2);
177 if (auto res = reserve(alloc, newSize); res.is_error()) {
178 return res;
179 }
180 }
181
182 ensure(push(elem).is_success(), "SOMEHOW GIVING MORE SPACE DIDN'T WORK");
183 return success();
184 }
185
190 [[nodiscard]] size_t size() const noexcept {
191 ensure(count <= elements.len, "COUNT EXCEEDS SLICE SIZE!");
192 return count;
193 }
194
199 [[nodiscard]] size_t capacity() const noexcept {
200 ensure(count <= elements.len, "COUNT EXCEEDS SLICE SIZE!");
201 return elements.len;
202 }
203
210 [[nodiscard]] Result<void, AllocationError> reserve(Allocator &alloc, const size_t newSize) noexcept {
211 ensure(elements.head, "NOT INITIALIZED");
212 ensure(count <= elements.len, "COUNT EXCEEDS SLICE SIZE!");
213 if (newSize <= capacity()) {
214 return success();
215 }
216 ensure(newSize > size(), "FAILED TO GROW");
217 auto newElemAllocRes = alloc.create_many<T>(newSize);
218 if (newElemAllocRes.is_error()) {
219 return newElemAllocRes.error();
220 }
221
222 auto newElems = *newElemAllocRes;
223 for (size_t i = 0; i < elements.size(); ++i) {
224 std::swap(newElems[i], elements[i]);
225 }
226 alloc.destroy_many(elements);
227 elements = newElems;
228 return success();
229 }
230
237 ensure(elements.head, "NOT INITIALIZED");
238 ensure(count <= elements.len, "COUNT EXCEEDS SLICE SIZE!");
239 auto newSize = size();
240 if (newSize == capacity()) {
241 return success();
242 }
243
244 ensure(newSize < size(), "FAILED TO SHRINK");
245 auto newElemAllocRes = alloc.create_many<T>(newSize);
246 if (!newElemAllocRes) {
247 return newElemAllocRes;
248 }
249
250 auto newElems = *newElemAllocRes;
251 for (size_t i = 0; i < elements.size(); ++i) {
252 std::swap(newElems[i], elements[i]);
253 }
254 alloc.destroy_many(elements);
255 elements = newElems;
256 return success();
257 }
258
266 push(const Slice<std::remove_const_t<T>> &elems) noexcept {
267 return push(elems.to_const());
268 }
269
276 [[nodiscard]] Result<void, CollectionAddNoAllocationError> push(const Slice<std::add_const_t<T>> &elems) noexcept {
277 ensure(elements.head, "NOT INITIALIZED");
278 if (count + elems.size() > elements.len) {
280 }
281
282 for (size_t i = 0; i < elems.size(); ++i) {
283 elements[count++] = elems[i];
284 }
285
286 return success();
287 }
288
295 [[nodiscard]] Result<void, CollectionAddNoAllocationError> push(const T &elem) noexcept {
296 ensure(elements.head, "NOT INITIALIZED");
297 if (count >= elements.len) {
299 }
300
301 elements[count++] = elem;
302 return success();
303 }
304
311 [[nodiscard]] Result<void, AllocationError> unshift(const T &elem) noexcept {
312 ensure(elements.head, "NOT INITIALIZED");
313 // add the new element to the end
314 if (auto err = push(elem)) {
315 return err;
316 }
317
318 // then bubble the last element down
319 for (size_t i = 0; i + 1 < count; ++i) {
320 const auto upper = count - 1 - i;
321 const auto lower = count - 2 - i;
322 std::swap(elements[upper], elements[lower]);
323 }
324 return success();
325 }
326
334 [[nodiscard]] Result<void, AllocationError> unshift(Allocator &alloc, const T &elem) noexcept {
335 ensure(elements.head, "NOT INITIALIZED");
336 // add the new element to the end
337 if (auto err = push(alloc, elem)) {
338 return err;
339 }
340
341 // then bubble the last element down
342 for (size_t i = 0; i + 1 < count; ++i) {
343 const auto upper = count - 1 - i;
344 const auto lower = count - 2 - i;
345 std::swap(elements[upper], elements[lower]);
346 }
347 return success();
348 }
349
354 Optional<T> shift() noexcept {
355 ensure(elements.head, "NOT INITIALIZED");
356 if (count == 0) {
357 return nullopt;
358 }
359 return shift_remove(0);
360 }
361
367 Optional<T> pop() noexcept {
368 ensure(elements.head, "NOT INITIALIZED");
369 if (count == 0) {
370 return nullopt;
371 }
372 --count;
373 return elements[count];
374 }
375
381 [[nodiscard]] T &at(size_t index) noexcept {
382 ensure(elements.head, "NOT INITIALIZED");
383 return elements[index];
384 }
385
391 [[nodiscard]] const T &at(size_t index) const noexcept {
392 ensure(elements.head, "NOT INITIALIZED");
393 return elements[index];
394 }
395
401 T &operator[](size_t index) noexcept {
402 ensure(elements.head, "NOT INITIALIZED");
403 return elements[index];
404 }
405
411 const T &operator[](size_t index) const noexcept {
412 ensure(elements.head, "NOT INITIALIZED");
413 return elements[index];
414 }
415
424 T swap_remove(size_t index) noexcept {
425 ensure(elements.head, "NOT INITIALIZED");
426 ensure(index < count, "ARRAY OUT OF BOUNDS DETECTED!");
427 auto end = count - 1;
428 if (index != end) {
429 std::swap(elements[end], elements[index]);
430 }
431
432 auto popRes = pop();
433 ensure(popRes.has_value(), "COULDN'T REMOVE ELMENT FROM NON-EMPTY LIST");
434 return *popRes;
435 }
436
445 T shift_remove(size_t index) noexcept {
446 ensure(elements.head, "NOT INITIALIZED");
447 ensure(index < count, "ARRAY OUT OF BOUNDS DETECTED!");
448
449 for (size_t i = index; i + 1 < count; ++i) {
450 std::swap(elements[index], elements[index + 1]);
451 }
452 auto popRes = pop();
453 ensure(popRes, "COULDN'T REMOVE ELMENT FROM NON-EMPTY LIST");
454 return *popRes;
455 }
456
460 [[nodiscard]] Slice<T> slice() { return elements.sub(0, count); }
461
465 [[nodiscard]] Slice<std::add_const_t<T>> slice() const { return elements.sub(0, count).to_const(); }
466 };
467
468 static_assert(is_mutable_addressable<ArrayList<int>>, "ARRAY LIST IS NOT MUTABLE ADDRESSABLE");
469 static_assert(is_iterator<decltype(ArrayList<int>{}.iter())>, "ITER IS NOT VALID");
470 static_assert(is_iterator<decltype(ArrayList<int>{}.ptr_iter())>, "ITER IS NOT VALID");
471} // namespace mtcore
472
473#endif // MTCORE_ARRAY_LIST_HPP
ValIter< T > val(const T &r)
Generic value iterator that uses the operator[] and incrementing indexes to iterate over a collection...
Definition iter.hpp:114
constexpr auto nullopt
Placeholder value for an empty Optional.
Definition optional.hpp:409
ConstPtrIter< T > const_ptr(const T &r)
Generic constant pointer iterator that uses the operator[] and incrementing indexes to iterate over a...
Definition iter.hpp:128
PtrIter< T > ptr(T &r)
Generic pointer iterator that uses the operator[] and incrementing indexes to iterate over a collecti...
Definition iter.hpp:101
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
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_iterator
Boolean check for if something has the trait Iterator.
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_*...
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...
Array list is a growable, dynamic array with elements inside Elements are stored in an array,...
T shift_remove(size_t index) noexcept
Removes the element at an index (must be present) Does preserve ordering Will shift all remaining ele...
Result< void, AllocationError > push(Allocator &alloc, const T &elem) noexcept
Pushes an item onto an array list.
Slice< T > slice()
Gets a slice over the underlying elements.
T & operator[](size_t index) noexcept
Get element at an index.
Result< void, CollectionAddNoAllocationError > push(const Slice< std::remove_const_t< T > > &elems) noexcept
Pushes an item onto the end of the ArrayList Will fail if there is not enough room.
Result< void, AllocationError > init(Allocator &alloc, size_t initCapacity=10) noexcept
Initializes an empty ArrayList with an initial capacity.
decltype(auto) iter() const noexcept
Gets iterator over values.
Optional< T > pop() noexcept
Pops the last element and returns it Will return an error if there is no element.
T & at(size_t index) noexcept
Get element at an index.
decltype(auto) ptr_iter() noexcept
Gets iterator over pointers to elements.
const T & at(size_t index) const noexcept
Get element at an index.
Result< void, CollectionAddNoAllocationError > push(const T &elem) noexcept
Pushes an item onto the end of the ArrayList Will fail if there is not enough room.
size_t capacity() const noexcept
Array list capacity.
void deinit(Allocator &alloc) noexcept
Cleans up any memory held by array list.
Slice< std::add_const_t< T > > slice() const
Gets a slice over the underlying elements.
Result< void, AllocationError > init(Allocator &alloc, std::initializer_list< T > init, size_t capacity=0) noexcept
Initializes an array list with specified elements.
const T & operator[](size_t index) const noexcept
Get element at an index.
Result< void, AllocationError > unshift(const T &elem) noexcept
Adds an element to the front, and shifts all other elements down by 1 If there is not enough room,...
Result< void, AllocationError > push(Allocator &alloc, const Slice< std::add_const_t< T > > &elems) noexcept
Pushes a list of items onto an array list.
T swap_remove(size_t index) noexcept
Removes the element at an index (must be present) Does NOT preserve ordering Will swap the removed el...
Result< void, AllocationError > unshift(Allocator &alloc, const T &elem) noexcept
Adds an element to the front, and shifts all other elements down by 1 If there is not enough room,...
Result< void, AllocationError > reserve(Allocator &alloc, const size_t newSize) noexcept
Will grow the capacity to be at least newSize large.
Optional< T > shift() noexcept
Removes the first element and shifts all remaining elements down.
decltype(auto) ptr_iter() const noexcept
Gets iterator over const pointers to elements.
size_t size() const noexcept
Gets the size of an array list.
Result< void, CollectionAddNoAllocationError > push(const Slice< std::add_const_t< T > > &elems) noexcept
Pushes an item onto the end of the ArrayList Will fail if there is not enough room.
Result< void, AllocationError > shrink_to_fit(Allocator &alloc) noexcept
Will shrink the capacity to match the current size.
Result< void, AllocationError > push(Allocator &alloc, const Slice< std::remove_const_t< T > > &elems) noexcept
Pushes a list of items onto an array list.
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
A Slice which is just a pointer + length Accessing elements through the array operator will do bounds...
constexpr Slice sub(size_t start) const noexcept
Gets a sub Slice from start.
constexpr Slice< std::add_const_t< T > > to_const() const noexcept
Converts to a const Slice.