[+] Aurora::Memory::HeapProxy[Ex]

[*] Heap improvements
[+] Heap::NewClassArray
This commit is contained in:
Reece Wilson 2024-01-16 21:11:08 +00:00
parent 997d4a2eca
commit c3e0418e1a
6 changed files with 570 additions and 59 deletions

View File

@ -9,10 +9,12 @@
namespace Aurora::Memory
{
struct ProxyHeap;
struct Heap
{
virtual AuSPtr<Heap> AllocateDivision(AuUInt32 heap, AuUInt32 alignment = 32) = 0;
virtual Types::size_t GetChunkSize(const void *head) = 0;
virtual Types::size_t GetChunkSize(const void *pHead) = 0;
virtual HeapStats &GetStats() = 0;
template<typename T = void *>
@ -59,51 +61,56 @@ namespace Aurora::Memory
}
template<typename T>
T ZRealloc(T in, Types::size_t length)
T ZRealloc(T pHead, Types::size_t length)
{
return reinterpret_cast<T>(_ZRealloc(reinterpret_cast<void *>(in), length));
return reinterpret_cast<T>(_ZRealloc(reinterpret_cast<void *>(pHead), length));
}
template<typename T>
T ZRealloc(T in, Types::size_t length, Types::size_t alloc)
T ZRealloc(T pHead, Types::size_t length, Types::size_t alloc)
{
return reinterpret_cast<T>(_ZRealloc(reinterpret_cast<void *>(in), length), alloc);
return reinterpret_cast<T>(_ZRealloc(reinterpret_cast<void *>(pHead), length), alloc);
}
template<typename T>
T FRealloc(T in, Types::size_t length)
T FRealloc(T pHead, Types::size_t length)
{
return reinterpret_cast<T>(_FRealloc(reinterpret_cast<void *>(in), length));
return reinterpret_cast<T>(_FRealloc(reinterpret_cast<void *>(pHead), length));
}
template<typename T>
T FRealloc(T in, Types::size_t length, Types::size_t alloc)
T FRealloc(T pHead, Types::size_t length, Types::size_t alloc)
{
return reinterpret_cast<T>(_FRealloc(reinterpret_cast<void *>(in), length), alloc);
return reinterpret_cast<T>(_FRealloc(reinterpret_cast<void *>(pHead), length), alloc);
}
template<typename T>
void Free(T in)
void Free(T pHead)
{
_Free(reinterpret_cast<void *>(in));
_Free(reinterpret_cast<void *>(pHead));
}
template <class T, class ...Args>
AuSPtr<T> NewClass(Args &&...args)
{
void *pPtr;
static const auto kAlignment = AuMax(alignof(T), sizeof(void *));
AuUInt8 *pPtr;
if constexpr (AuIsClass_v<T>)
if constexpr (AuIsClass_v<T>
#if !defined(AURT_HEAP_NO_STL)
&& !std::is_trivially_constructible_v<T>
#endif
)
{
pPtr = this->FAlloc<void *>(sizeof(T));
pPtr = this->FAlloc<AuUInt8 *>(sizeof(T) + kAlignment, kAlignment);
if (pPtr)
{
new (pPtr) T(AuForward<Args &&>(args)...);
new (pPtr + kAlignment) T(AuForward<Args &&>(args)...);
}
}
else
{
pPtr = this->ZAlloc<void *>(sizeof(T));
pPtr = this->ZAlloc<AuUInt8 *>(sizeof(T) + kAlignment, kAlignment);
}
if (!pPtr)
@ -111,45 +118,155 @@ namespace Aurora::Memory
return {};
}
return ToSmartPointer((T *)pPtr, true);
auto pThat = this->GetSelfReferenceRaw();
if (pThat &&
pThat != this)
{
*(void **)pPtr = pThat;
}
else
{
*(void **)pPtr = this;
}
return AuSPtr<T>((T *)(pPtr + kAlignment), &Heap::DeleteThat<T>);
}
template <class T, class ...Args>
AuSPtr<T> NewClassArray(AuUInt uElements, Args &&... fillCtr)
{
static const auto kAlignment = AuMax(alignof(T), sizeof(void *) * 2);
AuUInt8 *pPtr;
if (!uElements)
{
return {};
}
if constexpr (AuIsClass_v<T>
#if !defined(AURT_HEAP_NO_STL)
&& !std::is_trivially_constructible_v<T>
#endif
)
{
if (bool(pPtr = this->FAlloc<AuUInt8 *>((sizeof(T) * uElements) + kAlignment, kAlignment)))
{
for (AU_ITERATE_N(i, uElements))
{
new (pPtr + kAlignment + (sizeof(T) * i)) T(AuForward<Args &&>(fillCtr)...);
}
}
}
else
{
if (bool(pPtr = this->ZAlloc<AuUInt8 *>((sizeof(T) * uElements) + kAlignment, kAlignment)))
{
if constexpr (sizeof...(Args) != 0)
{
#if defined(AURT_HEAP_NO_STL)
static_assert(false);
#else
auto pElements = (T *)(pPtr + kAlignment);
std::fill(pElements, pElements + uElements, AuForward<Args &&>(fillCtr)...);
#endif
}
}
}
if (!pPtr)
{
return {};
}
auto pVoids = (void **)pPtr;
auto pThat = this->GetSelfReferenceRaw();
if (pThat &&
pThat != this)
{
pVoids[0] = pThat;
}
else
{
pVoids[0] = this;
}
pVoids[1] = (void *)uElements;
return AuSPtr<T>((T *)(pPtr + kAlignment), &Heap::DeleteThatArray<T>);
}
template<typename T>
AuSPtr<T> ToSmartPointer(T *in, bool bPinThis)
static AuSPtr<T> ToSmartPointer(AuSPtr<Heap> heap,
T *pHead,
bool bPinHeap = true)
{
if (in == nullptr) return {};
auto heapHandle = bPinThis ? GetSelfReference() : AuSPtr<Heap> {};
return AuSPtr<T>(in,
[heapHandle, in, this](T *delt)
auto handle = bPinHeap ?
heap :
AuSPtr<Heap> {};
auto pHeap = heap.get();
return AuSPtr<T>(pHead,
[handle, pHeap](T *pDeleteMe)
{
if constexpr (AuIsClass_v<T>)
{
delt->~T();
}
this->Free(delt);
});
}
template<typename T>
static AuSPtr<T> ToSmartPointer(AuSPtr<Heap> heap, T *in, bool pinHeap = true)
{
auto handle = pinHeap ? heap : AuSPtr<Heap> {};
auto ptr = heap.get();
return AuSPtr<T>(in,
[handle, ptr](T *delt)
{
if constexpr (AuIsClass_v<T>)
if constexpr (AuIsClass_v<T>
#if !defined(AURT_HEAP_NO_STL)
&& !std::is_trivially_destructible_v<T>
#endif
)
{
delt->~T();
pDeleteMe->~T();
}
ptr->Free(delt);
pHeap->Free(pDeleteMe);
});
}
private:
protected:
template <typename T>
static void DeleteThat(T *pThat)
{
static const auto kAlignment = AuMax(alignof(T), sizeof(void *));
if constexpr (AuIsClass_v<T>
#if !defined(AURT_HEAP_NO_STL)
&& !std::is_trivially_destructible_v<T>
#endif
)
{
pThat->~T();
}
auto &pHeap = *(Heap **)(((char *)pThat) - kAlignment);
pHeap->_Free(&pHeap);
}
template <typename T>
static void DeleteThatArray(T *pThat)
{
static const auto kAlignment = AuMax(alignof(T), sizeof(void *) * 2);
auto pVoids = (void **)(((char *)pThat) - kAlignment);
auto pHeap = (Heap *)pVoids[0];
auto uCount = (AuUInt)pVoids[1];
if constexpr (AuIsClass_v<T>
#if !defined(AURT_HEAP_NO_STL)
&& !std::is_trivially_destructible_v<T>
#endif
)
{
for (AU_ITERATE_N(i, uCount))
{
auto &refElement = pThat[i];
refElement.~T();
}
}
pHeap->_Free(pVoids);
}
friend struct ProxyHeap;
virtual AuSPtr<Heap> GetSelfReference() = 0; // may return empty/default. not all heaps are sharable.
virtual Heap *GetSelfReferenceRaw() = 0;
virtual AU_ALLOC void *_ZAlloc(Types::size_t uLength) = 0;
virtual AU_ALLOC void *_ZAlloc(Types::size_t uLength, Types::size_t align) = 0;
@ -171,7 +288,7 @@ namespace Aurora::Memory
Allocates uLength amount of contiguous virtual memory
@warning Heaps are guaranteed to outlive their allocations; heaps are the one object that effectively own a single reference count on themselves.
Requesting termination before all of its' memory has been free will result, in at worst, a warning.
Requesting termination before all of its' memory has been free will result, pHead at worst, a warning.
Expect to leak unless all allocs have been paired by a free.
I do not expect to implement force frees simply because all our primary use cases keep track of dtors to forcefully release leaked objects.
@ -185,4 +302,10 @@ namespace Aurora::Memory
// AllocHeap but use mimalloc (or the default allocator) instead
AUKN_SHARED_API(AllocHeapMimalloc, Heap, AuUInt uLength);
// Proxies an existing heap with encapsulated statistics
AUKN_SHARED_API(HeapProxy, Heap, const AuSPtr<Heap> &pHead);
// Proxies an existing heap with encapsulated statistics and leak detector
AUKN_SHARED_API(HeapProxyEx, Heap, const AuSPtr<Heap> &pHead, LeakFinderAlloc_f pfAlloc, LeakFinderFree_f pfFree);
}

