feat(memory): complete low-level memory management utilities

This commit is contained in:
_Redstone_c_ 2022-03-18 20:17:28 +08:00
parent 94416676d2
commit cd1a8da1a8
6 changed files with 403 additions and 0 deletions

View File

@ -0,0 +1,85 @@
#include "Memory/Memory.h"
#include "Memory/Alignment.h"
#if PLATFORM_WINDOWS
#include <corecrt_malloc.h>
#endif
NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)
NAMESPACE_BEGIN(Memory)
void* Malloc(size_t Count, size_t Alignment)
{
const size_t MinimumAlignment = Count >= 16 ? 16 : 8;
Alignment = MinimumAlignment > Alignment ? MinimumAlignment : Alignment;
void* Result = nullptr;
#if PLATFORM_WINDOWS
if (Count != 0) Result = _aligned_malloc(Count, Alignment);
#else
void* Ptr = SystemMalloc(Count + Alignment + sizeof(void*) + sizeof(size_t));
if (Ptr)
{
Result = Align(reinterpret_cast<uint8*>(Ptr) + sizeof(void*) + sizeof(size_t), Alignment);
*reinterpret_cast<void**>(reinterpret_cast<uint8*>(Result) - sizeof(void*)) = Ptr;
*reinterpret_cast<size_t*>(reinterpret_cast<uint8*>(Result) - sizeof(void*) - sizeof(size_t)) = Count;
}
#endif
return Result;
}
void* Realloc(void* Ptr, size_t Count, size_t Alignment)
{
const size_t MinimumAlignment = Count >= 16 ? 16 : 8;
Alignment = MinimumAlignment > Alignment ? MinimumAlignment : Alignment;
if (Ptr && Count)
{
#if PLATFORM_WINDOWS
return _aligned_realloc(Ptr, Count, Alignment);
#else
void* Result = Malloc(Count, Alignment);
size_t PtrSize = *reinterpret_cast<size_t*>(reinterpret_cast<uint8*>(Ptr) - sizeof(void*) - sizeof(size_t));
Memcpy(Result, Ptr, Count < PtrSize ? Count : PtrSize);
Free(Ptr);
return Result;
#endif
}
else if (Ptr == nullptr)
{
return Malloc(Count, Alignment);
}
else
{
Free(Ptr);
return nullptr;
}
}
void Free(void* Ptr)
{
#if PLATFORM_WINDOWS
_aligned_free(Ptr);
#else
SystemFree(*reinterpret_cast<void**>(reinterpret_cast<uint8*>(Ptr) - sizeof(void*)));
#endif
}
size_t QuantizeSize(size_t Count, size_t Alignment)
{
return Count;
}
NAMESPACE_END(Memory)
NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END
REPLACEMENT_OPERATOR_NEW_AND_DELETE

View File

