module; #include #include 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 constexpr scalar max(scalar const & a, scalar const & b) { return (a > b) ? a : b; } template constexpr scalar min(scalar const & a, scalar const & b) { return (a < b) ? a : b; } template 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 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 constexpr slice(type(&array)[array_size]) : length{array_size}, pointer{array} { } slice as_bytes() const { return {reinterpret_cast(this->pointer), this->length * sizeof(type)}; } slice as_chars() const { return {reinterpret_cast(this->pointer), this->length * sizeof(type)}; } operator slice() const { return (*reinterpret_cast const *>(this)); } type & operator[](usize index) const { return this->pointer[index]; } slice after(usize index) const { return {this->pointer + index, this->length - index}; } slice until(usize index) const { return {this->pointer, index}; } slice 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 struct [[nodiscard]] optional { optional() : buffer{0} { } optional(element const & value) : buffer{0} { (*reinterpret_cast(this->buffer)) = value; this->buffer[sizeof(element)] = 1; } optional(optional const & that) : buffer{0} { if (that.has_value()) { (*reinterpret_cast(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(this->buffer) : fallback; } element & value() { return *reinterpret_cast(this->buffer); } element const & value() const { return *reinterpret_cast(this->buffer); } private: u8 buffer[sizeof(element) + 1]; }; template struct [[nodiscard]] expected { expected(value_element const & value) : buffer{0} { (*reinterpret_cast(this->buffer)) = value; this->buffer[buffer_size] = 1; } expected(error_element const & error) : buffer{0} { (*reinterpret_cast(this->buffer)) = error; } bool is_ok() const { return this->buffer[buffer_size]; } value_element & value() { if (!this->is_ok()) unreachable(); return *reinterpret_cast(this->buffer); } value_element const & value() const { if (!this->is_ok()) unreachable(); return *reinterpret_cast(this->buffer); } error_element & error() { if (this->is_ok()) unreachable(); return *reinterpret_cast(this->buffer); } error_element const & error() const { if (this->is_ok()) unreachable(); return *reinterpret_cast(this->buffer); } private: static constexpr usize buffer_size = max(sizeof(value_element), sizeof(error_element)); u8 buffer[buffer_size + 1]; }; template struct callable; template struct callable { using function = return_type(*)(argument_types...); callable(function callable_function) : dispatcher(dispatch_function) { new (this->capture) function{callable_function}; } callable(callable const &) = delete; template callable(functor const & callable_functor) : dispatcher(dispatch_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(userdata))(arguments...); } template static return_type dispatch_functor(u8 const * userdata, argument_types... arguments) { return (*reinterpret_cast(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(slice const &)>; using writable = callable(slice const &)>; template bool equals(slice const & a, slice 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 stream(writable const & output, readable const & input, slice 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; } }