321 lines
7.7 KiB
C++
321 lines
7.7 KiB
C++
module;
|
|
|
|
#include <cstdint>
|
|
#include <cstddef>
|
|
|
|
export module core;
|
|
|
|
export namespace core {
|
|
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;
|
|
|
|
using i32 = uint32_t;
|
|
|
|
usize const i32_max = 0xffffffff;
|
|
|
|
using u64 = uint32_t;
|
|
|
|
using i64 = uint32_t;
|
|
|
|
using f32 = float;
|
|
|
|
using f64 = double;
|
|
|
|
struct allocator {
|
|
allocator() = default;
|
|
|
|
allocator(allocator const &) = delete;
|
|
|
|
virtual ~allocator() {};
|
|
|
|
virtual u8 * reallocate(u8 * maybe_allocation, usize requested_size) = 0;
|
|
|
|
virtual void deallocate(void * allocation) = 0;
|
|
};
|
|
}
|
|
|
|
export void * operator new(core::usize requested_size, core::u8 * allocation_placement) {
|
|
return allocation_placement;
|
|
}
|
|
|
|
export void * operator new[](core::usize requested_size, core::u8 * allocation_placement) {
|
|
return allocation_placement;
|
|
}
|
|
|
|
export void * operator new(core::usize requested_size, core::allocator & allocator) {
|
|
return allocator.reallocate(nullptr, requested_size);
|
|
}
|
|
|
|
export void * operator new[](core::usize requested_size, core::allocator & allocator) {
|
|
return allocator.reallocate(nullptr, requested_size);
|
|
}
|
|
|
|
export namespace core {
|
|
[[noreturn]] void unreachable() {
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
template<typename scalar> constexpr scalar max(scalar const & a, scalar const & b) {
|
|
return (a > b) ? a : b;
|
|
}
|
|
|
|
template<typename scalar> constexpr scalar min(scalar const & a, scalar const & b) {
|
|
return (a < b) ? a : b;
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
f32 round32(f32 value) {
|
|
return __builtin_roundf(value);
|
|
}
|
|
|
|
template<typename type> struct slice {
|
|
usize length;
|
|
|
|
type * pointer;
|
|
|
|
constexpr slice() : length{0}, pointer{nullptr} {
|
|
|
|
}
|
|
|
|
constexpr slice(char const *&& zstring) : length{0}, pointer{zstring} {
|
|
while (zstring[length] != 0) this->length += 1;
|
|
}
|
|
|
|
constexpr slice(type * slice_pointer, usize slice_length) : length{slice_length}, pointer{slice_pointer} {
|
|
|
|
}
|
|
|
|
template<usize array_size> constexpr slice(type(&array)[array_size]) : length{array_size}, pointer{array} {
|
|
|
|
}
|
|
|
|
slice<u8 const> as_bytes() const {
|
|
return {reinterpret_cast<u8 const *>(this->pointer), this->length * sizeof(type)};
|
|
}
|
|
|
|
slice<char const> as_chars() const {
|
|
return {reinterpret_cast<char const *>(this->pointer), this->length * sizeof(type)};
|
|
}
|
|
|
|
operator slice<type const>() const {
|
|
return (*reinterpret_cast<slice<type const> const *>(this));
|
|
}
|
|
|
|
type & operator[](usize index) const {
|
|
return this->pointer[index];
|
|
}
|
|
|
|
slice<type> after(usize index) const {
|
|
return {this->pointer + index, this->length - index};
|
|
}
|
|
|
|
slice<type> until(usize index) const {
|
|
return {this->pointer, index};
|
|
}
|
|
|
|
slice<type> between(usize a, usize b) const {
|
|
return {this->pointer + a, b};
|
|
}
|
|
|
|
constexpr type * begin() const {
|
|
return this->pointer;
|
|
}
|
|
|
|
constexpr type * end() const {
|
|
return this->pointer + this->length;
|
|
}
|
|
};
|
|
|
|
template<typename element> struct [[nodiscard]] optional {
|
|
optional() : buffer{0} {
|
|
|
|
}
|
|
|
|
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()) {
|
|
(*reinterpret_cast<element *>(this->buffer)) = that.value();
|
|
this->buffer[sizeof(element)] = 1;
|
|
} else {
|
|
this->buffer[sizeof(element)] = 0;
|
|
}
|
|
}
|
|
|
|
bool has_value() const {
|
|
return this->buffer[sizeof(element)] == 1;
|
|
}
|
|
|
|
element const & value_or(element const & fallback) const {
|
|
return this->has_value() ? *reinterpret_cast<element const *>(this->buffer) : fallback;
|
|
}
|
|
|
|
element & value() {
|
|
return *reinterpret_cast<element *>(this->buffer);
|
|
}
|
|
|
|
element const & value() const {
|
|
return *reinterpret_cast<element const *>(this->buffer);
|
|
}
|
|
|
|
private:
|
|
u8 buffer[sizeof(element) + 1];
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
bool is_ok() const {
|
|
return this->buffer[buffer_size];
|
|
}
|
|
|
|
value_element & value() {
|
|
if (!this->is_ok()) unreachable();
|
|
|
|
return *reinterpret_cast<value_element *>(this->buffer);
|
|
}
|
|
|
|
value_element const & value() const {
|
|
if (!this->is_ok()) unreachable();
|
|
|
|
return *reinterpret_cast<value_element const *>(this->buffer);
|
|
}
|
|
|
|
error_element & error() {
|
|
if (this->is_ok()) unreachable();
|
|
|
|
return *reinterpret_cast<error_element *>(this->buffer);
|
|
}
|
|
|
|
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];
|
|
};
|
|
|
|
template<typename> struct callable;
|
|
|
|
template<typename return_type, typename... argument_types> struct callable<return_type(argument_types...)> {
|
|
using function = return_type(*)(argument_types...);
|
|
|
|
callable(function callable_function) : dispatcher(dispatch_function) {
|
|
new (this->capture) function{callable_function};
|
|
}
|
|
|
|
callable(callable const &) = delete;
|
|
|
|
template<typename functor> callable(functor const & callable_functor) : dispatcher(dispatch_functor<functor>) {
|
|
new (this->capture) functor{callable_functor};
|
|
}
|
|
|
|
return_type operator()(argument_types const &... arguments) const {
|
|
return this->dispatcher(this->capture, arguments...);
|
|
}
|
|
|
|
private:
|
|
static constexpr usize capture_size = 24;
|
|
|
|
return_type(* dispatcher)(u8 const * userdata, argument_types... arguments);
|
|
|
|
u8 capture[capture_size];
|
|
|
|
static return_type dispatch_function(u8 const * userdata, argument_types... arguments) {
|
|
return (*reinterpret_cast<function const *>(userdata))(arguments...);
|
|
}
|
|
|
|
template<typename functor_type> static return_type dispatch_functor(u8 const * userdata, argument_types... arguments) {
|
|
return (*reinterpret_cast<functor_type const*>(userdata))(arguments...);
|
|
}
|
|
};
|
|
|
|
allocator & null_allocator() {
|
|
static struct : public allocator {
|
|
u8 * reallocate(u8 * maybe_allocation, usize requested_size) override {
|
|
if (maybe_allocation != nullptr) unreachable();
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void deallocate(void * allocation) override {
|
|
if (allocation != nullptr) unreachable();
|
|
}
|
|
} a;
|
|
|
|
return a;
|
|
}
|
|
|
|
enum class io_error {
|
|
unavailable,
|
|
};
|
|
|
|
using readable = callable<expected<usize, io_error>(slice<u8> const &)>;
|
|
|
|
using writable = callable<expected<usize, io_error>(slice<u8 const> const &)>;
|
|
|
|
template<typename element_type> bool equals(slice<element_type const> const & a, slice<element_type 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;
|
|
}
|
|
|
|
expected<usize, io_error> stream(writable const & output, readable const & input, slice<u8> const & buffer) {
|
|
usize written = 0;
|
|
expected maybe_read = input(buffer);
|
|
|
|
if (!maybe_read.is_ok()) return maybe_read.error();
|
|
|
|
usize read = maybe_read.value();
|
|
|
|
while (read != 0) {
|
|
expected const maybe_written = output(buffer.until(read));
|
|
|
|
if (!maybe_written.is_ok()) return maybe_read.error();
|
|
|
|
written += maybe_written.value();
|
|
maybe_read = input(buffer);
|
|
|
|
if (!maybe_read.is_ok()) return maybe_read.error();
|
|
|
|
read = maybe_read.value();
|
|
}
|
|
|
|
return written;
|
|
}
|
|
}
|