removing useless "realloc" feature in memory arenas

This commit is contained in:
2026-07-28 16:00:39 -04:00
parent 4081539ee4
commit 4dc14a04b3
5 changed files with 4 additions and 196 deletions
-17
View File
@@ -12,14 +12,6 @@ namespace Juliet
constexpr global uint64 g_Arena_Default_Commit_Size = Kilobytes(64); constexpr global uint64 g_Arena_Default_Commit_Size = Kilobytes(64);
constexpr global uint64 k_ArenaHeaderSize = 128; constexpr global uint64 k_ArenaHeaderSize = 128;
struct ArenaFreeNode
{
ArenaFreeNode* Next;
ArenaFreeNode* Previous;
index_t Position;
size_t Size;
};
#if JULIET_DEBUG #if JULIET_DEBUG
struct ArenaDebugInfo; struct ArenaDebugInfo;
#endif #endif
@@ -41,10 +33,6 @@ namespace Juliet
Arena* FreeBlockLast; Arena* FreeBlockLast;
ArenaFreeNode* FreeNodes;
bool AllowRealloc : 1;
JULIET_DEBUG_ONLY(uint16 LostNodeCount;) JULIET_DEBUG_ONLY(uint16 LostNodeCount;)
JULIET_DEBUG_ONLY(bool CanReserveMore : 1;) JULIET_DEBUG_ONLY(bool CanReserveMore : 1;)
@@ -66,9 +54,6 @@ namespace Juliet
uint64 ReserveSize = g_Arena_Default_Reserve_Size; uint64 ReserveSize = g_Arena_Default_Reserve_Size;
uint64 CommitSize = g_Arena_Default_Commit_Size; uint64 CommitSize = g_Arena_Default_Commit_Size;
// True: All push will be 32 bytes minimum
bool AllowRealloc = false;
// When false, will assert if a new block is reserved. // When false, will assert if a new block is reserved.
JULIET_DEBUG_ONLY(bool CanReserveMore : 1 = true;) JULIET_DEBUG_ONLY(bool CanReserveMore : 1 = true;)
}; };
@@ -80,8 +65,6 @@ namespace Juliet
// Raw Push, can be used but templated helpers exists below // Raw Push, can be used but templated helpers exists below
[[nodiscard]] JULIET_API void* ArenaPush(NonNullPtr<Arena> arena, size_t size, size_t align, [[nodiscard]] JULIET_API void* ArenaPush(NonNullPtr<Arena> arena, size_t size, size_t align,
bool shouldBeZeroed JULIET_DEBUG_ONLY(, const char* tag)); bool shouldBeZeroed JULIET_DEBUG_ONLY(, const char* tag));
[[nodiscard]] JULIET_API void* ArenaReallocate(NonNullPtr<Arena> arena, void* oldPtr, size_t oldSize, size_t newSize,
size_t align, bool shouldBeZeroed JULIET_DEBUG_ONLY(, const char* tag));
JULIET_API void ArenaPopTo(NonNullPtr<Arena> arena, size_t position); JULIET_API void ArenaPopTo(NonNullPtr<Arena> arena, size_t position);
JULIET_API void ArenaPop(NonNullPtr<Arena> arena, size_t amount); JULIET_API void ArenaPop(NonNullPtr<Arena> arena, size_t amount);
JULIET_API void ArenaClear(NonNullPtr<Arena> arena); JULIET_API void ArenaClear(NonNullPtr<Arena> arena);
-104
View File
@@ -18,12 +18,6 @@ namespace Juliet
constexpr uint64 k_PageSize = Kilobytes(4); constexpr uint64 k_PageSize = Kilobytes(4);
} // namespace } // namespace
namespace
{
static_assert(sizeof(ArenaFreeNode) == 32, "ArenaFreeNode should be 32 bytes and not more");
constexpr size_t k_ArenaFreeNodeSize = sizeof(ArenaFreeNode);
} // namespace
// https://github.com/EpicGamesExt/raddebugger/blob/master/src/base/base_arena.c // https://github.com/EpicGamesExt/raddebugger/blob/master/src/base/base_arena.c
Arena* ArenaAllocate(const ArenaParams& params JULIET_DEBUG_ONLY(, const char* name), const std::source_location& loc) Arena* ArenaAllocate(const ArenaParams& params JULIET_DEBUG_ONLY(, const char* name), const std::source_location& loc)
@@ -51,9 +45,6 @@ namespace Juliet
arena->BasePosition = 0; arena->BasePosition = 0;
arena->Position = k_ArenaHeaderSize; arena->Position = k_ArenaHeaderSize;
arena->FreeNodes = nullptr;
arena->AllowRealloc = params.AllowRealloc;
#if JULIET_DEBUG #if JULIET_DEBUG
arena->CanReserveMore = params.CanReserveMore; arena->CanReserveMore = params.CanReserveMore;
arena->FirstDebugInfo = nullptr; arena->FirstDebugInfo = nullptr;
@@ -86,52 +77,6 @@ namespace Juliet
size_t positionPrePush = AlignPow2(current->Position, align); size_t positionPrePush = AlignPow2(current->Position, align);
size_t positionPostPush = positionPrePush + size; size_t positionPostPush = positionPrePush + size;
if (arena->AllowRealloc)
{
size = Max(size, k_ArenaFreeNodeSize);
for (ArenaFreeNode* freeNode = current->FreeNodes; freeNode != nullptr; freeNode = freeNode->Next)
{
if (size <= freeNode->Size)
{
index_t position = freeNode->Position;
size_t remainingSize = freeNode->Size - size;
if (remainingSize < k_ArenaFreeNodeSize)
{
ArenaFreeNode* previous = freeNode->Previous;
ArenaFreeNode* next = freeNode->Next;
if (previous)
{
previous->Next = next;
}
if (next)
{
next->Previous = previous;
}
#if JULIET_DEBUG
if (remainingSize > 0)
{
++current->LostNodeCount;
}
#endif
}
else
{
freeNode->Position += size;
}
auto* result = reinterpret_cast<Byte*>(current) + position;
if (shouldBeZeroed)
{
MemoryZero(result, size);
}
return result;
}
}
}
// If allowed and needed, add a new block and chain it to the arena. // If allowed and needed, add a new block and chain it to the arena.
if (current->Reserved < positionPostPush /* flags : chaining allowed */) if (current->Reserved < positionPostPush /* flags : chaining allowed */)
{ {
@@ -225,55 +170,6 @@ namespace Juliet
return result; return result;
} }
void* ArenaReallocate(NonNullPtr<Arena> arena, void* oldPtr, size_t oldSize, size_t newSize, size_t align,
bool shouldBeZeroed JULIET_DEBUG_ONLY(, const char* tag))
{
void* result = ArenaPush(arena, newSize, align, shouldBeZeroed JULIET_DEBUG_ONLY(, tag));
// Find the correct block to release
Arena* block = nullptr;
for (block = arena->Current; block != nullptr; block = block->Previous)
{
if ((reinterpret_cast<Byte*>(block) < static_cast<Byte*>(oldPtr)) &&
(static_cast<Byte*>(oldPtr) <= (reinterpret_cast<Byte*>(block) + block->Reserved)))
{
break;
}
}
Assert(block != nullptr);
// Copy old to new
MemCopy(result, oldPtr, std::min(oldSize, newSize));
// Zero the old memory
MemoryZero(oldPtr, oldSize);
if (oldSize >= sizeof(ArenaFreeNode))
{
ArenaFreeNode* freeNode = static_cast<ArenaFreeNode*>(oldPtr);
ptrdiff_t posPtr = static_cast<Byte*>(oldPtr) - reinterpret_cast<Byte*>(block);
index_t position = static_cast<index_t>(posPtr);
freeNode->Position = position;
freeNode->Size = oldSize;
// Insert at head of the free list
freeNode->Next = block->FreeNodes;
freeNode->Previous = nullptr;
if (block->FreeNodes)
{
block->FreeNodes->Previous = freeNode;
}
block->FreeNodes = freeNode;
#if JULIET_DEBUG
// Remove the debug info for the old allocation (since it's now free)
size_t oldOffset = static_cast<size_t>(static_cast<Byte*>(oldPtr) - reinterpret_cast<Byte*>(block));
DebugArenaRemoveAllocation(block, oldOffset);
#endif
}
return result;
}
void ArenaPopTo(NonNullPtr<Arena> arena, size_t position) void ArenaPopTo(NonNullPtr<Arena> arena, size_t position)
{ {
size_t clampedPosition = ClampBottom(k_ArenaHeaderSize, position); size_t clampedPosition = ClampBottom(k_ArenaHeaderSize, position);
@@ -117,60 +117,6 @@ namespace Juliet::UnitTest
ArenaRelease(testArena); ArenaRelease(testArena);
{
// Test reallocate
Arena* arena = ArenaAllocate({ .AllowRealloc = true } JULIET_DEBUG_ONLY(, "Test Realloc"));
char* charArray = ArenaPushArray<char>(arena, 128);
char* secondCharArray = ArenaPushArray<char>(arena, 128);
char* thirdCharArray = ArenaPushArray<char>(arena, 128);
secondCharArray = static_cast<char*>(ArenaReallocate(arena, secondCharArray, 128, 256, alignof(char), true JULIET_DEBUG_ONLY(, "ReallocChar")));
char* fourthCharArray = ArenaPushArray<char>(arena, 128);
Assert(charArray);
Assert(secondCharArray);
Assert(thirdCharArray);
Assert(fourthCharArray);
ArenaRelease(arena);
}
{
// Test Reallocate Shrink (Buffer Overflow Bug Check)
Arena* arena = ArenaAllocate({ .AllowRealloc = true } JULIET_DEBUG_ONLY(, "Test Shrink"));
size_t largeSize = 100;
char* large = ArenaPushArray<char>(arena, largeSize);
MemSet(large, 'A', largeSize);
size_t smallSize = 50;
char* smallData = static_cast<char*>(ArenaReallocate(arena, large, largeSize, smallSize, alignof(char), true JULIET_DEBUG_ONLY(, "ResizeSmall")));
for (size_t i = 0; i < smallSize; ++i)
{
Assert(smallData[i] == 'A');
}
// Allocate next block (should be immediately after 'small')
// Don't zero it, if overflow happened it will contain 'A's
char* next = static_cast<char*>(ArenaPush(arena, 50, alignof(char), false JULIET_DEBUG_ONLY(, "OverflowCheck")));
bool corrupted = false;
for(size_t i = 0; i < 50; ++i)
{
if (next[i] == 'A')
{
corrupted = true;
break;
}
}
Assert(!corrupted);
ArenaRelease(arena);
}
printf("All Paged MemoryArena tests passed.\n"); printf("All Paged MemoryArena tests passed.\n");
} }
} // namespace Juliet::UnitTest } // namespace Juliet::UnitTest
@@ -450,24 +450,6 @@ namespace Juliet::Debug
info = info->Next; info = info->Next;
ImGui::PopID(); ImGui::PopID();
} }
// Draw Free Nodes (Holes) if Realloc is enabled
if (arena->AllowRealloc && blk->FreeNodes)
{
ArenaFreeNode* freeNode = blk->FreeNodes;
while (freeNode)
{
float fxStart = pos.x + static_cast<float>(static_cast<double>(freeNode->Position) * scale);
float fWidth = static_cast<float>(static_cast<double>(freeNode->Size) * scale);
ImVec2 fMin(fxStart, pos.y + 1);
ImVec2 fMax(fxStart + fWidth, pos.y + blockHeight - 1);
dl->AddRectFilled(fMin, fMax, IM_COL32(50, 50, 50, 200));
freeNode = freeNode->Next;
}
}
} }
pos.y += blockHeight + blockSpacing; pos.y += blockHeight + blockSpacing;
+2 -1
View File
@@ -154,7 +154,8 @@ namespace Juliet
void InitializeEngine(JulietInit_Flags flags) void InitializeEngine(JulietInit_Flags flags)
{ {
EngineInstance.PlatformArena = ArenaAllocate({ .AllowRealloc = true } JULIET_DEBUG_PARAM("Platform Arena")); EngineInstance.PlatformArena =
ArenaAllocate({ .ReserveSize = Megabytes(128) } JULIET_DEBUG_PARAM("Platform Arena"));
InitializeLogManager(); InitializeLogManager();