Started `Error` (CXX exceptions), `alloc_t` (C/XX secure/polymorphic allocators),

Added macros, types, constraints, Rust-like Borrow interface (TODO: rework that)

Fortune for readpass's current commit: Future blessing − 末吉
master
Avril 2 years ago
parent d11d82c9b7
commit 8ac56c862f
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -13,9 +13,9 @@ SRC_CXX = $(shell find -O2 $(SRC)/ -type f -name \*.cpp -or -name \*.cxx)
INCLUDE=include
# Link to these libraries dynamicalls
SHARED_LIBS=
SHARED_LIBS=ssl sodium pthread
# Link to these libraries statically
STATIC_LIBS=
STATIC_LIBS=fmt
override __COMMA=,
override __VERSION_SPLIT:= $(subst ., ,$(VERSION))

@ -0,0 +1,52 @@
//! Custom allocation framekwork for frozen (RO) and/or secure allocations, shared allocations, aliased allocation, etc. (polymorphic.)
#ifndef _ALLOC_H
#define _ALLOC_H
#ifdef __cplusplus
#include <memory>
#endif
#include "types.h"
#include "macros.h"
LINK_START(C)
typedef struct base_allocator alloc_t;
//TODO: C API anonlogue for allocator interface below.
LINK_END
#if $CXX
extern "C" {
struct base_allocator {
//TODO: Allocator interface...
virtual ~base_allocator();
}
}
namespace alloc {
class FrozenAllocator : public alloc_t {
struct _impl;
protected:
struct anon_raw_secmem;
/*struct anon_raw_secmem::deleter {
operator
};*/
struct alloc_info;
struct alloc_value;
public:
template<typename T>
FrozenAllocator(FrozenAllocator &&) noexcept;
FrozenAllocator& operator=(FrozenAllocator &&);
FrozenAllocator(const FrozenAllocator&) = delete;
FrozenAllocator& operator=(const FrozenAllocator&) = delete;
virtual ~FrozenAllocator();
private:
std::unique_ptr<_impl> inner_;
//std::shared_ptr<anon_raw_secmem> m_manager;
//std::map<alloc_info, std::unique_ptr<alloc_value>> m_values;
};
}
#endif
#endif /* _ALLOC_H */

@ -0,0 +1,22 @@
#pragma once
#include "constraints.hh"
namespace borrow {
#if 0
//TODO: Eh, rethink this when brain not fogged...
template<typename T, where::deref_to<T> P = T*>
struct Borrow {
using traits = std::pointer_traits<P>;
using type = T;
using pointer_type = P;
using pointer = traits::pointer;
static_assert(std::is_same_v<T, traits::element_type>, "Invalid `deref_to<T> P` (element_type != T)");
};
#endif
}

