You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
4.5 KiB

//! 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 "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;
constexpr void operator()(void* restrict p) const noexcept { return apply_delete(p); }
virtual ~deleter();
protected:
explicit deleter(std::shared_ptr<anon_raw_secmem>&& p);
template<where::is_polymorphic T>
constexpr static void* erase_type(T* ptr) noexcept { return dynamic_cast<void*>(ptr); }
template<where::is_polymorphic T>
constexpr static void const* erase_type(T const* ptr) noexcept { return dynamic_cast<void const*>(ptr); }
template<typename T>
constexpr static void* erase_type_unsafe(T* ptr) noexcept { return static_cast<void*>(ptr); }
template<typename T>
constexpr static T* add_type_unsafe(void* ptr) noexcept { return static_cast<T*>(ptr); }
template<typename T>
constexpr static void const* erase_type_unsafe(T const* ptr) noexcept { return static_cast<void const*>(ptr); }
template<typename T>
constexpr static T const* add_type_unsafe(void const* ptr) noexcept { return static_cast<T const*>(ptr); }
virtual void apply_delete(void* restrict, bool = true) const noexcept;
inline deleter* finalizer_group_id() const noexcept {
return m_group_ptr ?: static_cast<deleter*>(this);
}
// See dtor impl
void apply_finalizer_group(std::initializer_list<void*> = {}) noexcept;
void apply_finalizer_now(std::initializer_list<void*> = {}) noexcept;
// 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;
private:
// Used to sync `this`'s group across copies.
deleter* m_group_ptr;
};
struct alloc_info;
struct alloc_value;
template<typename T>
struct 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.
inline void operator()(T* ptr) const noexcept { return apply_delete(deleter::erase_type_unsafe(ptr)); }
protected:
inline virtual void apply_delete_typed(T* ptr) const noexcept {
ptr->~T();
}
private:
inline void apply_delete(void* restrict up) const noexcept override final {
if constexpr(std::is_trivially_destructible_v<T>) {
deleter::apply_delete(up); // If the dtor is trivial, ignore it and use default behaviour.
} else {
deleter::apply_delete(up, false); // Unlock the memory, but do *not* add it to this deleter's finalizer group.
apply_delete_typed(static_cast<T*>(up)); // Apply the destructor for `T`
deleter::apply_finalizer_now({up}); // Get `anon_raw_secmem` to deallocate the memory *now*, instead of at destruction.
}
}
};
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;
};
}
#endif
#endif /* _ALLOC_H */