feat(memory): complete low-level memory management utilities
This commit is contained in:
parent
94416676d2
commit
cd1a8da1a8
85
Redcraft.Utility/Source/Private/Memory/Memory.cpp
Normal file
85
Redcraft.Utility/Source/Private/Memory/Memory.cpp
Normal 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
|
142
Redcraft.Utility/Source/Private/Testing/MemoryTesting.cpp
Normal file
142
Redcraft.Utility/Source/Private/Testing/MemoryTesting.cpp
Normal 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
|
48
Redcraft.Utility/Source/Public/Memory/Alignment.h
Normal file
48
Redcraft.Utility/Source/Public/Memory/Alignment.h
Normal 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
|
110
Redcraft.Utility/Source/Public/Memory/Memory.h
Normal file
110
Redcraft.Utility/Source/Public/Memory/Memory.h
Normal 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); }
|
@ -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;
|
||||
|
||||
|
16
Redcraft.Utility/Source/Public/Testing/MemoryTesting.h
Normal file
16
Redcraft.Utility/Source/Public/Testing/MemoryTesting.h
Normal 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
|
Loading…
Reference in New Issue
Block a user