@ -0,0 +1,142 @@
#include "Testing/MemoryTesting.h"
#include "Memory/Memory.h"
#include "Memory/Alignment.h"
#include "Miscellaneous/AssertionMacros.h"
NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)
void TestMemory()
{
TestAlignment();
TestMemoryBuffer();
TestMemoryMalloc();
}
void TestAlignment()
{
int32 Unaligned = 0xAAAA;
int32 Aligned8 = Memory::Align(Unaligned, 8);
int32 Aligned16 = Memory::Align(Unaligned, 16);
int32 Aligned32 = Memory::Align(Unaligned, 32);
int32 Aligned64 = Memory::Align(Unaligned, 64);
int32 AlignedDown8 = Memory::AlignDown(Unaligned, 8);
int32 AlignedDown16 = Memory::AlignDown(Unaligned, 16);
int32 AlignedDown32 = Memory::AlignDown(Unaligned, 32);
int32 AlignedDown64 = Memory::AlignDown(Unaligned, 64);
int32 AlignedArbitrary8 = Memory::AlignArbitrary(Unaligned, 8);
int32 AlignedArbitrary16 = Memory::AlignArbitrary(Unaligned, 16);
int32 AlignedArbitrary32 = Memory::AlignArbitrary(Unaligned, 32);
int32 AlignedArbitrary64 = Memory::AlignArbitrary(Unaligned, 64);
always_check((Memory::IsAligned(Aligned8, 8) && Aligned8 > Unaligned));
always_check((Memory::IsAligned(Aligned16, 16) && Aligned16 > Unaligned));
always_check((Memory::IsAligned(Aligned32, 32) && Aligned32 > Unaligned));
always_check((Memory::IsAligned(Aligned64, 64) && Aligned64 > Unaligned));
always_check((Memory::IsAligned(Aligned8, 8) && AlignedDown8 < Unaligned));
always_check((Memory::IsAligned(Aligned16, 16) && AlignedDown16 < Unaligned));
always_check((Memory::IsAligned(Aligned32, 32) && AlignedDown32 < Unaligned));
always_check((Memory::IsAligned(Aligned64, 64) && AlignedDown64 < Unaligned));
always_check((Memory::IsAligned(AlignedArbitrary8, 8)));
always_check((Memory::IsAligned(AlignedArbitrary16, 16)));
always_check((Memory::IsAligned(AlignedArbitrary32, 32)));
always_check((Memory::IsAligned(AlignedArbitrary64, 64)));
}
void TestMemoryBuffer()
{
int64 TempA;
int64 TempB;
int64 TempC;
int64 TempD;
uint8* PtrA = reinterpret_cast<uint8*>(&TempA);
uint8* PtrB = reinterpret_cast<uint8*>(&TempB);
uint8* PtrC = reinterpret_cast<uint8*>(&TempC);
uint8* PtrD = reinterpret_cast<uint8*>(&TempD);
TempA = 0x0123456789ABCDEF;
TempB = 0x0123456789AB0000;
Memory::Memmove(PtrA, PtrA + 2, 6);
always_check((TempA << 16) == TempB);
TempA = 1004;
TempB = 1005;
TempC = 1005;
TempD = 1006;
int32 ResultA = Memory::Memcmp(PtrA, PtrB, sizeof(int64));
int32 ResultB = Memory::Memcmp(PtrB, PtrC, sizeof(int64));
int32 ResultC = Memory::Memcmp(PtrC, PtrD, sizeof(int64));
always_check((ResultA < 0) != (ResultB < 0));
always_check(ResultB == 0);
Memory::Memset(PtrA, 0x3F, sizeof(int64));
always_check(TempA == 0x3F3F3F3F3F3F3F3F);
Memory::Memset(TempB, 0x3F);
always_check(TempB == 0x3F3F3F3F3F3F3F3F);
Memory::Memzero(PtrA, sizeof(int64));
always_check(TempA == 0);
Memory::Memzero(TempB);
always_check(TempB == 0);
TempA = 0x0123456789ABCDEF;
Memory::Memcpy(PtrC, PtrA, sizeof(int64));
always_check(TempA == TempC);
TempB = 0xDEDCBA9876543210;
Memory::Memcpy(TempD, TempB);
always_check(TempB == TempD);
}
void TestMemoryMalloc()
{
int32* PtrA;
int64* PtrB;
PtrA = reinterpret_cast<int32*>(Memory::SystemMalloc(sizeof(int32)));
*PtrA = 0x01234567;
always_check(*PtrA == 0x01234567);
PtrB = reinterpret_cast<int64*>(Memory::SystemRealloc(PtrA, sizeof(int64)));
*PtrB = 0x0123456789ABCDEF;
always_check(*PtrB == 0x0123456789ABCDEF);
Memory::SystemFree(PtrB);
PtrA = reinterpret_cast<int32*>(Memory::Malloc(sizeof(int32), 1024));
always_check(Memory::IsAligned(PtrA, 1024));
*PtrA = 0x01234567;
always_check(*PtrA == 0x01234567);
PtrB = reinterpret_cast<int64*>(Memory::Realloc(PtrA, sizeof(int64), 1024));
always_check(Memory::IsAligned(PtrB, 1024));
*PtrB = 0x0123456789ABCDEF;
always_check(*PtrB == 0x0123456789ABCDEF);
Memory::Free(PtrB);
PtrA = new int32;
PtrB = new int64;
*PtrA = 0x01234567;
always_check(*PtrA == 0x01234567);
*PtrB = 0x0123456789ABCDEF;
always_check(*PtrB == 0x0123456789ABCDEF);
delete PtrA;
delete PtrB;
struct alignas(1024) FTest { int32 A; };
FTest* PtrC = new FTest[4];
always_check(Memory::IsAligned(PtrC, 1024));
PtrC->A = 0x01234567;
always_check(PtrC->A == 0x01234567);
delete[] PtrC;
}
NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END

View File

@ -0,0 +1,48 @@
#pragma once
#include "CoreTypes.h"
#include "TypeTraits/TypeTraits.h"
NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)
NAMESPACE_BEGIN(Memory)
template <typename T>
FORCEINLINE constexpr T Align(T InValue, size_t Alignment)
{
static_assert(TIsIntegral<T>::Value || TIsPointer<T>::Value, "Align expects an integer or pointer type");
return (T)(((uintptr_t)(InValue) + static_cast<uintptr_t>(Alignment) - 1) & ~(static_cast<uintptr_t>(Alignment) - 1));
}
template <typename T>
FORCEINLINE constexpr T AlignDown(T InValue, size_t Alignment)
{
static_assert(TIsIntegral<T>::Value || TIsPointer<T>::Value, "AlignDown expects an integer or pointer type");
return (T)((uintptr_t)(InValue) & ~(static_cast<uintptr_t>(Alignment) - 1));
}
template <typename T>
FORCEINLINE constexpr T AlignArbitrary(T InValue, size_t Alignment)
{
static_assert(TIsIntegral<T>::Value || TIsPointer<T>::Value, "AlignArbitrary expects an integer or pointer type");
return (T)((((uintptr_t)(InValue) + static_cast<uintptr_t>(Alignment) - 1) / static_cast<uintptr_t>(Alignment)) * static_cast<uintptr_t>(Alignment));
}
template <typename T>
FORCEINLINE constexpr bool IsAligned(T InValue, size_t Alignment)
{
static_assert(TIsIntegral<T>::Value || TIsPointer<T>::Value, "IsAligned expects an integer or pointer type");
return !((uintptr_t)(InValue) & (static_cast<uintptr_t>(Alignment) - 1));
}
NAMESPACE_END(Memory)
NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END