@ -0,0 +1,103 @@
#pragma once
#include <memory>
#include <concepts>
#include <utility>
#include "macros.hh"
namespace where {
#if 0
//XXX: I think this is fine? auto&& rvalue references seem to bind to const& references anyway?
constexpr auto const& as_const( auto& t ) noexcept { return std::as_const(FORWARD(t)); }
constexpr auto const&& as_const( auto&& t ) noexcept {
using T = decltype(t);
static_assert(!std::is_reference_t<T>, "Bad decltype(auto&&)");
return static_cast<std::add_const_t<T>&&>(t);
}
#endif
template<typename T>
using add_const_reference_t = decltype((std::as_const(std::declval<T>())));
template<typename T>
using add_const_pointer_t = decltype(std::addressof(std::as_const(std::declval<T>())));
static_assert(
std::is_lvalue_reference_v<add_const_reference_t<int>>
and std::is_lvalue_reference_v<add_const_reference_t<int const&>>
and std::is_lvalue_reference_v<add_const_reference_t<int &&>
and std::is_lvalue_reference_v<add_const_reference_t<int const&&>
and std::is_same_v
< add_const_reference_t<std::unique_ptr<int>&>
, std::unique_ptr<int> const&
>
, "add_const_reference_t is not perfectly forwarding");
template<typename P>
concept pointer_like = requires {
typename std::pointer_traits<P>;
typename std::pointer_traits<P>::element_type;
typename std::pointer_traits<P>::pointer;
};
template<typename P, typename T, bool Noexcept = true>
concept deref_to = pointer_like<P>
and requires(P ptr) {
// element type can be assigned to `T`.
requires(std::is_convertible_v<std::pointer_traits<P>::element_type, T>);
// Dereferencing the pointer can be assigned to `T`.
{ *ptr } noexcept(Noexcept) -> std::convertible_to<std::pointer_traits<P>::element_type>;
//{ ptr <=> nullptr } noexcept(Noexcept) -> std::convertible_to<
// { std::as_const(*ptr) } noexcept(Noexcept) -> std::convertible_to<add_const_reference_t<T>>;
// { std::addressof(value) } -> std::convertible_to<std::pointer_traits<P>::pointer>;
};
template<typename P, typename T, bool Noexcept = true>
concept may_deref_to = deref_to<P, T, Noexcept>
and requires(P ptr) {
// The pointer is nullable
{ ptr } noexcept(Noexcept) -> std::convertible_to<bool>;
{ !ptr } noexcept(Noexcept) -> std::convertible_to<bool>;
// The pointer can be constructed from `nullptr`.
//{ nullptr } noexcept(Noexcept) -> std::convertible_to<P>;
// The pointer can be assigned `nullptr`.
//{ ptr = nullptr } noexcept(Noexcept);
// The pointer is orderable.
//{ ptr == ptr } noexcept(Noexcept) -> std::convertible_to<bool>;
//{ ptr != ptr } noexcept(Noexcept) -> std::convertible_to<bool>;
// The pointer is null-comparable
{ ptr == nullptr } noexcept(Noexcept) -> std::convertible_to<bool>;
{ ptr != nullptr } noexcept(Noexcept) -> std::convertible_to<bool>;
};
// { std::as_const(*ptr) } noexcept -> std::convertible_to<add_const_reference_t<T>>;
// { std::addressof(value) } noexcept -> std::convertible_to<P>;
/*requires(std::is_convertible_v
< add_const_reference_t< std::pointer_traits<P>::element_type >
, add_const_reference_t< T >
>);
{ std::as_const(*std::declval<std::pointer_traits<P>::pointer>()) }
-> std::convertible_to<std::pointer_traits<P>::element_type>;
//std::convertible_to< add_const_pointer_t<*/
//};
#if 0
(std::is_pointer_v<P> &&
// P = ?*
requires(P ptr) {
{ ptr } -> std::convertible_to<T const*>;
{ static_cast<T*>(ptr) };
}
)
or requires(P ptr, T value) {
};
#endif
}

@ -0,0 +1,20 @@
#include "macros.hh"
namespace error {
struct Error {
DEFINE_INTERFACE(Error);
constexpr Error() noexcept = default;
constexpr operator std::string_view() const noexcept { return message(); }
constexpr virtual std::string_view message() const noexcept =0;
constexpr virtual Error const* inner() const noexcept { return nullptr; }
// TODO: `borrow::Borrow<typename T, where::deref_to<T> (using std::pointer_traits<>) PtrType = T*>` (borrow.hh): essentially specialised `std::variant<T, PtrType>`
constexpr virtual borrow::Borrow<const Report> report() const& { return borrow::Borrow{ nullptr }; }
// constexpr virtual borrow::Borrow<const Report> report() & { return borrow::Borrow{ nullptr }; }
constexpr virtual borrow::Borrow<Report> report() & { return borrow::Borrow{ nullptr }; }
constexpr virtual borrow::Borrow<Report>:into_owned_t report() && { return borrow::Borrow{ nullptr }.into_owned(); } // <- Will initialise with `into_owned() &&` member function, which `std::move()`s the value from the pointer instead of just copying the pointer. XXX: How do we actually want this borrowing interface to behave, huh?
};
}

