127 lines
3.6 KiB
C++
Executable File
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;
|
|
};
|
|
}
|