//! 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>
# include <pair>
# endif
# include "constraints.hh"
# 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 ;
// Untyped deleter
struct deleter {
friend class anon_raw_secmem ;
constexpr deleter ( const deleter & ) noexcept = default ;
constexpr deleter & operator = ( const deleter & ) noexcept = default ;
constexpr deleter ( deleter & & ) noexcept = default ;
constexpr deleter & operator = ( deleter & & ) noexcept = default ;
[ [ gnu : : nonnull ( 2 ) ] ]
constexpr void operator ( ) ( void * restrict p ) const noexcept { apply_delete ( p ) ; }
virtual ~ deleter ( ) ;
protected :
explicit deleter ( std : : shared_ptr < anon_raw_secmem > & & p ) ;
[ [ gnu : : nonnull ( 2 ) ] ]
virtual std : : pair < void * > apply_delete ( void * restrict , bool = true ) const noexcept ;
virtual void scramble_memory ( std : : initializer_list < std : : pair < void * > > ptrs ) const noexcept ;
inline void scramble_memory ( std : : pair < void * > ptr ) const noexcept { return scramble_memory ( { ptr } ) ; }
// 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 ;
} ;
struct alloc_vt ;
struct alloc_info ;
struct alloc_value ;
template < typename >
struct deleter_for ;
template < typename >
struct deleter_for_value ;
public :
FrozenAllocator ( FrozenAllocator & & ) noexcept ;
FrozenAllocator & operator = ( FrozenAllocator & & ) ;
FrozenAllocator ( const FrozenAllocator & ) = delete ;
FrozenAllocator & operator = ( const FrozenAllocator & ) = delete ;
virtual ~ FrozenAllocator ( ) ;
private :
std : : unique_ptr < _impl > inner_ ;
/// Manages everything about the actual allocations
//std::shared_ptr<anon_raw_secmem> m_manager;
/// A map of values inside the allocator. This is destroyed in reverse order, meaning all the living values are destroyed *before* `m_manager`'d destruction deallocates them.
//std::map<alloc_info, std::unique_ptr<alloc_value, deleter>> m_values;
} ;
template < typename T >
struct FrozenAllocator : : deleter_for
: virtual deleter
{
inline deleter_for ( std : : shared_ptr < anon_raw_secmem > & & m )
: deleter ( std : : move ( m ) ) { }
virtual ~ deleter_for ( ) = default ; // This will use deleter's dtor to remove allocations.
[ [ gnu : : nonnull ( 2 ) ] ]
inline void operator ( ) ( T * ptr ) const noexcept { apply_delete ( deleter : : erase_type_unsafe ( ptr ) ) ; }
protected :
inline virtual void apply_delete_typed ( T * ptr ) const noexcept {
ptr - > ~ T ( ) ;
}
private :
[ [ gnu : : nonnull ( 2 ) ] ]
inline std : : pair < void * > apply_delete ( void * restrict up ) const noexcept override final {
if constexpr ( std : : is_trivially_destructible_v < T > ) {
return deleter : : apply_delete ( up ) ; // If the dtor is trivial, ignore it and use default behaviour.
} else {
auto pair = deleter : : apply_delete ( up , false ) ; // Unlock the memory and remove the allocation from `anon_raw_secmem`, but do *not* bzero it.
apply_delete_typed ( static_cast < T * > ( up ) ) ; // Apply the destructor for `T` to the alligned pointer `up`.
deleter : : scramble_memory ( pair ) ; // *now* bzero the unaligned pointer (full range, including alignment padding.)
return pair ;
}
}
} ;
template < typename T >
struct FrozenAllocator : : deleter_for_value final : deleter_for < T > {
//TODO: Re-work this? Is it useful? Is it needed?
inline virtual ~ deleter_for_value ( ) {
deleter_for < T > : : apply_delete ( m_value_ptr ) ;
}
private :
T * m_value_ptr ;
} ;
}
# endif
# endif /* _ALLOC_H */