@ -0,0 +1,82 @@
#ifndef _MACROS_H
#define _MACROS_H
#ifdef __cplusplus
#include <type_traits>
#include <utility>
#define $CXX 1
#define $C 0
#else
#include <stdbool.h>
#define $CXX 0
#define $C 1
#endif
// Empty token
#define $NULL
#if $CXX // C++
#define LINK_START(C) extern #C {
#define LINK_END }
#define restrict __restrict__
#define $auto __auto_type
#define typeof(...) decltype(__VA_ARGS__)
#define $typeof(...) __typeof__(__VA_ARGS__)
#define $LIKELY(expr) __builtin_expect(bool(expr), true)
#define $UNLIKELY(expr) __builtin_expect(bool(expr), false)
#else // C
#define $LIKELY(expr) __builtin_expect((bool)(expr), true)
#define $UNLIKELY(expr) __builtin_expect((bool)(expr), false)
#define auto __auto_type
#define $auto __auto_type
#define LINK_START(_)
#define LINK_END
#define nullptr ((void*)0)
#define $typeof __typeof__
#define alignof _Alignof
#define static_assert _Static_assert
#define alignas(...) __attribute__((aligned(__VA_ARGS__)))
#endif
#define unrestrict __attribute__((__may_alias__))
#if $CXX
#define $READ_ONCE_FROM(...) [&] () noexcept -> auto { using cvx_t = ::std::add_pointer_t<::std::add_const_t<::std::add_volatile_t<decltype(__VA_ARGS__)>>>; \
return *static_cast<cvx_t>(::std::addressof(__VA_ARGS__)); \
}()
#define $WRITE_ONCE_TO(X, value) [&] () noexcept -> decltype(auto) { using vx_t = ::std::add_pointer_t<::std::add_volatile_t<decltype(X)>>; \
static_assert(::std::is_convertible_v<decltype(value), decltype(*::std::declval<vx_t>())>, "Cannot convert " #value " to assign to " #X); \
return *static_cast<vx_t>(::std::addressof(X)) = ::std::forward<decltype(value)>(value); \
}()
#else
#define $READ_ONCE_FROM(X) ({ $auto $__read_once_from = (X); *((const volatile $typeof($__read_once_from)*)(&$__read_once_from)); })
#define $WRITE_ONCE_TO(X, value) ({ $auto $__write_once_to = (X); *((volatile $typeof($__write_once_to)*)(&$__write_once_to)) = (value); })
#endif
#define $identity_type(...) $typeof(__VA_ARGS__)
#define $TRANSMUTE(T, expr) \
({ $auto $__expr_from = (expr); \
static_assert(sizeof(typeof($__expr_from)) == sizeof(T) \
&& alignof(typeof($__expr_from)) == alignof(typeof($__expr_from)), "Transmute source type does not have identical layout to dest type " #T); \
union { typeof($__expr_from) from; $typeof(T) to; } $__rec = { .from = $__expr_from; }; \
$__rec.to; })
#if $CXX
#define TRANSMUTE(T, ...) ::std::bit_cast<$typeof(T)>(__VA_ARGS__)
#else
#define TRANSMUTE(T, ...) $TRANSMUTE(T, __VA_ARGS__)
#endif
#define $__int_width_
#define $int(N) $identity_type(_BitInt(N))
#define $sint(N) $identity_type(signed _BitInt(N))
#define $uint(N) $identity_type(unsigned _BitInt(N))
#if !defined(_MACROS_HH) && defined(__cplusplus)
#include "macros.hh"
#endif
#endif /* _MACROS_H */

@ -0,0 +1,45 @@
#ifndef _MACROS_HH
#define _MACROS_HH
#ifdef __cplusplus
#include <type_traits>
#include <utility>
#ifndef NOTHING
#define NOTHING
#endif
#define _DEFINE_CLASS(NAME, constexpr, virtual, noexcept, dtor, copy, move, copy_assign, move_assign, ...)
constexpr NAME(NAME &&) noexcept move
constexpr NAME(NAME const&) noexcept copy
constexpr NAME & operator=(NAME &&) noexcept move_assign
constexpr NAME & operator=(NAME const&) noexcept copy_assign
constexpr virtual ~NAME() dtor
#define DEFINE_INTERFACE(...) \
_DEFINE_CLASS(__VA_ARGS__, constexpr, virtual, noexcept, =default, =default;, =default;, =default;, =default;, NOTHING, NOTHING, NOTHING, NOTHING, NOTHING, NOTHING, NOTHING, NOTHING)
#define DEFINE_MOVE_INTERFACE(...) \
_DEFINE_CLASS(__VA_ARGS__, constexpr, virtual, noexcept, =default, =delete;, =default;, =delete;, =default;, NOTHING, NOTHING, NOTHING, NOTHING, NOTHING, NOTHING, NOTHING, NOTHING)
#define FORWARD(T) ::std::forward<decltype(T)>(T)
#define $L(...) { return __VA_ARGS__; }
#define $L_(...) { __VA_ARGS__; }
//#define $CLASS
#define $impl $CLASS::
#define $ctor $CLASS::$CLASS
#define $dtor $CLASS::~$CLASS
#define $ctor_cpoy(n) $ctor($CLASS const& n)
#define $ctor_move(n) $ctor($CLASS && n)
#define $assign $CLASS & operator=
#define $assign_copy(n) $CLASS & $CLASS::operator=($CLASS const& n)
#define $assign_move(n) $CLASS & $CLASS::operator=($CLASS && n)
#elif defined(DEBUG)
#warn "Included C++ header to C TU"
#endif
#endif

@ -0,0 +1,125 @@
#ifndef _TYPES_H
#define _TYPES_H
#include <stdint.h>
#include <stddef.h>
#include <sys/types.h>
#ifdef $int
#define $__bit_int(X) $int(X)
#else
#define $__bit_int(X) _BitInt(X)
#else
#endif
#define $__defint_x(N) \
typedef signed $__bit_int(N) i ## N; \
typedef unsigned $__bit_int(N) u ## N
#define $__defint(N) \
typedef int ## N ## _t i ## N; \
typedef uint ## N ## _T u ## N
#define $__INTS_8 \
$__defint_x(00); \
$__defint_x(01); \
$__defint_x(02); \
$__defint_x(03); \
$__defint_x(04); \
$__defint_x(05); \
$__defint_x(06); \
$__defint_x(07); \
$__defint(8)
#define $__INTS_16 \
$__defint_x(0x9); \
$__defint_x(0xa); \
$__defint_x(0xb); \
$__defint_x(0xc); \
$__defint_x(0xd); \
$__defint_x(0xe); \
$__defint_x(0xf); \
$__defint(16)
#define $__INTS_CHUNK_8F(N, ...) \
/*$__defint_x(0 ## N ## 0);*/ \
$__defint_x(0 ## N ## 1); \
$__defint_x(0 ## N ## 2); \
$__defint_x(0 ## N ## 3); \
$__defint_x(0 ## N ## 4); \
$__defint_x(0 ## N ## 5); \
$__defint_x(0 ## N ## 6); \
$__defint_x(0 ## N ## 7) \
__VA_OPT__(; $__defint(__VA_ARGS__))
#define $__INTS_CHUNK_8(N) \
$__defint_x(0 ## N ## 0); \
$__defint_x(0 ## N ## 1); \
$__defint_x(0 ## N ## 2); \
$__defint_x(0 ## N ## 3); \
$__defint_x(0 ## N ## 4); \
$__defint_x(0 ## N ## 5); \
$__defint_x(0 ## N ## 6); \
$__defint_x(0 ## N ## 7)
#define $__INTS_CHUNK_16(N) \
$__defint_x(0x ## N ## 0); \
$__defint_x(0x ## N ## 1); \
$__defint_x(0x ## N ## 2); \
$__defint_x(0x ## N ## 3); \
$__defint_x(0x ## N ## 4); \
$__defint_x(0x ## N ## 5); \
$__defint_x(0x ## N ## 6); \
$__defint_x(0x ## N ## 7); \
$__defint_x(0x ## N ## 8); \
$__defint_x(0x ## N ## 9); \
$__defint_x(0x ## N ## a); \
$__defint_x(0x ## N ## b); \
$__defint_x(0x ## N ## c); \
$__defint_x(0x ## N ## d); \
$__defint_x(0x ## N ## e); \
$__defint_x(0x ## N ## f)
#define $__INTS_CHUNK_16F(N, d) \
$__defint_x(0x ## N ## 1); \
$__defint_x(0x ## N ## 2); \
$__defint_x(0x ## N ## 3); \
$__defint_x(0x ## N ## 4); \
$__defint_x(0x ## N ## 5); \
$__defint_x(0x ## N ## 6); \
$__defint_x(0x ## N ## 7); \
$__defint_x(0x ## N ## 8); \
$__defint_x(0x ## N ## 9); \
$__defint_x(0x ## N ## a); \
$__defint_x(0x ## N ## b); \
$__defint_x(0x ## N ## c); \
$__defint_x(0x ## N ## d); \
$__defint_x(0x ## N ## e); \
$__defint_x(0x ## N ## f); \
$__defint(d)
// Define `{i,u}{0...128}`
$__INTS_8;
$__INTS_16;
$__INTS_CHUNK_8F(1); //24
$__INTS_CHUNK_16(1);//, 32);
$__INTS_CHUNK_16(2);//, 48);
$__INTS_CHUNK_16(3);//, 64);
$__INTS_CHUNK_16(4);//, 80);
$__INTS_CHUNK_16(5);//, 96);
$__INTS_CHUNK_16(6)//, 128);
typedef signed __int128 i128;
typedef unsigned __int128 u128;
typedef size_t usize;
typedef ssize_t isize;
typedef _Float16 f16;
typedef float f32;
typedef double f64;
typedef long double f128;
#endif /* _TYPES_H */

@ -0,0 +1,51 @@
#include <map>
#include <alloc.h>
extern "C" {
base_allocator::~base_allocator() {}
}
namespace alloc {
// This is the *true manager* of the allocation arena, when it is destroyed, the memory is cleared
struct FrozenAllocator::anon_raw_secmem {
// Base class for all deallocations that happen within an `anon_raw_secmem` (managed, static, polymorphic, unmanaged, etc.) (TODO: See below, the base should be moved into the header so typed children can be templated...)
struct deleter { //XXX: TODO: How to define this type in the header file? It should be the virt base of all templated deleters,
//TODO: XXX: Rework this design, the deleter must be typed for the value it is deleting... This virtual base should be in the header, and should just contain the shared_ptr reference to anon_raw_secmem.
virtual ~deleter() = default;
protected:
// To prevent anon_raw_secmem being destroyed while there are still allocated values, the base class for the deleter for those values contains a refcount. e.g: `std::unique_ptr<T, deleter_for<T>>` where: `deleter_for<T> final : public deleter { virtual ~deleter_for(); ... };`, or `std::shared_ptr<T>`, where: `std::shared_ptr<value_with_deleter<T>>` aliases-ctor(`old, old->value_ptr()`) -> `std::shared_ptr<T>`
std::shared_ptr<anon_raw_secmem> m_manager_ref;
};
~anon_raw_secmem() {
//TODO: Clear and `munmap()` the used page(s)
//XXX: Due to how this is managed via `shared_ptr<>`, it is UB for this to be called *before* all actual allocation of the memory are unallocated (via `deleter`, which they must *all* be managed by.
}
};
struct FrozenAllocator::alloc_info {
};
struct FrozenAllocator::alloc_value {
};
struct FrozenAllocator::_impl {
std::shared_ptr<anon_raw_secmem> m_manager;
std::map<alloc_info, std::unique_ptr<alloc_value>> m_values;
};
#define $CLASS FrozenAllocator
$ctor_move(m) noexcept
: inner_(std::move(m.inner_)) {}
$assign_move(m) {
if($LIKELY(this != &m))
inner_ = std::move(m.inner_);
return *this;
}
$dtor() {}
#undef $CLASS /* FrozenAllocator */
}

@ -0,0 +1,19 @@
#include <stdexcept>
#include <error.hh>
#include <init.hh>
int main(int argc, char** argv) try {
init::ensure();
return 0;
} catch(error::Fatal& fatal) {
} catch(error::Error& err) {
} catch(std::exception& se) {
} catch(...) {
}
Loading…
Cancel
Save