19#ifndef MTCORE_RING_BUFFER_HPP
20#define MTCORE_RING_BUFFER_HPP
57 ensure(elements.head,
"NOT INITIALIZED");
65 ensure(elements.head,
"NOT INITIALIZED");
72 decltype(
auto)
iter()
noexcept {
73 ensure(elements.head,
"NOT INITIALIZED");
84 ensure(!head && !tail && !count,
"ALREADY INITIALIZED");
85 ensure(elements.head ==
nullptr && elements.size() == 0,
"ALREADY INITIALIZED!");
86 initCapacity = initCapacity ? initCapacity : 10;
88 if (allocRes.is_error()) {
89 return allocRes.error();
105 ensure(!head && !tail && !count,
"ALREADY INITIALIZED");
106 ensure(elements.head ==
nullptr && elements.size() == 0,
"ALREADY INITIALIZED!");
112 if (allocRes.is_error()) {
113 return allocRes.error();
116 elements = *allocRes;
117 for (
const auto &elem:
init) {
131 elements.head =
nullptr;
141 ensure(elements.head,
"NOT INITIALIZED");
146 ensure(elements.head,
"NOT INITIALIZED");
151 [[nodiscard]]
size_t size() const noexcept {
return count; }
153 [[nodiscard]]
size_t capacity() const noexcept {
return elements.size(); }
156 [[nodiscard]]
const T &
at(
size_t i)
const noexcept {
157 ensure(elements.head,
"NOT INITIALIZED");
158 ensure(i < count,
"OUT OF BOUNDS ACCESS");
159 auto index = i + head;
160 if (index >= elements.size()) {
161 index -= elements.size();
163 return elements[index];
167 T &
at(
size_t i)
noexcept {
168 ensure(elements.head,
"NOT INITIALIZED");
169 ensure(i < count,
"OUT OF BOUNDS ACCESS");
170 auto index = i + head;
171 if (index >= elements.size()) {
172 index -= elements.size();
174 return elements[index];
179 ensure(elements.head,
"NOT INITIALIZED");
181 if (
auto err = grow_if_needed(alloc); err.is_error()) {
192 ensure(elements.head,
"NOT INITIALIZED");
194 if (
auto err = grow_if_needed(alloc); err.is_error()) {
205 ensure(elements.head,
"NOT INITIALIZED");
207 if (count >= elements.size()) {
211 elements[tail++] = elem;
213 if (tail == elements.size()) {
220 const auto newSize = std::max(
static_cast<size_t>(1), count);
222 if (newBufferRes.is_error()) {
223 return newBufferRes.error();
225 auto newBuffer = *newBufferRes;
227 for (
size_t i = 0, curHead = head; i < newBuffer.size() && i < count; ++i) {
230 if (curHead == elements.size()) {
234 std::swap(newBuffer[i], elements[curHead]);
237 tail = elements.size();
240 elements = newBuffer;
247 ensure(elements.head,
"NOT INITIALIZED");
249 if (count >= elements.size()) {
253 auto new_head = head;
255 new_head = elements.size() - 1;
260 elements[new_head] = elem;
268 ensure(elements.head,
"NOT INITIALIZED");
274 auto &res = elements[head++];
277 if (head == elements.size()) {
285 ensure(elements.head,
"NOT INITIALIZED");
291 auto new_tail = tail;
293 new_tail = elements.size() - 1;
299 auto res = elements[tail];
309 if (count < elements.
size()) {
313 size_t newSize = elements.len + ((elements.len + 1) / 2);
314 ensure(newSize >
size(),
"FAILED TO GROW RING BUFFER");
315 auto newElemAllocRes = alloc.
create_many<T>(newSize);
316 if (newElemAllocRes.is_error()) {
317 return newElemAllocRes.error();
320 auto newElems = *newElemAllocRes;
321 for (
size_t i = 0, curHead = head; i < elements.size(); ++i) {
324 if (curHead == elements.size()) {
328 std::swap(newElems[i], elements[curHead]);
331 tail = elements.size();
355 template<
typename T,
size_t Capacity = 10>
358 std::array<T, Capacity> elements;
392 void init(std::initializer_list<T> list) {
393 ensure(list.size() < Capacity,
"TOO MANY ELEMENTS PROVIDED");
394 for (
const auto &e: list) {
404 [[nodiscard]]
const T &
operator[](
size_t i)
const noexcept {
return at(i); }
416 [[nodiscard]]
size_t size() const noexcept {
return count; }
423 [[nodiscard]]
const T &
at(
size_t i)
const noexcept {
424 ensure(i < count,
"OUT OF BOUNDS ACCESS");
425 auto index = i + head;
426 if (index >= elements.size()) {
427 index -= elements.size();
429 return elements[index];
437 [[nodiscard]] T &
at(
size_t i)
noexcept {
438 ensure(i < count,
"OUT OF BOUNDS ACCESS");
439 auto index = i + head;
440 if (index >= elements.size()) {
441 index -= elements.size();
443 return elements[index];
452 if (count >= elements.size()) {
456 elements[tail++] = elem;
458 if (tail == elements.size()) {
470 if (count >= elements.size()) {
474 auto new_head = head;
476 new_head = elements.size() - 1;
481 elements[new_head] = elem;
496 auto res = std::move(elements[head++]);
499 if (head == elements.size()) {
514 auto new_tail = tail;
516 new_tail = elements.size() - 1;
522 auto res = elements[tail];
ValIter< T > val(const T &r)
Generic value iterator that uses the operator[] and incrementing indexes to iterate over a collection...
constexpr auto nullopt
Placeholder value for an empty Optional.
ConstPtrIter< T > const_ptr(const T &r)
Generic constant pointer iterator that uses the operator[] and incrementing indexes to iterate over a...
PtrIter< T > ptr(T &r)
Generic pointer iterator that uses the operator[] and incrementing indexes to iterate over a collecti...
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
#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.
Error< Underlying > error(Underlying err)
Creates an error.
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...
Represents a ring buffer with static memory allocation Can be used as a FIFO or LIFO queue Allows mem...
void init(std::initializer_list< T > list)
Initializes a new fixed ring buffer with specific elements.
size_t size() const noexcept
Gets current size of the buffer.
void init()
Initializes a new fixed ring buffer.
Optional< T > pop_front()
Removes an element from the front.
Optional< T > pop_back()
Removes an element from the back.
Result< void, CollectionAddNoAllocationError > push_front(const T &elem)
Adds an element to the front of the buffer.
Result< void, CollectionAddNoAllocationError > push_back(const T &elem)
Adds an element to the back of the buffer.
decltype(auto) iter() noexcept
Gets an iterator that returns copies of values.
T & at(size_t i) noexcept
Gets an element at a specific index.
decltype(auto) ptr_iter() const noexcept
Gets a constant pointer iterator to values.
decltype(auto) ptr_iter() noexcept
Gets a pointer iterator to values.
const T & operator[](size_t i) const noexcept
Gets an element at a specific index.
const T & at(size_t i) const noexcept
Gets an element at a specific index.
T & operator[](size_t i) noexcept
Gets an element at a specific index.
Represents a value that may or may not exist (an "Optional" value) Similar concept to std::optional,...
Represents a Result that may have an error (error code) or a success value A type of "void" means the...
Represents a ring buffer with dynamically allocated memory Can be used as a FIFO or LIFO queue Allows...
decltype(auto) ptr_iter() noexcept
Gets iterator over pointers to elements.
Result< void, AllocationError > init(Allocator &alloc, std::initializer_list< T > init, size_t capacity=0) noexcept
Initializes an ring buffer with specified elements.
const T & operator[](size_t i) const noexcept
Access element.
Result< void, CollectionAddNoAllocationError > push_front(const T &elem)
Add element to front of buffer.
const T & at(size_t i) const noexcept
Access element.
decltype(auto) iter() noexcept
Gets iterator over values.
size_t size() const noexcept
Get size.
size_t capacity() const noexcept
Get capacity.
Optional< T > pop_front()
Removes and returns element from front.
decltype(auto) ptr_iter() const noexcept
Gets iterator over const pointers to elements.
T & at(size_t i) noexcept
Access element.
Result< void, AllocationError > push_back(Allocator &alloc, const T &elem)
Add element to back of buffer.
Optional< T > pop_back()
Removes and returns element from back.
Result< void, CollectionAddNoAllocationError > push_back(const T &elem)
Add element to back of buffer.
T & operator[](size_t i) noexcept
Access element.
Result< void, AllocationError > push_front(Allocator &alloc, const T &elem)
Add element to front of buffer.
Result< void, AllocationError > shrink_to_fit(Allocator &alloc)
void deinit(Allocator &alloc)
Cleans up any memory held by ring buffer.
Result< void, AllocationError > init(Allocator &alloc, size_t initCapacity=10) noexcept
Initializes an empty ArrayList with an initial capacity.
A Slice which is just a pointer + length Accessing elements through the array operator will do bounds...
constexpr size_t size() const noexcept
Gets the size of a Slice.