View File

@ -0,0 +1,110 @@
#pragma once
#include "CoreTypes.h"
#include "TypeTraits/TypeTraits.h"
#include <new>
#include <cstring>
#include <cstdlib>
NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)
NAMESPACE_BEGIN(Memory)
inline constexpr size_t DEFAULT_ALIGNMENT = 0;
inline constexpr size_t MINIMUM_ALIGNMENT = 8;
#ifdef __cpp_lib_hardware_interference_size
inline constexpr size_t DESTRUCTIVE_INTERFERENCE = std::hardware_destructive_interference_size;
inline constexpr size_t CONSTRUCTIVE_INTERFERENCE = std::hardware_constructive_interference_size;
#else
inline constexpr size_t DESTRUCTIVE_INTERFERENCE = 64;
inline constexpr size_t CONSTRUCTIVE_INTERFERENCE = 64;
#endif
FORCEINLINE void* Memmove(void* Destination, const void* Source, size_t Count)
{
return std::memmove(Destination, Source, Count);
}
FORCEINLINE int32 Memcmp(const void* BufferLHS, const void* BufferRHS, size_t Count)
{
return std::memcmp(BufferLHS, BufferRHS, Count);
}
FORCEINLINE void Memset(void* Destination, uint8 ValueToSet, size_t Count)
{
std::memset(Destination, ValueToSet, Count);
}
FORCEINLINE void* Memzero(void* Destination, size_t Count)
{
return std::memset(Destination, 0, Count);
}
FORCEINLINE void* Memcpy(void* Destination, const void* Source, size_t Count)
{
return std::memcpy(Destination, Source, Count);
}
template<typename T>
FORCEINLINE void Memset(T& Source, uint8 ValueToSet)
{
static_assert(!TIsPointer<T>::Value, "For pointers use the three parameters function");
Memset(&Source, ValueToSet, sizeof(T));
}
template<typename T>
FORCEINLINE void Memzero(T& Source)
{
static_assert(!TIsPointer<T>::Value, "For pointers use the two parameters function");
Memzero(&Source, sizeof(T));
}
template<typename T>
FORCEINLINE void Memcpy(T& Destination, const T& Source)
{
static_assert(!TIsPointer<T>::Value, "For pointers use the three parameters function");
Memcpy(&Destination, &Source, sizeof(T));
}
FORCEINLINE void* SystemMalloc(size_t Count)
{
return std::malloc(Count);
}
FORCEINLINE void* SystemRealloc(void* Ptr, size_t Count)
{
return std::realloc(Ptr, Count);
}
FORCEINLINE void SystemFree(void* Ptr)
{
std::free(Ptr);
}
REDCRAFTUTILITY_API void* Malloc(size_t Count, size_t Alignment = DEFAULT_ALIGNMENT);
REDCRAFTUTILITY_API void* Realloc(void* Ptr, size_t Count, size_t Alignment = DEFAULT_ALIGNMENT);
REDCRAFTUTILITY_API void Free(void* Ptr);
REDCRAFTUTILITY_API size_t QuantizeSize(size_t Count, size_t Alignment = DEFAULT_ALIGNMENT);
NAMESPACE_END(Memory)
NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END
#pragma warning(disable : 28251)
// The global overload operators new/delete do not cross .dll boundaries, and the macros should be placed in the .cpp of each module.
#define REPLACEMENT_OPERATOR_NEW_AND_DELETE \
void* operator new(std::size_t Count) { return NAMESPACE_REDCRAFT::Memory::Malloc(Count); } \
void* operator new(std::size_t Count, std::align_val_t Alignment) { return NAMESPACE_REDCRAFT::Memory::Malloc(Count, static_cast<NAMESPACE_REDCRAFT::size_t>(Alignment)); } \
void operator delete(void* Ptr) noexcept { NAMESPACE_REDCRAFT::Memory::Free(Ptr); } \
void operator delete(void* Ptr, std::align_val_t Alignment) noexcept { NAMESPACE_REDCRAFT::Memory::Free(Ptr); }

View File

@ -4,6 +4,7 @@
#include <cstdint>
#include <cstdlib>
#include <cstddef>
NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
@ -142,6 +143,7 @@ typedef WIDECHAR TCHAR;
typedef NAMESPACE_STD::uintptr_t uintptr_t;
typedef NAMESPACE_STD::intptr_t intptr_t;
typedef NAMESPACE_STD::ptrdiff_t ptrdiff_t;
typedef NAMESPACE_STD::size_t size_t;
typedef intptr_t ssize_t;

View File

@ -0,0 +1,16 @@
#pragma once
#include "CoreTypes.h"
NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)
REDCRAFTUTILITY_API void TestMemory();
REDCRAFTUTILITY_API void TestAlignment();
REDCRAFTUTILITY_API void TestMemoryBuffer();
REDCRAFTUTILITY_API void TestMemoryMalloc();
NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END