// Copyright Epic Games, Inc. All Rights Reserved. #include #include #include #include #if ZEN_PLATFORM_WINDOWS # include ZEN_THIRD_PARTY_INCLUDES_START # include ZEN_THIRD_PARTY_INCLUDES_END #else # include #endif namespace zen { ////////////////////////////////////////////////////////////////////////// static void* AlignedAllocImpl(size_t Size, size_t Alignment) { #if ZEN_PLATFORM_WINDOWS # if ZEN_USE_MIMALLOC && 0 /* this path is not functional */ return mi_aligned_alloc(Alignment, Size); # else return _aligned_malloc(Size, Alignment); # endif #else // aligned_alloc() states that size must be a multiple of alignment. Some // platforms return null if this requirement isn't met. Size = (Size + Alignment - 1) & ~(Alignment - 1); return std::aligned_alloc(Alignment, Size); #endif } void AlignedFreeImpl(void* ptr) { if (ptr == nullptr) return; #if ZEN_PLATFORM_WINDOWS # if ZEN_USE_MIMALLOC && 0 /* this path is not functional */ return mi_free(ptr); # else _aligned_free(ptr); # endif #else std::free(ptr); #endif } ////////////////////////////////////////////////////////////////////////// MemoryArena::MemoryArena() { } MemoryArena::~MemoryArena() { } void* MemoryArena::Alloc(size_t Size, size_t Alignment) { return AlignedAllocImpl(Size, Alignment); } void MemoryArena::Free(void* ptr) { AlignedFreeImpl(ptr); } ////////////////////////////////////////////////////////////////////////// void* Memory::Alloc(size_t Size, size_t Alignment) { return AlignedAllocImpl(Size, Alignment); } void Memory::Free(void* ptr) { AlignedFreeImpl(ptr); } ////////////////////////////////////////////////////////////////////////// ChunkingLinearAllocator::ChunkingLinearAllocator(uint64_t ChunkSize, uint64_t ChunkAlignment) : m_ChunkSize(ChunkSize) , m_ChunkAlignment(ChunkAlignment) { } ChunkingLinearAllocator::~ChunkingLinearAllocator() { Reset(); } void ChunkingLinearAllocator::Reset() { for (void* ChunkEntry : m_ChunkList) { Memory::Free(ChunkEntry); } m_ChunkList.clear(); m_ChunkCursor = nullptr; m_ChunkBytesRemain = 0; } void* ChunkingLinearAllocator::Alloc(size_t Size, size_t Alignment) { ZEN_ASSERT_SLOW(zen::IsPow2(Alignment)); // This could be improved in a bunch of ways // // * We pessimistically allocate memory even though there may be enough memory available for a single allocation due to the way we take // alignment into account below // * The block allocation size could be chosen to minimize slack for the case when multiple oversize allocations are made rather than // minimizing the number of chunks // * ... const uint64_t AllocationSize = zen::RoundUp(Size, Alignment); if (m_ChunkBytesRemain < (AllocationSize + Alignment - 1)) { const uint64_t ChunkSize = zen::RoundUp(zen::Max(m_ChunkSize, Size), m_ChunkSize); void* ChunkPtr = Memory::Alloc(ChunkSize, m_ChunkAlignment); m_ChunkCursor = reinterpret_cast(ChunkPtr); m_ChunkBytesRemain = ChunkSize; m_ChunkList.push_back(ChunkPtr); } const uint64_t AlignFixup = (Alignment - reinterpret_cast(m_ChunkCursor)) & (Alignment - 1); void* ReturnPtr = m_ChunkCursor + AlignFixup; const uint64_t Delta = AlignFixup + AllocationSize; ZEN_ASSERT_SLOW(m_ChunkBytesRemain >= Delta); m_ChunkCursor += Delta; m_ChunkBytesRemain -= Delta; ZEN_ASSERT_SLOW(IsPointerAligned(ReturnPtr, Alignment)); return ReturnPtr; } ////////////////////////////////////////////////////////////////////////// // // Unit tests // #if ZEN_WITH_TESTS TEST_CASE("ChunkingLinearAllocator") { ChunkingLinearAllocator Allocator(4096); void* p1 = Allocator.Alloc(1, 1); void* p2 = Allocator.Alloc(1, 1); CHECK(p1 != p2); void* p3 = Allocator.Alloc(1, 4); CHECK(IsPointerAligned(p3, 4)); void* p3_2 = Allocator.Alloc(1, 4); CHECK(IsPointerAligned(p3_2, 4)); void* p4 = Allocator.Alloc(1, 8); CHECK(IsPointerAligned(p4, 8)); for (int i = 0; i < 100; ++i) { void* p0 = Allocator.Alloc(64); ZEN_UNUSED(p0); } } TEST_CASE("MemoryView") { { uint8_t Array1[16] = {}; MemoryView View1 = MakeMemoryView(Array1); CHECK(View1.GetSize() == 16); } { uint32_t Array2[16] = {}; MemoryView View2 = MakeMemoryView(Array2); CHECK(View2.GetSize() == 64); } CHECK(MakeMemoryView({1.0f, 1.2f}).GetSize() == 8); } void memory_forcelink() { } #endif } // namespace zen