ona/source/core/sequence.cpp

119 lines
3.0 KiB
C++

export module core.sequence;
import core;
export namespace core {
enum class reserve_error {
out_of_memory,
};
template<typename element> struct sequence {
sequence() = default;
sequence(sequence const &) = delete;
virtual ~sequence() {};
virtual slice<element const> as_slice() const = 0;
virtual optional<reserve_error> append(slice<element const> const & appended_elements) = 0;
};
template<typename element, usize initial_capacity = 1> struct stack : public sequence<element> {
stack(allocator * buffer_allocator) : filled{0},
buffer{0}, buffer_allocator{buffer_allocator} {
this->elements = this->buffer;
}
~stack() override {
if (this->elements.pointer != this->buffer)
this->buffer_allocator->deallocate(this->elements.pointer);
}
slice<element const> as_slice() const override {
return this->elements;
}
optional<reserve_error> push(element const & pushed_element) {
if (this->filled == this->elements.length) {
optional const maybe_error = this->reserve(this->elements.length);
if (maybe_error.has_value()) return maybe_error;
}
this->elements[this->filled] = pushed_element;
this->filled += 1;
return {};
}
optional<reserve_error> append(slice<element const> const & pushed_elements) override {
usize const updated_fill = this->filled + pushed_elements.length;
if (updated_fill >= this->elements.length) {
optional const maybe_error = this->reserve(max(this->elements.length, updated_fill));
if (maybe_error.has_value()) return maybe_error;
}
for (usize i = 0; i < pushed_elements.length; i += 1)
this->elements[this->filled + i] = pushed_elements[i];
this->filled = updated_fill;
return {};
}
optional<reserve_error> reserve(usize capacity) {
usize const requested_capacity = this->elements.length + capacity;
if (this->elements.pointer == this->buffer) {
u8 * const maybe_allocation = this->buffer_allocator->
reallocate(nullptr, sizeof(element) * requested_capacity);
if (maybe_allocation == nullptr) {
this->elements = {};
return reserve_error::out_of_memory;
}
this->elements = {reinterpret_cast<element *>(maybe_allocation), requested_capacity};
} else {
u8 * const maybe_allocation = this->buffer_allocator->reallocate(
reinterpret_cast<u8 *>(this->elements.pointer),
sizeof(element) * requested_capacity);
if (maybe_allocation == nullptr) {
this->elements = {};
return reserve_error::out_of_memory;
}
this->elements = {reinterpret_cast<element *>(maybe_allocation), requested_capacity};
}
return {};
}
private:
allocator * buffer_allocator;
usize filled;
slice<element> elements;
element buffer[initial_capacity];
};
struct sequence_writer : public writable {
sequence_writer(sequence<u8> * target_sequence) : writable{[target_sequence](slice<u8 const> const & buffer) -> optional<usize> {
optional const maybe_error = target_sequence->append(buffer);
if (maybe_error.has_value()) return {};
return buffer.length;
}} {}
};
}