#pragma once #include "CoreTypes.h" #include "TypeTraits/TypeTraits.h" #include #include #include NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) NAMESPACE_BEGIN(Memory) /** * Default allocator alignment. * Blocks >= 16 bytes will be 16-byte-aligned, Blocks < 16 will be 8-byte aligned. If the allocator does * not support allocation alignment, the alignment will be ignored. */ inline constexpr size_t DefaultAlignment = 0; /** * Minimum allocator alignment. */ inline constexpr size_t MinimumAlignment = 8; #ifdef __cpp_lib_hardware_interference_size /** * Minimum offset between two objects to avoid false sharing. * * struct FTwoCacheLiner { // occupies two cache lines * alignas(DestructiveInterference) TAtomic X; * alignas(DestructiveInterference) TAtomic Y; * }; * */ inline constexpr size_t DestructiveInterference = std::hardware_destructive_interference_size; /** * Maximum size of contiguous memory to promote true sharing. * * struct alignas(ConstructiveInterference) FOneCacheLiner { // occupies one cache line * TAtomic X; * TAtomic Y; * }; * */ inline constexpr size_t ConstructiveInterference = std::hardware_constructive_interference_size; #else /** * Minimum offset between two objects to avoid false sharing. * * struct FTwoCacheLiner { // occupies two cache lines * alignas(DestructiveInterference) TAtomic X; * alignas(DestructiveInterference) TAtomic Y; * }; * */ inline constexpr size_t DestructiveInterference = 64; /** * Maximum size of contiguous memory to promote true sharing. * * struct alignas(ConstructiveInterference) FOneCacheLiner { // occupies one cache line * TAtomic X; * TAtomic Y; * }; * */ inline constexpr size_t ConstructiveInterference = 64; #endif /** * Copies 'Count' bytes from the buffer pointed to by 'Source' to the buffer pointed to by 'Destination'. * The buffers may overlap, copying takes place as if the characters were copied to a temporary character * array and then the characters were copied from the array to 'Destination'. * * @param Destination - The pointer to the memory location to copy to. * @param Source - The pointer to the memory location to copy from. * @param Count - The number of bytes to copy. * * @return Destination */ FORCEINLINE void* Memmove(void* Destination, const void* Source, size_t Count) { return std::memmove(Destination, Source, Count); } /** * Reinterprets the buffers pointed to by 'BufferLHS' and 'BufferRHS' as arrays of unsigned char and * compares the first 'Count' characters of these arrays. The comparison is done lexicographically. * The sign of the result is the sign of the difference between the values of the first pair of bytes * (both interpreted as unsigned char) that differ in the objects being compared. * * @param BufferLHS - The pointers to the memory buffers to compare. * @param BufferRHS - The pointers to the memory buffers to compare. * @param Count - The number of bytes to examine. * * @return Negative value if the first differing byte in 'BufferLHS' is less than the corresponding byte in 'BufferRHS'. * 0 if all 'Count' bytes of 'BufferLHS' and 'BufferRHS' are equal. * Positive value if the first differing byte in 'BufferLHS' is greater than the corresponding byte in 'BufferRHS'. */ FORCEINLINE int32 Memcmp(const void* BufferLHS, const void* BufferRHS, size_t Count) { return std::memcmp(BufferLHS, BufferRHS, Count); } /** * Copies 'ValueToSet' into each of the first 'Count' characters of the buffer pointed to by 'Destination'. * * @param Destination - The pointer to the buffer to fill. * @param ValueToSet - The fill byte. * @param Count - The number of bytes to fill. * * @return Destination */ FORCEINLINE void* Memset(void* Destination, uint8 ValueToSet, size_t Count) { return std::memset(Destination, ValueToSet, Count); } /** * Copies 0 into each of the first 'Count' characters of the buffer pointed to by 'Destination'. * * @param Destination - The pointer to the buffer to fill. * @param Count - The number of bytes to fill. * * @return Destination */ FORCEINLINE void* Memzero(void* Destination, size_t Count) { return std::memset(Destination, 0, Count); } /** * Copies 'Count' bytes from the buffer pointed to by 'Source' to the buffer pointed to by 'Destination'. * If the buffers overlap, the behavior is undefined. * * @param Destination - The pointer to the memory location to copy to. * @param Source - The pointer to the memory location to copy from. * @param Count - The number of bytes to copy. * * @return Destination */ FORCEINLINE void* Memcpy(void* Destination, const void* Source, size_t Count) { return std::memcpy(Destination, Source, Count); } /** * Copies the object referenced to by 'Source' to the object referenced to by 'Destination'. * The objects may overlap, copying takes place as if the characters were copied to a temporary character * array and then the characters were copied from the array to 'Destination'. * * @param Destination - The reference to the object to copy to. * @param Source - The reference to the object to copy from. */ template requires (!CPointer) FORCEINLINE void Memmove(T& Destination, const T& Source) { Memmove(&Destination, &Source, sizeof(T)); } /** * Reinterprets the objects referenced to by 'BufferLHS' and 'BufferRHS' as arrays of unsigned char and * compares the all characters of these arrays. The comparison is done lexicographically. * The sign of the result is the sign of the difference between the values of the first pair of bytes * (both interpreted as unsigned char) that differ in the objects being compared. * * @param BufferLHS - The reference to the object to compare. * @param BufferRHS - The reference to the object to compare. * * @return Negative value if the first differing byte in 'BufferLHS' is less than the corresponding byte in 'BufferRHS'. * 0 if all bytes of 'BufferLHS' and 'BufferRHS' are equal. * Positive value if the first differing byte in 'BufferLHS' is greater than the corresponding byte in 'BufferRHS'. */ template requires (!CPointer) FORCEINLINE int32 Memcmp(const T& BufferLHS, const T& BufferRHS) { return Memcmp(&BufferLHS, &BufferRHS, sizeof(T)); } /** * Copies 'ValueToSet' into each of the all characters of the object referenced to by 'Destination'. * * @param Destination - The reference to the object to fill. * @param ValueToSet - The fill byte. */ template requires (!CPointer) FORCEINLINE void Memset(T& Destination, uint8 ValueToSet) { Memset(&Destination, ValueToSet, sizeof(T)); } /** * Copies 0 into each of the all characters of the object referenced to by 'Destination'. * * @param Destination - The reference to the object to fill. * @param ValueToSet - The fill byte. */ template requires (!CPointer) FORCEINLINE void Memzero(T& Destination) { Memzero(&Destination, sizeof(T)); } /** * Copies the object referenced to by 'Source' to the object referenced to by 'Destination'. * If the objects overlap, the behavior is undefined. * * @param Destination - The reference to the object to copy to. * @param Source - The reference to the object to copy from. */ template requires (!CPointer) FORCEINLINE void Memcpy(T& Destination, const T& Source) { Memcpy(&Destination, &Source, sizeof(T)); } /** Fallback to std::malloc(). */ NODISCARD FORCEINLINE void* SystemMalloc(size_t Count) { return std::malloc(Count); } /** Fallback to std::realloc(). */ NODISCARD FORCEINLINE void* SystemRealloc(void* Ptr, size_t Count) { return std::realloc(Ptr, Count); } /** Fallback to std::free(). */ FORCEINLINE void SystemFree(void* Ptr) { std::free(Ptr); } /** * Allocates 'Count' bytes of uninitialized storage with 'Alignment'. * * @param Count - The number of bytes to allocate. * @param Alignment - The alignment value, must be a power of 2. * * @return The non-null pointer to the beginning of newly allocated memory. To avoid a memory leak, * the returned pointer must be deallocated with Free() or Realloc(). * * @see DefaultAlignment */ NODISCARD REDCRAFTUTILITY_API void* Malloc(size_t Count, size_t Alignment = DefaultAlignment); /** * Reallocates the given area of memory. It must be previously allocated by Malloc() or Realloc(). * If 'Ptr' is a nullptr, effectively the same as calling Malloc(). * * @param Ptr - The pointer to the memory area to be reallocated. * @param Count - The number of bytes to allocate. * @param Alignment - The alignment value, must be a power of 2. * * @return The non-null pointer to the beginning of newly allocated memory. To avoid a memory leak, * the returned pointer must be deallocated with Free() or Realloc(). * * @see DefaultAlignment */ NODISCARD REDCRAFTUTILITY_API void* Realloc(void* Ptr, size_t Count, size_t Alignment = DefaultAlignment); /** * Deallocates the space previously allocated by Malloc() or Realloc(). * If 'Ptr' is a nullptr, the function does nothing. * * @param Ptr - The pointer to the memory to deallocate. */ REDCRAFTUTILITY_API void Free(void* Ptr); /** * For some allocators this will return the actual size that should be requested to eliminate * internal fragmentation. The return value will always be >= 'Count'. This can be used to grow * and shrink containers to optimal sizes. * This call is always fast and threadsafe with no locking. * * @param Count - The number of bytes to allocate. * @param Alignment - The alignment value, must be a power of 2. * * @return The optimized new size. */ NODISCARD REDCRAFTUTILITY_API size_t QuantizeSize(size_t Count, size_t Alignment = DefaultAlignment); 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 \ NODISCARD void* operator new(std::size_t Count) { return NAMESPACE_REDCRAFT::Memory::Malloc(Count, __STDCPP_DEFAULT_NEW_ALIGNMENT__); } \ NODISCARD void* operator new(std::size_t Count, std::align_val_t Alignment) { return NAMESPACE_REDCRAFT::Memory::Malloc(Count, static_cast(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); }