2023-02-18 04:34:40 +01:00
|
|
|
module;
|
|
|
|
|
|
|
|
#include <cstdint>
|
|
|
|
#include <cstddef>
|
2023-02-20 16:26:27 +01:00
|
|
|
#include <type_traits>
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-19 17:50:29 +01:00
|
|
|
export module coral;
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
// Runtime utilities.
|
2023-02-19 17:50:29 +01:00
|
|
|
export namespace coral {
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Triggers safety-checked behavior in debug mode.
|
|
|
|
*
|
|
|
|
* In release mode, the compiler can use this function as a marker to optimize out safety-
|
|
|
|
* checked logic branches that should never be executed.
|
|
|
|
*/
|
|
|
|
[[noreturn]] void unreachable() {
|
|
|
|
__builtin_unreachable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Concrete and interface types.
|
2023-02-19 17:50:29 +01:00
|
|
|
export namespace coral {
|
2023-02-18 04:34:40 +01:00
|
|
|
using usize = size_t;
|
|
|
|
|
|
|
|
using size = __ssize_t;
|
|
|
|
|
|
|
|
using u8 = uint8_t;
|
|
|
|
|
|
|
|
usize const u8_max = 0xff;
|
|
|
|
|
|
|
|
using i8 = uint8_t;
|
|
|
|
|
|
|
|
using u16 = uint16_t;
|
|
|
|
|
|
|
|
usize const u16_max = 0xffff;
|
|
|
|
|
|
|
|
using i16 = uint16_t;
|
|
|
|
|
|
|
|
using u32 = uint32_t;
|
|
|
|
|
2023-02-20 02:28:58 +01:00
|
|
|
using i32 = int32_t;
|
2023-02-18 04:34:40 +01:00
|
|
|
|
|
|
|
usize const i32_max = 0xffffffff;
|
|
|
|
|
2023-02-20 01:35:29 +01:00
|
|
|
using u64 = uint64_t;
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-20 02:28:58 +01:00
|
|
|
using i64 = int64_t;
|
2023-02-18 04:34:40 +01:00
|
|
|
|
|
|
|
using f32 = float;
|
|
|
|
|
|
|
|
using f64 = double;
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Base type for runtime-pluggable memory allocation strategies used by the core library.
|
|
|
|
*/
|
2023-02-18 04:34:40 +01:00
|
|
|
struct allocator {
|
|
|
|
virtual ~allocator() {};
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* If `allocation` is `nullptr`, the allocator will attempt to allocate a new memory block
|
|
|
|
* of `requested_size` bytes. Otherwise, the allocator will attempt to reallocate
|
|
|
|
* `allocation` to be `request_size` bytes in size.
|
|
|
|
*
|
|
|
|
* The returned address will point to a dynamically allocated buffer of `requested_size` if
|
|
|
|
* the operation was successful, otherwise `nullptr`.
|
|
|
|
*
|
|
|
|
* *Note*: If the returned address is a non-`nullptr`, it should be deallocated prior to
|
|
|
|
* program exit. This may be achieved through either [deallocate] or implementation-
|
|
|
|
* specific allocator functionality.
|
|
|
|
*
|
|
|
|
* *Note*: Attempting to pass a non-`nullptr` `allocation` address not allocated by the
|
|
|
|
* allocator *will* result in erroneous implementation-behavior.
|
|
|
|
*
|
|
|
|
* *Note*: After invocation, `allocation` should be considered an invalid memory address.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] virtual u8 * reallocate(u8 * allocation, usize requested_size) = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If `allocation` points to a non-`nullptr` address, the allocator will deallocate it.
|
|
|
|
* Otherwise, the function has no side-effects.
|
|
|
|
*
|
|
|
|
* *Note* that attempting to pass a non-`nullptr` `allocation` address not allocated by the
|
|
|
|
* allocator *will* result in erroneous implementation-behavior.
|
|
|
|
*/
|
2023-02-18 04:34:40 +01:00
|
|
|
virtual void deallocate(void * allocation) = 0;
|
|
|
|
};
|
2023-02-18 14:19:01 +01:00
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Length-signed pointer type that describes how many elements of `type` it references,
|
|
|
|
* providing a type-safe wrapper for passing arrays and zero-terminated strings to functions.
|
|
|
|
*/
|
2023-02-18 04:34:40 +01:00
|
|
|
template<typename type> struct slice {
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Number of `type` elements referenced.
|
|
|
|
*/
|
2023-02-22 16:35:22 +01:00
|
|
|
usize length{0};
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Base element address referenced.
|
|
|
|
*/
|
2023-02-22 16:35:22 +01:00
|
|
|
type * pointer{nullptr};
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-22 16:35:22 +01:00
|
|
|
constexpr slice() = default;
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
constexpr slice(char const *&& zstring) {
|
|
|
|
this->pointer = zstring;
|
|
|
|
this->length = 0;
|
|
|
|
|
2023-02-18 04:34:40 +01:00
|
|
|
while (zstring[length] != 0) this->length += 1;
|
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
constexpr slice(type * slice_pointer, usize slice_length) {
|
|
|
|
this->pointer = slice_pointer;
|
|
|
|
this->length = slice_length;
|
2023-02-18 04:34:40 +01:00
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
constexpr slice(type * slice_begin, type * slice_end) {
|
|
|
|
this->pointer = slice_begin;
|
|
|
|
this->length = static_cast<usize>(slice_end - slice_begin);
|
|
|
|
}
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
template<usize array_size> constexpr slice(type(&array)[array_size]) {
|
|
|
|
this->pointer = array;
|
|
|
|
this->length = array_size;
|
2023-02-18 04:34:40 +01:00
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Reinterprets the data referenced as a series of bytes.
|
|
|
|
*
|
|
|
|
* The returned view is constant to protect against inadvertant memory corruption.
|
|
|
|
*/
|
2023-02-18 04:34:40 +01:00
|
|
|
slice<u8 const> as_bytes() const {
|
|
|
|
return {reinterpret_cast<u8 const *>(this->pointer), this->length * sizeof(type)};
|
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Reinterprets the data referenced as a series of chars.
|
|
|
|
*
|
|
|
|
* The returned view is constant to protect against inadvertant memory corruption.
|
|
|
|
*
|
|
|
|
* *Note* the returned value has no guarantees about the validity of any specific character
|
|
|
|
* encoding set.
|
|
|
|
*/
|
2023-02-18 04:34:40 +01:00
|
|
|
slice<char const> as_chars() const {
|
|
|
|
return {reinterpret_cast<char const *>(this->pointer), this->length * sizeof(type)};
|
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Returns the base pointer of the slice.
|
|
|
|
*/
|
|
|
|
constexpr type * begin() const {
|
|
|
|
return this->pointer;
|
2023-02-18 04:34:40 +01:00
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Returns the tail pointer of the slice.
|
|
|
|
*/
|
|
|
|
constexpr type * end() const {
|
|
|
|
return this->pointer + this->length;
|
2023-02-18 04:34:40 +01:00
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Returns a new slice with the base-pointer offset by `index` elements and a length of
|
|
|
|
* `range` elements from `index`.
|
|
|
|
*
|
|
|
|
* *Note* that attempting to slice with an `index` or `range` outside of the existing slice
|
|
|
|
* bounds will result in safety-checked behavior.
|
|
|
|
*/
|
|
|
|
constexpr slice sliced(usize index, usize range) const {
|
|
|
|
if ((this->length <= index) || ((range + index) > this->length)) unreachable();
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
return {this->pointer + index, range - index};
|
2023-02-18 04:34:40 +01:00
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
operator slice<type const>() const {
|
|
|
|
return (*reinterpret_cast<slice<type const> const *>(this));
|
2023-02-18 04:34:40 +01:00
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
constexpr type & operator[](usize index) const {
|
2023-02-19 17:50:29 +01:00
|
|
|
if (this->length <= index) unreachable();
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
return this->pointer[index];
|
2023-02-18 04:34:40 +01:00
|
|
|
}
|
|
|
|
};
|
2023-02-18 20:40:12 +01:00
|
|
|
}
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
// Math functions.
|
2023-02-19 17:50:29 +01:00
|
|
|
export namespace coral {
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Returns the maximum value between `a` and `b`.
|
|
|
|
*/
|
|
|
|
template<typename scalar> constexpr scalar max(scalar const & a, scalar const & b) {
|
|
|
|
return (a > b) ? a : b;
|
|
|
|
}
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Returns the minimum value between `a` and `b`.
|
|
|
|
*/
|
|
|
|
template<typename scalar> constexpr scalar min(scalar const & a, scalar const & b) {
|
|
|
|
return (a < b) ? a : b;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns `value` clamped between the range of `min_value` and `max_value` (inclusive).
|
|
|
|
*/
|
|
|
|
template<typename scalar> constexpr scalar clamp(scalar const & value, scalar const & min_value, scalar const & max_value) {
|
|
|
|
return max(min_value, min(max_value, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns `value` rounded to the nearest whole number.
|
|
|
|
*/
|
|
|
|
f32 round32(f32 value) {
|
|
|
|
return __builtin_roundf(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocates and initializes a type of `requested_size` in `buffer`, returning its base pointer. As
|
|
|
|
* a result of accepting a pre-allocated buffer, invocation does not allocate any dynamic memory.
|
|
|
|
*
|
|
|
|
* *Note*: passing an `buffer` smaller than `requested_size` will result in safety-checked
|
|
|
|
* behavior.
|
|
|
|
*/
|
2023-02-19 17:50:29 +01:00
|
|
|
export void * operator new(coral::usize requested_size, coral::slice<coral::u8> const & buffer) {
|
|
|
|
if (buffer.length < requested_size) coral::unreachable();
|
2023-02-18 20:40:12 +01:00
|
|
|
|
|
|
|
return buffer.pointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocates and initializes a series of types at `requested_size` in `buffer`, returning the base
|
|
|
|
* pointer. As a result of accepting a pre-allocated buffer, invocation does not allocate any
|
|
|
|
* dynamic memory.
|
|
|
|
*
|
|
|
|
* *Note*: passing an `buffer` smaller than `requested_size` will result in safety-checked
|
|
|
|
* behavior.
|
|
|
|
*/
|
2023-02-19 17:50:29 +01:00
|
|
|
export void * operator new[](coral::usize requested_size, coral::slice<coral::u8> const & buffer) {
|
|
|
|
if (buffer.length < requested_size) coral::unreachable();
|
2023-02-18 20:40:12 +01:00
|
|
|
|
|
|
|
return buffer.pointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempts to allocate and initialize a type of `requested_size` using `allocator`.
|
|
|
|
*
|
|
|
|
* *Note*: If the returned address is a non-`nullptr`, it should be deallocated prior to program
|
2023-02-19 17:50:29 +01:00
|
|
|
* exit. This may be achieved through either [coral::allocator::deallocate] or implementation-
|
2023-02-18 20:40:12 +01:00
|
|
|
* specific allocator functionality.
|
|
|
|
*/
|
2023-02-19 17:50:29 +01:00
|
|
|
export [[nodiscard]] void * operator new(coral::usize requested_size, coral::allocator & allocator) {
|
2023-02-18 20:40:12 +01:00
|
|
|
return allocator.reallocate(nullptr, requested_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempts to allocate and initialize a series of types of `requested_size` using `allocator`.
|
|
|
|
*
|
|
|
|
* *Note*: If the returned address is a non-`nullptr`, it should be deallocated prior to program
|
2023-02-19 17:50:29 +01:00
|
|
|
* exit. This may be achieved through either [coral::allocator::deallocate] or implementation-
|
2023-02-18 20:40:12 +01:00
|
|
|
* specific allocator functionality.
|
|
|
|
*/
|
2023-02-19 17:50:29 +01:00
|
|
|
export [[nodiscard]] void * operator new[](coral::usize requested_size, coral::allocator & allocator) {
|
2023-02-18 20:40:12 +01:00
|
|
|
return allocator.reallocate(nullptr, requested_size);
|
|
|
|
}
|
|
|
|
|
2023-02-22 16:35:22 +01:00
|
|
|
/**
|
|
|
|
* If `pointer` is a non-`nullptr` value, the referenced memory will be deallocated using
|
|
|
|
* `allocator`. Otherwise, the function has no side-effects.
|
|
|
|
*
|
|
|
|
* *Note*: passing a `pointer` value that was not allocated by `allocator` will result in erroneous
|
|
|
|
* behavior defined by the [coral::allocator] implementation.
|
|
|
|
*/
|
|
|
|
export void operator delete(void * pointer, coral::allocator & allocator) {
|
|
|
|
return allocator.deallocate(pointer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
export void operator delete[](void * pointer, coral::allocator & allocator) {
|
|
|
|
return allocator.deallocate(pointer);
|
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
// Wrapper types.
|
2023-02-19 17:50:29 +01:00
|
|
|
export namespace coral {
|
2023-02-24 01:33:04 +01:00
|
|
|
template<typename> struct closure;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Type-erasing view wrapper for both function and functor types that have a call operator with
|
|
|
|
* a return value matching `return_value` and arguments matching `argument_values`.
|
|
|
|
*
|
|
|
|
* **Note**: closures take no ownership of allocated memory, making it the responsibility of
|
|
|
|
* the caller to manage the lifetime of any functor assigned to it.
|
|
|
|
*/
|
|
|
|
template<typename returns, typename... arguments> struct closure<returns(arguments...)> {
|
|
|
|
using function = returns(*)(arguments...);
|
|
|
|
|
|
|
|
closure(function callable_function) {
|
|
|
|
this->dispatch = [](void const * context, arguments... dispatch_arguments) -> returns {
|
|
|
|
return (reinterpret_cast<function const *>(context))(dispatch_arguments...);
|
|
|
|
};
|
|
|
|
|
|
|
|
this->context = callable_function;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename functor> closure(functor * callable_functor) {
|
|
|
|
this->dispatch = [](void const * context, arguments... dispatch_arguments) -> returns {
|
|
|
|
return (*reinterpret_cast<functor const*>(context))(dispatch_arguments...);
|
|
|
|
};
|
|
|
|
|
|
|
|
this->context = callable_functor;
|
|
|
|
}
|
|
|
|
|
|
|
|
closure(closure const &) = delete;
|
|
|
|
|
|
|
|
template<typename functor> closure(functor && callable_functor) {
|
|
|
|
this->dispatch = [](void const * context, arguments... dispatch_arguments) -> returns {
|
|
|
|
return (*reinterpret_cast<functor const*>(context))(dispatch_arguments...);
|
|
|
|
};
|
|
|
|
|
|
|
|
this->context = &callable_functor;
|
|
|
|
}
|
|
|
|
|
|
|
|
returns operator()(arguments const &... call_arguments) const {
|
|
|
|
return this->dispatch(this->context, call_arguments...);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void const * context;
|
|
|
|
|
|
|
|
returns(* dispatch)(void const *, arguments...);
|
|
|
|
};
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Monadic container for a single-`element` value or nothing.
|
|
|
|
*/
|
|
|
|
template<typename element> struct [[nodiscard]] optional {
|
|
|
|
optional() : buffer{0} {}
|
2023-02-18 04:34:40 +01:00
|
|
|
|
|
|
|
optional(element const & value) : buffer{0} {
|
|
|
|
(*reinterpret_cast<element *>(this->buffer)) = value;
|
|
|
|
this->buffer[sizeof(element)] = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
optional(optional const & that) : buffer{0} {
|
|
|
|
if (that.has_value()) {
|
2023-02-20 16:30:00 +01:00
|
|
|
(*reinterpret_cast<element *>(this->buffer)) = *that;
|
2023-02-18 04:34:40 +01:00
|
|
|
this->buffer[sizeof(element)] = 1;
|
|
|
|
} else {
|
|
|
|
this->buffer[sizeof(element)] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Returns `true` if the optional contains a value, otherwise `false`.
|
|
|
|
*/
|
2023-02-18 04:34:40 +01:00
|
|
|
bool has_value() const {
|
|
|
|
return this->buffer[sizeof(element)] == 1;
|
|
|
|
}
|
|
|
|
|
2023-02-20 16:26:27 +01:00
|
|
|
/**
|
|
|
|
* Attempts to call `apply` on the contained value, returning a new [optional] of whatever type `apply` returns.
|
|
|
|
*
|
|
|
|
* If the optional is empty, an empty optional will always be returned.
|
|
|
|
*/
|
|
|
|
template<typename functor> std::invoke_result_t<functor, element> map(functor const & apply) const {
|
2023-02-20 16:30:00 +01:00
|
|
|
if (this->has_value()) return apply(**this);
|
2023-02-20 16:26:27 +01:00
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Returns the contained value or `fallback` if the optional is empty.
|
|
|
|
*/
|
2023-02-18 04:34:40 +01:00
|
|
|
element const & value_or(element const & fallback) const {
|
|
|
|
return this->has_value() ? *reinterpret_cast<element const *>(this->buffer) : fallback;
|
|
|
|
}
|
|
|
|
|
2023-02-20 16:30:00 +01:00
|
|
|
element & operator *() {
|
2023-02-18 20:40:12 +01:00
|
|
|
if (!this->has_value()) unreachable();
|
|
|
|
|
2023-02-18 04:34:40 +01:00
|
|
|
return *reinterpret_cast<element *>(this->buffer);
|
|
|
|
}
|
|
|
|
|
2023-02-20 16:30:00 +01:00
|
|
|
element const & operator *() const {
|
|
|
|
if (!this->has_value()) unreachable();
|
|
|
|
|
2023-02-18 04:34:40 +01:00
|
|
|
return *reinterpret_cast<element const *>(this->buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
u8 buffer[sizeof(element) + 1];
|
|
|
|
};
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Monadic container for a descriminating union of either `value_element` or `error_element`.
|
|
|
|
*/
|
2023-02-18 14:19:01 +01:00
|
|
|
template<typename value_element, typename error_element> struct [[nodiscard]] expected {
|
|
|
|
expected(value_element const & value) : buffer{0} {
|
|
|
|
(*reinterpret_cast<value_element *>(this->buffer)) = value;
|
|
|
|
this->buffer[buffer_size] = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
expected(error_element const & error) : buffer{0} {
|
|
|
|
(*reinterpret_cast<error_element *>(this->buffer)) = error;
|
|
|
|
}
|
|
|
|
|
2023-02-24 01:33:04 +01:00
|
|
|
/**
|
|
|
|
* Monadic function for calling `predicate` conditionally based on whether the expected is
|
|
|
|
* ok. If ok, the result of `predicate` is returned, otherwise `false` is always returned.
|
|
|
|
*
|
|
|
|
* This function may be used to chain conditional checks that depend on the expected being
|
|
|
|
* ok without creating a new local variable.
|
|
|
|
*/
|
|
|
|
bool and_test(closure<bool(value_element const &)> const & predicate) const {
|
|
|
|
return this->is_ok() && predicate(this->value());
|
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Returns `true` if the optional contains a value, otherwise `false` if it holds an error.
|
|
|
|
*/
|
2023-02-18 14:19:01 +01:00
|
|
|
bool is_ok() const {
|
|
|
|
return this->buffer[buffer_size];
|
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Returns a reference to the contained value.
|
|
|
|
*
|
|
|
|
* *Note*: attempting to access the value of an erroneous expected will trigger safety-
|
|
|
|
* checked behavior.
|
|
|
|
*/
|
2023-02-18 14:19:01 +01:00
|
|
|
value_element & value() {
|
|
|
|
if (!this->is_ok()) unreachable();
|
|
|
|
|
|
|
|
return *reinterpret_cast<value_element *>(this->buffer);
|
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Returns the contained value.
|
|
|
|
*
|
|
|
|
* *Note*: attempting to access the value of an erroneous expected will trigger safety-
|
|
|
|
* checked behavior.
|
|
|
|
*/
|
2023-02-18 14:19:01 +01:00
|
|
|
value_element const & value() const {
|
|
|
|
if (!this->is_ok()) unreachable();
|
|
|
|
|
|
|
|
return *reinterpret_cast<value_element const *>(this->buffer);
|
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Returns a reference to the contained error.
|
|
|
|
*
|
|
|
|
* *Note*: attempting to access the error of a non-erroneous expected will trigger safety-
|
|
|
|
* checked behavior.
|
|
|
|
*/
|
2023-02-18 14:19:01 +01:00
|
|
|
error_element & error() {
|
|
|
|
if (this->is_ok()) unreachable();
|
|
|
|
|
|
|
|
return *reinterpret_cast<error_element *>(this->buffer);
|
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Returns the contained error.
|
|
|
|
*
|
|
|
|
* *Note*: attempting to access the error of a non-erroneous expected will trigger safety-
|
|
|
|
* checked behavior.
|
|
|
|
*/
|
2023-02-18 14:19:01 +01:00
|
|
|
error_element const & error() const {
|
|
|
|
if (this->is_ok()) unreachable();
|
|
|
|
|
|
|
|
return *reinterpret_cast<error_element const *>(this->buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
static constexpr usize buffer_size = max(sizeof(value_element), sizeof(error_element));
|
|
|
|
|
|
|
|
u8 buffer[buffer_size + 1];
|
|
|
|
};
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Errors that may occur while executing an opaque I/O operation via the `readable` and
|
|
|
|
* `writable` type aliases.
|
|
|
|
*/
|
2023-02-18 14:19:01 +01:00
|
|
|
enum class io_error {
|
|
|
|
unavailable,
|
|
|
|
};
|
|
|
|
|
2023-02-20 22:58:36 +01:00
|
|
|
/**
|
|
|
|
* Readable resource interface.
|
|
|
|
*/
|
2023-02-20 01:34:46 +01:00
|
|
|
struct reader {
|
2023-02-20 22:58:36 +01:00
|
|
|
virtual ~reader() {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempts to fill `data` with whatever data the reader has to offer, returning the number
|
|
|
|
* of bytes actually read.
|
|
|
|
*
|
|
|
|
* Should the read operation fail for any reason, a [io_error] is returned instead.
|
|
|
|
*/
|
|
|
|
virtual expected<usize, io_error> read(slice<u8> const & data) = 0;
|
2023-02-20 01:34:46 +01:00
|
|
|
};
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-20 22:58:36 +01:00
|
|
|
/**
|
|
|
|
* Writable resource interface.
|
|
|
|
*/
|
2023-02-20 01:34:46 +01:00
|
|
|
struct writer {
|
2023-02-20 22:58:36 +01:00
|
|
|
virtual ~writer() {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempts to write `data` out to the writer, returning the number of bytes actually
|
|
|
|
* written.
|
|
|
|
*
|
|
|
|
* Should the write operation fail for any reason, a [io_error] is returned instead.
|
|
|
|
*/
|
|
|
|
virtual expected<usize, io_error> write(slice<u8 const> const & data) = 0;
|
2023-02-20 01:34:46 +01:00
|
|
|
};
|
2023-02-18 20:40:12 +01:00
|
|
|
}
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
// Input/output operations.
|
2023-02-19 17:50:29 +01:00
|
|
|
export namespace coral {
|
2023-02-20 15:30:14 +01:00
|
|
|
/**
|
|
|
|
* Returns `value` reinterpreted as a sequence of bytes.
|
|
|
|
*/
|
2023-02-20 16:00:39 +01:00
|
|
|
slice<u8 const> as_bytes(auto const * value) {
|
|
|
|
return {reinterpret_cast<u8 const *>(value), sizeof(value)};
|
2023-02-20 15:30:14 +01:00
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
2023-02-18 21:48:06 +01:00
|
|
|
* Compares `a` and `b`, returning the difference between them or `0` if they are identical.
|
2023-02-18 20:40:12 +01:00
|
|
|
*/
|
2023-02-18 21:48:06 +01:00
|
|
|
constexpr size compare(slice<u8 const> const & a, slice<u8 const> const & b) {
|
|
|
|
usize const range = min(a.length, b.length);
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 21:48:06 +01:00
|
|
|
for (usize index = 0; index < range; index += 1) {
|
|
|
|
size const difference = static_cast<size>(a[index]) - static_cast<size>(b[index]);
|
2023-02-18 04:34:40 +01:00
|
|
|
|
2023-02-18 21:48:06 +01:00
|
|
|
if (difference != 0) return difference;
|
|
|
|
}
|
|
|
|
|
|
|
|
return static_cast<size>(a.length) - static_cast<size>(b.length);
|
2023-02-18 04:34:40 +01:00
|
|
|
}
|
|
|
|
|
2023-02-18 20:40:12 +01:00
|
|
|
/**
|
|
|
|
* Copies the contents of `origin` into `target`.
|
|
|
|
*
|
|
|
|
* *Note*: safety-checked behavior is triggered if `target` is smaller than `origin`.
|
|
|
|
*/
|
|
|
|
void copy(slice<u8> const & target, slice<u8 const> const & origin) {
|
2023-02-19 17:51:38 +01:00
|
|
|
if (target.length < origin.length) unreachable();
|
2023-02-18 20:40:12 +01:00
|
|
|
|
2023-02-19 17:32:02 +01:00
|
|
|
for (usize i = 0; i < origin.length; i += 1) target[i] = origin[i];
|
2023-02-19 15:15:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Zeroes the contents of `target`.
|
|
|
|
*/
|
|
|
|
void zero(slice<u8> const & target) {
|
|
|
|
for (usize i = 0; i < target.length; i += 1) target[i] = 0;
|
2023-02-18 20:40:12 +01:00
|
|
|
}
|
|
|
|
|
2023-02-18 21:48:06 +01:00
|
|
|
/**
|
|
|
|
* Tests the equality of `a` against `b`, returning `true` if they contain identical bytes,
|
|
|
|
* otherwise `false`.
|
|
|
|
*/
|
|
|
|
constexpr bool equals(slice<u8 const> const & a, slice<u8 const> const & b) {
|
|
|
|
if (a.length != b.length) return false;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < a.length; i += 1) if (a[i] != b[i]) return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a hash code generated from the values in `bytes`.
|
2023-02-19 00:49:31 +01:00
|
|
|
*
|
|
|
|
* *Note:* the returned hash code is not guaranteed to be unique.
|
2023-02-18 21:48:06 +01:00
|
|
|
*/
|
|
|
|
constexpr usize hash(slice<u8 const> const & bytes) {
|
|
|
|
usize hash_code = 5381;
|
|
|
|
|
|
|
|
for (u8 const byte : bytes) hash_code = ((hash_code << 5) + hash_code) + byte;
|
|
|
|
|
|
|
|
return hash_code;
|
|
|
|
}
|
|
|
|
|
2023-02-19 17:21:44 +01:00
|
|
|
/**
|
|
|
|
* Swaps the values of `element` in `a` and `b` around using copy semantics.
|
|
|
|
*/
|
|
|
|
template<typename element> constexpr void swap(element & a, element & b) {
|
|
|
|
element const temp = a;
|
|
|
|
a = b;
|
|
|
|
b = temp;
|
|
|
|
}
|
2023-02-18 04:34:40 +01:00
|
|
|
}
|