ona/source/coral/sequence.cpp

241 lines
7.3 KiB
C++
Raw Normal View History

2023-02-19 17:50:29 +01:00
export module coral.sequence;
2023-02-18 04:34:40 +01:00
2023-02-19 17:50:29 +01:00
import coral;
2023-02-18 04:34:40 +01:00
2023-02-22 21:53:58 +01:00
// Collections.
2023-02-19 17:50:29 +01:00
export namespace coral {
2023-02-18 20:40:12 +01:00
/**
2023-02-22 21:53:58 +01:00
* Result codes used by [contiguous_range]-derived types when they are appended to in any way.
2023-02-18 20:40:12 +01:00
*
* [append_result::ok] indicates that an append operation was successful.
*
* [append_result::out_of_memory] alerts that the memory required to perform the append
* operation failed.
*/
enum class [[nodiscard]] append_result {
ok,
2023-02-18 04:34:40 +01:00
out_of_memory,
};
2023-02-18 20:40:12 +01:00
/**
2023-02-22 21:53:58 +01:00
* Base type for all collections which store their elements as a single block of contiguous
* memory.
2023-02-18 20:40:12 +01:00
*
* Sequences are any data structure which owns a linear, non-unique set of elements which may
* be queried and/or mutated.
*/
2023-02-22 21:53:58 +01:00
template<typename element> struct contiguous_range {
virtual ~contiguous_range() {};
2023-02-18 04:34:40 +01:00
2023-02-18 20:40:12 +01:00
/**
2023-02-22 21:53:58 +01:00
* Attempts to append `source_elements` to the contiguous_range.
2023-02-18 20:40:12 +01:00
*
* The returned [append_result] indicates whether the operation was successful or not.
*
2023-02-22 21:53:58 +01:00
* If the returned [append_result] is anything but [append_result::ok], the
* [contiguous_range] will be left in an implementation-defined state.
2023-02-18 20:40:12 +01:00
*/
virtual append_result append(slice<element const> const & source_elements) = 0;
2023-02-22 21:53:58 +01:00
/**
* Returns a read-only [slice] of the current range values.
*
* *Note*: the behavior of retaining the returned value past the scope of the source
* [contiguous_range] or any subsequent modifications to it is implementation-defined.
*/
virtual slice<element const> as_slice() = 0;
2023-02-18 04:34:40 +01:00
};
2023-02-18 20:40:12 +01:00
/**
2023-02-22 21:53:58 +01:00
* Last-in-first-out contiguous range of `element` values.
2023-02-18 20:40:12 +01:00
*
* [stack] types will default to using an inline array of `init_capacity` at first. After all
* local storage has been exhausted, the [stack] will switch to a dynamic buffer. Because of
* this, it is recommended to use larger `init_capacity` values for data which has a known or
* approximate upper bound at compile-time. Otherwise, the `init_capacity` value may be left at
* its default.
2023-02-22 16:31:21 +01:00
*
* *Note*: the [allocator] referenced in the stack must remain valid for the duration of the
* stack lifetime.
2023-02-18 20:40:12 +01:00
*/
2023-02-22 21:53:58 +01:00
template<typename element, usize init_capacity = 1> struct stack : public contiguous_range<element> {
2023-02-22 16:31:21 +01:00
stack(allocator * dynamic_allocator) {
2023-02-18 20:40:12 +01:00
this->dynamic_allocator = dynamic_allocator;
2023-02-18 04:34:40 +01:00
}
~stack() override {
2023-02-22 16:31:21 +01:00
if (this->is_dynamic()) {
for (element & e : this->elements) e.~element();
this->dynamic_allocator->deallocate(this->elements.pointer);
}
2023-02-18 04:34:40 +01:00
}
2023-02-18 20:40:12 +01:00
/**
* Attempts to append `source_elements` to the top of the stack.
*
* The returned [append_result] indicates whether the operation was successful or not.
*
* If the returned [append_result] is anything but [append_result::ok], the stack will be
* be left in an empty but valid state.
*
* *Note* that [push] is recommended when appending singular values.
*/
append_result append(slice<element const> const & source_elements) override {
usize const updated_fill = this->filled + source_elements.length;
2023-02-18 04:34:40 +01:00
2023-02-18 20:40:12 +01:00
if (updated_fill >= this->elements.length) {
append_result const result = this->reserve(updated_fill);
2023-02-18 04:34:40 +01:00
2023-02-18 20:40:12 +01:00
if (result != append_result::ok) return result;
2023-02-18 04:34:40 +01:00
}
2023-02-18 20:40:12 +01:00
for (usize i = 0; i < source_elements.length; i += 1)
this->elements[this->filled + i] = source_elements[i];
2023-02-18 04:34:40 +01:00
2023-02-18 20:40:12 +01:00
this->filled = updated_fill;
return append_result::ok;
2023-02-18 04:34:40 +01:00
}
2023-02-22 21:53:58 +01:00
/**
* Returns a read-only [slice] of the current stack values.
*
* *Note*: the returned slice should be considered invalid if any mutable operation is
* performed on the source [stack] or it is no longer in scope.
*/
slice<element const> as_slice() override {
return this->elements.sliced(0, this->filled);
}
2023-02-18 20:40:12 +01:00
/**
* Returns `true` if the stack is backed by dynamic memory, otherwise `false`.
*/
bool is_dynamic() const {
2023-02-22 16:31:21 +01:00
return this->elements.pointer != reinterpret_cast<element const *>(this->local_buffer);
2023-02-18 20:40:12 +01:00
}
2023-02-18 04:34:40 +01:00
2023-02-18 20:40:12 +01:00
/**
* Attempts to append `source_element` to the top of the stack.
*
* The returned [append_result] indicates whether the operation was successful or not.
*
* If the returned [append_result] is anything but [append_result::ok], the stack will be
* be left in an empty but valid state.
*
* *Note* that [append] is recommended when appending many values at once.
*/
append_result push(element const & source_element) {
if (this->filled == this->elements.length) {
append_result const result = this->reserve(this->elements.length);
if (result != append_result::ok) return result;
}
this->elements[this->filled] = source_element;
this->filled += 1;
2023-02-18 04:34:40 +01:00
2023-02-18 20:40:12 +01:00
return append_result::ok;
2023-02-18 04:34:40 +01:00
}
2023-02-18 20:40:12 +01:00
/**
* Attempts to reserve `capacity` number of elements additional space on the stack, forcing
* it to use dynamic memory _even_ if it hasn't exhausted the local buffer yet.
*
* The returned [append_result] indicates whether the operation was successful or not.
*
* If the returned [append_result] is anything but [append_result::ok], the stack will be
* be left in an empty but valid state.
*
* *Note* that manual invocation is not recommended if the [stack] has a large
* `initial_capacity` argument.
*/
append_result reserve(usize capacity) {
usize const requested_capacity = this->filled + capacity;
2023-02-18 04:34:40 +01:00
2023-02-18 20:40:12 +01:00
if (this->is_dynamic()) {
u8 * const buffer = this->dynamic_allocator->reallocate(
reinterpret_cast<u8 *>(this->elements.pointer),
sizeof(element) * requested_capacity);
2023-02-18 04:34:40 +01:00
2023-02-18 20:40:12 +01:00
if (buffer == nullptr) {
2023-02-18 04:34:40 +01:00
this->elements = {};
2023-02-18 20:40:12 +01:00
return append_result::out_of_memory;
2023-02-18 04:34:40 +01:00
}
2023-02-18 20:40:12 +01:00
this->elements = {reinterpret_cast<element *>(buffer), requested_capacity};
2023-02-18 04:34:40 +01:00
} else {
2023-02-18 20:40:12 +01:00
usize const buffer_size = sizeof(element) * requested_capacity;
u8 * const buffer = this->dynamic_allocator->reallocate(nullptr, buffer_size);
2023-02-18 04:34:40 +01:00
2023-02-18 20:40:12 +01:00
if (buffer == nullptr) {
2023-02-18 04:34:40 +01:00
this->elements = {};
2023-02-18 20:40:12 +01:00
return append_result::out_of_memory;
2023-02-18 04:34:40 +01:00
}
copy({buffer, buffer_size}, this->elements.as_bytes());
2023-02-18 20:40:12 +01:00
this->elements = {reinterpret_cast<element *>(buffer), requested_capacity};
2023-02-18 04:34:40 +01:00
}
2023-02-18 20:40:12 +01:00
return append_result::ok;
2023-02-18 04:34:40 +01:00
}
private:
2023-02-22 16:31:21 +01:00
allocator * dynamic_allocator{nullptr};
2023-02-18 04:34:40 +01:00
2023-02-22 16:31:21 +01:00
usize filled{0};
2023-02-18 04:34:40 +01:00
2023-02-22 16:31:21 +01:00
slice<element> elements{reinterpret_cast<element *>(local_buffer), init_capacity};
2023-02-18 04:34:40 +01:00
2023-02-22 16:31:21 +01:00
u8 local_buffer[init_capacity]{0};
2023-02-18 04:34:40 +01:00
};
2023-02-22 21:53:58 +01:00
}
using contiguous_byte_range = coral::contiguous_range<coral::u8>;
// Reader / writers.
export namespace coral {
/**
* Readable type for streaming data from a [contiguous_range] containing [u8] values.
*/
struct contiguous_reader : public reader {
contiguous_reader(contiguous_byte_range * range) {
this->range = range;
}
expected<usize, io_error> write(slice<u8 const> const & buffer) {
switch (this->range->append(buffer)) {
case append_result::ok: return buffer.length;
case append_result::out_of_memory: return io_error::unavailable;
default: unreachable();
}
}
private:
contiguous_byte_range * range;
};
2023-02-18 04:34:40 +01:00
2023-02-18 20:40:12 +01:00
/**
2023-02-22 21:53:58 +01:00
* Writable type for appending data to a [contiguous_range] containing [u8] values.
2023-02-18 20:40:12 +01:00
*/
2023-02-22 21:53:58 +01:00
struct contiguous_writer : public writer {
contiguous_writer(contiguous_byte_range * range) {
this->range = range;
}
expected<usize, io_error> write(slice<u8 const> const & buffer) {
2023-02-22 21:53:58 +01:00
switch (this->range->append(buffer)) {
case append_result::ok: return buffer.length;
case append_result::out_of_memory: return io_error::unavailable;
default: unreachable();
}
}
private:
2023-02-22 21:53:58 +01:00
contiguous_byte_range * range;
2023-02-18 04:34:40 +01:00
};
}