View File

@ -9,6 +9,13 @@
#include <Aurora/Data/Data.hpp>
namespace Aurora::Memory
{
using LeakFinderAlloc_f = void(__cdecl *)(void *, AuUInt);
using LeakFinderFree_f = void(__cdecl *)(void *);
using MemoryLowNotification_f = void(__cdecl *)(AuUInt, int);
}
#include "MemRef.hpp"
#include "HeapStats.hpp"
#include "Heap.hpp"
@ -21,10 +28,6 @@
namespace Aurora::Memory
{
using LeakFinderAlloc_f = void(__cdecl *)(void *, AuUInt);
using LeakFinderFree_f = void(__cdecl *)(void *);
using MemoryLowNotification_f = void(__cdecl *)(AuUInt, int);
AUKN_SYM void SetLeakFinder(LeakFinderAlloc_f pAlloc,
LeakFinderFree_f pFree);

View File

@ -75,6 +75,11 @@ namespace Aurora::Memory
return {};
}
Heap *GetSelfReferenceRaw() override
{
return this;
}
void UpdateStats() override
{
auto other = AuDebug::gReserveHeap->GetStats();

View File

@ -20,8 +20,7 @@ namespace Aurora::Memory
{
static AuUInt32 RoundPageUp(AuUInt32 value)
{
auto pageMask = HWInfo::GetPageSize() - 1;
return (value + pageMask) & ~(pageMask);
return AuPageRoundUp(value, HWInfo::GetPageSize());
}
static void *HeapLargeAllocate(AuUInt length)
@ -90,6 +89,7 @@ namespace Aurora::Memory
void *_FRealloc(void *buffer, Types::size_t length, Types::size_t align) override;
void _Free(void *buffer) override;
AuSPtr<Heap> GetSelfReference() override;
Heap *GetSelfReferenceRaw() override;
void TryRelease();
void DecrementUsers();
@ -147,24 +147,35 @@ namespace Aurora::Memory
bool InternalHeap::Init(AuUInt length, void *ptr)
{
SysAssert(!this->base_, "heap already initialized");
SysAssert(length, "invalid heap allocation");
this->length_ = length;
if (ptr)
{
this->base_ = ptr;
this->length_ = length;
this->ownsMemory_ = false;
}
else
{
this->base_ = HeapLargeAllocate(length);
if (!base_) return false;
if (length <= 4096)
{
length = 4086;
}
if (!(this->base_ = HeapLargeAllocate(length)))
{
return false;
}
this->ownsMemory_ = true;
this->length_ = length;
}
this->heap_ = o1heapInit(this->base_, length);
if (!(this->heap_ = o1heapInit(this->base_, length)))
{
return false;
}
return true;
}
@ -218,7 +229,7 @@ namespace Aurora::Memory
void *InternalHeap::_FAlloc(Types::size_t length, Types::size_t align)
{
SysAssert(align < O1HEAP_ALIGNMENT, "heap wrapping is unsupported, alignment past the supported 2^x alignment is not possible");
SysAssert(align <= O1HEAP_ALIGNMENT, "heap wrapping is unsupported, alignment past the supported 2^x alignment is not possible");
return this->_FAlloc(length);
}
@ -241,7 +252,7 @@ namespace Aurora::Memory
void *InternalHeap::_ZAlloc(Types::size_t length, Types::size_t align)
{
SysAssert(align < O1HEAP_ALIGNMENT, "heap wrapping is unsupported, alignment past the supported 2^x alignment is not possible");
SysAssert(align <= O1HEAP_ALIGNMENT, "heap wrapping is unsupported, alignment past the supported 2^x alignment is not possible");
return _ZAlloc(length);
}
@ -261,7 +272,7 @@ namespace Aurora::Memory
void *InternalHeap::_ZRealloc(void *buffer, Types::size_t length, Types::size_t align)
{
SysAssert(align < O1HEAP_ALIGNMENT, "heap wrapping is unsupported, alignment past the supported 2^x alignment is not possible");
SysAssert(align <= O1HEAP_ALIGNMENT, "heap wrapping is unsupported, alignment past the supported 2^x alignment is not possible");
return this->_ZRealloc(buffer, length);
}
@ -281,7 +292,7 @@ namespace Aurora::Memory
void *InternalHeap::_FRealloc(void *buffer, Types::size_t length, Types::size_t align)
{
SysAssert(align < O1HEAP_ALIGNMENT, "heap wrapping is unsupported, alignment past the supported 2^x alignment is not possible");
SysAssert(align <= O1HEAP_ALIGNMENT, "heap wrapping is unsupported, alignment past the supported 2^x alignment is not possible");
return this->_FRealloc(buffer, length);
}
@ -357,6 +368,11 @@ namespace Aurora::Memory
}
}
Heap *InternalHeap::GetSelfReferenceRaw()
{
return this;
}
AUKN_SYM Heap *AllocHeapNew(AuUInt size)
{
auto heap = _new InternalHeap();

323
Source/Memory/HeapProxy.cpp Normal file
View File

@ -0,0 +1,323 @@
/***
Copyright (C) 2024 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: HeapProxy.cpp
Date: 2024-1-16
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "Memory.hpp"
#include "Heap.hpp"
#include "HeapProxy.hpp"
namespace Aurora::Memory
{
ProxyHeap::ProxyHeap(std::shared_ptr<Heap> pHeap,
LeakFinderAlloc_f pAlloc,
LeakFinderFree_f pFree) :
pHeap(pHeap),
pAlloc(pAlloc),
pFree(pFree)
{
}
void ProxyHeap::UpdateStats()
{
auto &stats = this->pHeap->GetStats();
this->stats.uBytesLiveCounter = this->uBytesAllocated;
this->stats.qwBytesAllocatedLifetime = this->uBytesLifetime;
this->stats.uBytesPeakCounter = this->uBytesPeak;
this->stats.qwBytesFreeLifetime = this->uBytesFree;
this->stats.uBytesCapacity = stats.uBytesCapacity;
}
AuSPtr<Heap> ProxyHeap::AllocateDivision(AuUInt32 heap, AuUInt32 alignment)
{
return this->pHeap->AllocateDivision(heap, alignment);
}
void *ProxyHeap::_ZAlloc(Types::size_t uLength)
{
if (auto pThat = this->pHeap->ZAlloc(uLength))
{
auto uLengthCurrent = this->GetChunkSize(pThat);
AuAtomicAdd(&this->uBytesLifetime, uLengthCurrent);
AuAtomicAdd(&this->uBytesAllocated, uLengthCurrent);
this->uBytesPeak = AuMax(this->uBytesPeak, this->uBytesAllocated);
if (this->pAlloc)
{
this->pAlloc(pThat, uLength);
}
return pThat;
}
else
{
return {};
}
}
void *ProxyHeap::_ZAlloc(Types::size_t uLength, Types::size_t align)
{
if (auto pThat = this->pHeap->ZAlloc(uLength, align))
{
auto uLengthCurrent = this->GetChunkSize(pThat);
AuAtomicAdd(&this->uBytesLifetime, uLengthCurrent);
AuAtomicAdd(&this->uBytesAllocated, uLengthCurrent);
this->uBytesPeak = AuMax(this->uBytesPeak, this->uBytesAllocated);
if (this->pAlloc)
{
this->pAlloc(pThat, uLength);
}
return pThat;
}
else
{
return {};
}
}
Types::size_t ProxyHeap::GetChunkSize(const void *head)
{
return this->pHeap->GetChunkSize(head);
}
void *ProxyHeap::_FAlloc(Types::size_t uLength)
{
if (auto pThat = this->pHeap->_FAlloc(uLength))
{
auto uLengthCurrent = this->GetChunkSize(pThat);
AuAtomicAdd(&this->uBytesLifetime, uLengthCurrent);
AuAtomicAdd(&this->uBytesAllocated, uLengthCurrent);
this->uBytesPeak = AuMax(this->uBytesPeak, this->uBytesAllocated);
if (this->pAlloc)
{
this->pAlloc(pThat, uLength);
}
return pThat;
}
else
{
return {};
}
}
void *ProxyHeap::_FAlloc(Types::size_t uLength, Types::size_t align)
{
if (auto pThat = this->pHeap->_FAlloc(uLength, align))
{
auto uLengthCurrent = this->GetChunkSize(pThat);
AuAtomicAdd(&this->uBytesLifetime, uLengthCurrent);
AuAtomicAdd(&this->uBytesAllocated, uLengthCurrent);
this->uBytesPeak = AuMax(this->uBytesPeak, this->uBytesAllocated);
if (this->pAlloc)
{
this->pAlloc(pThat, uLength);
}
return pThat;
}
else
{
return {};
}
}
void *ProxyHeap::_ZRealloc(void *pHead, Types::size_t uLength, Types::size_t align)
{
if (pHead)
{
auto uLengthCurrent = this->GetChunkSize(pHead);
if (auto pThat = this->pHeap->_ZRealloc(pHead, uLength, align))
{
auto uLengthNext = this->GetChunkSize(pThat);
AuAtomicAdd(&this->uBytesFree, uLengthCurrent);
AuAtomicAdd(&this->uBytesLifetime, uLengthNext);
AuAtomicAdd(&this->uBytesAllocated, decltype(this->uBytesAllocated)(uLengthNext) - decltype(this->uBytesAllocated)(uLengthCurrent));
this->uBytesPeak = AuMax(this->uBytesPeak, this->uBytesAllocated);
if (this->pAlloc)
{
this->pFree(pHead);
this->pAlloc(pThat, uLength);
}
return pThat;
}
else
{
return {};
}
}
else
{
return nullptr;
}
}
void *ProxyHeap::_ZRealloc(void *pHead, Types::size_t uLength)
{
if (pHead)
{
auto uLengthCurrent = this->GetChunkSize(pHead);
if (auto pThat = this->pHeap->_ZRealloc(pHead, uLength))
{
auto uLengthNext = this->GetChunkSize(pThat);
AuAtomicAdd(&this->uBytesFree, uLengthCurrent);
AuAtomicAdd(&this->uBytesLifetime, uLengthNext);
AuAtomicAdd(&this->uBytesAllocated, decltype(this->uBytesAllocated)(uLengthNext) - decltype(this->uBytesAllocated)(uLengthCurrent));
this->uBytesPeak = AuMax(this->uBytesPeak, this->uBytesAllocated);
if (this->pAlloc)
{
this->pFree(pHead);
this->pAlloc(pThat, uLength);
}
return pThat;
}
else
{
return {};
}
}
else
{
return nullptr;
}
}
void *ProxyHeap::_FRealloc(void *pHead, Types::size_t uLength, Types::size_t align)
{
if (pHead)
{
auto uLengthCurrent = this->GetChunkSize(pHead);
if (auto pThat = this->pHeap->_FRealloc(pHead, uLength, align))
{
auto uLengthNext = this->GetChunkSize(pThat);
AuAtomicAdd(&this->uBytesFree, uLengthCurrent);
AuAtomicAdd(&this->uBytesLifetime, uLengthNext);
AuAtomicAdd(&this->uBytesAllocated, decltype(this->uBytesAllocated)(uLengthNext) - decltype(this->uBytesAllocated)(uLengthCurrent));
this->uBytesPeak = AuMax(this->uBytesPeak, this->uBytesAllocated);
if (this->pAlloc)
{
this->pFree(pHead);
this->pAlloc(pThat, uLength);
}
return pThat;
}
else
{
return {};
}
}
else
{
return nullptr;
}
}
void *ProxyHeap::_FRealloc(void *pHead, Types::size_t uLength)
{
if (pHead)
{
auto uLengthCurrent = this->GetChunkSize(pHead);
if (auto pThat = this->pHeap->_FRealloc(pHead, uLength))
{
auto uLengthNext = this->GetChunkSize(pThat);
AuAtomicAdd(&this->uBytesFree, uLengthCurrent);
AuAtomicAdd(&this->uBytesLifetime, uLengthNext);
AuAtomicAdd(&this->uBytesAllocated, decltype(this->uBytesAllocated)(uLengthNext) - decltype(this->uBytesAllocated)(uLengthCurrent));
this->uBytesPeak = AuMax(this->uBytesPeak, this->uBytesAllocated);
if (this->pAlloc)
{
this->pFree(pHead);
this->pAlloc(pThat, uLength);
}
return pThat;
}
else
{
return {};
}
}
else
{
return nullptr;
}
}
void ProxyHeap::_Free(void *pHead)
{
if (pHead)
{
auto uLengthCurrent = this->GetChunkSize(pHead);
AuAtomicAdd(&this->uBytesFree, uLengthCurrent);
AuAtomicSub(&this->uBytesAllocated, uLengthCurrent);
if (this->pAlloc)
{
this->pFree(pHead);
}
}
}
AuSPtr<Heap> ProxyHeap::GetSelfReference()
{
return this->pHeap;
}
Heap *ProxyHeap::GetSelfReferenceRaw()
{
return this->pHeap.get();
}
AUKN_SYM Heap *HeapProxyNew(const AuSPtr<Heap> &pHeap)
{
if (!pHeap)
{
SysPushErrorArg();
return {};
}
auto pRetHeap = _new ProxyHeap(pHeap);
if (!pRetHeap)
{
SysPushErrorMemory();
return nullptr;
}
return pRetHeap;
}
AUKN_SYM void HeapProxyRelease(Heap *heap)
{
AuSafeDelete<ProxyHeap *>(heap);
}
AUKN_SYM Heap *HeapProxyExNew(const AuSPtr<Heap> &pHeap,
LeakFinderAlloc_f pAlloc,
LeakFinderFree_f pFree)
{
if (!pHeap ||
!pAlloc ||
!pFree)
{
SysPushErrorArg();
return {};
}
auto pRetHeap = _new ProxyHeap(pHeap, pAlloc, pFree);
if (!pRetHeap)
{
SysPushErrorMemory();
return nullptr;
}
return pRetHeap;
}
AUKN_SYM void HeapProxyExRelease(Heap *heap)
{
AuSafeDelete<ProxyHeap *>(heap);
}
}

View File

@ -0,0 +1,41 @@
/***
Copyright (C) 2024 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: HeapProxy.hpp
Date: 2024-1-16
Author: Reece
***/
namespace Aurora::Memory
{
struct ProxyHeap : BaseHeap
{
std::shared_ptr<Heap> pHeap;
LeakFinderAlloc_f pAlloc;
LeakFinderFree_f pFree;
AuUInt64 uBytesAllocated {}; // current
AuUInt64 uBytesPeak {}; // max
AuUInt64 uBytesFree {}; // free count
AuUInt64 uBytesLifetime {}; // alloc count
ProxyHeap(std::shared_ptr<Heap> pHeap,
LeakFinderAlloc_f pAlloc = {},
LeakFinderFree_f pFree = {});
void UpdateStats() override;
AuSPtr<Heap> GetSelfReference() override;
Heap *GetSelfReferenceRaw() override;
AuSPtr<Heap> AllocateDivision(AuUInt32 heap, AuUInt32 alignment) override;
void *_ZAlloc(Types::size_t length) override;
void *_ZAlloc(Types::size_t length, Types::size_t align) override;
Types::size_t GetChunkSize(const void *head) override;
void *_FAlloc(Types::size_t length) override;
void *_FAlloc(Types::size_t length, Types::size_t align) override;
void *_ZRealloc(void *buffer, Types::size_t length, Types::size_t align) override;
void *_ZRealloc(void *buffer, Types::size_t length) override;
void *_FRealloc(void *buffer, Types::size_t length, Types::size_t align) override;
void *_FRealloc(void *buffer, Types::size_t length) override;
void _Free(void *buffer) override;
};
}