ona/source/coral/stack.cpp

127 lines
3.6 KiB
C++
Executable File

export module coral.stack;
import coral;
// Collections.
export namespace coral {
/**
* Result codes used when pushing elements to a stack.
*
* [push_result::ok] indicates that the push operation was successful.
*
* [push_result::out_of_memory] alerts that the memory required to perform the push operation failed.
*/
enum class [[nodiscard]] push_result {
ok,
out_of_memory,
};
/**
* Last-in-first-out contiguous sequence of `element` values.
*
* *Note*: the [allocator] used by the stack must remain valid for the duration of the stack lifetime.
*/
template<typename element> struct stack {
stack(allocator & element_allocator) : element_allocator{element_allocator} {
this->filled_elements = {};
}
~stack() {
for (element & e : this->filled_elements) e.~element();
this->element_allocator.deallocate(this->filled_elements.pointer);
}
/**
* Returns `true` if there are no elements in the stack, otherwise `false`.
*/
bool is_empty() const {
return this->filled_elements.length == 0;
}
/**
* Attempts to append `value` to the top of the stack.
*
* The returned [push_result] indicates whether the operation was successful or not.
*
* If the returned [push_result] is anything but [push_result::ok], the stack will be be left in an empty but
* valid state.
*
* *Note* [push_all] is recommended when appending many values at once.
*/
push_result push(element const & value) {
if (this->filled_elements.length == this->element_capacity) {
constexpr usize min_size {2};
push_result const result {this->reserve(this->element_capacity + (static_cast<usize>(this->element_capacity == 0) * min_size))};
if (result != push_result::ok) return result;
}
this->filled_elements[this->filled_elements.length] = value;
this->filled_elements.length += 1;
return push_result::ok;
}
/**
* Attempts to append `elements` to the top of the stack.
*
* The returned [push_result] indicates whether the operation was successful or not.
*
* If the returned [push_result] is anything but [push_result::ok], the stack will be left in an empty but valid
* state.
*
* *Note* [push] is recommended when appending singular values.
*/
push_result push_all(slice<element const> const & values) {
usize const updated_fill {this->filled + values.length};
if (updated_fill >= this->elements.length) {
push_result const result {this->reserve(values.length)};
if (result != push_result::ok) return result;
}
for (usize i {0}; i < values.length; i += 1) this->elements[this->filled + i] = values[i];
this->filled = updated_fill;
return push_result::ok;
}
/**
* Attempts to reserve `requested_capacity` number of elements additional space on the stack.
*
* The returned [push_result] indicates whether the operation was successful or not.
*
* If the returned [push_result] is anything but [push_result::ok], the stack will be left in an empty but valid
* state.
*/
push_result reserve(usize requested_capacity) {
usize const total_capacity {this->elements_filled + requested_capacity};
u8 * const buffer {this->element_allocator.reallocate(reinterpret_cast<u8 *>(
this->elements_filled.pointer), sizeof(element) * total_capacity)};
if (buffer == nullptr) {
this->element_capacity = 0;
this->filled_elements = {};
return push_result::out_of_memory;
}
this->filled_elements.pointer = reinterpret_cast<element *>(buffer);
this->element_capacity = total_capacity;
return push_result::ok;
}
private:
allocator & element_allocator;
usize element_capacity{0};
slice<element> filled_elements;
};
}