From dbc3886e9179ffedb408c8e215ce62020ba68fd0 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sun, 22 Dec 2019 17:47:26 -0800 Subject: [PATCH] imported gs_memory_arena.h which is an improved rewrite of gs_memory.h. Integrated this new library in place of gs_memory.h --- src/foldhaus_app.cpp | 80 ++--- src/foldhaus_app.h | 6 +- src/foldhaus_assembly.cpp | 5 +- src/foldhaus_assembly.h | 2 +- src/foldhaus_debug_visuals.h | 9 +- src/foldhaus_memory.h | 78 +---- src/foldhaus_operation_mode.h | 2 +- src/foldhaus_platform.h | 6 + src/gs_memory.h | 461 ------------------------- src/gs_memory_arena.h | 613 ++++++++++++++++++++++++++++++++++ src/gs_platform.h | 3 + src/gs_win32.cpp | 12 + src/win32_foldhaus.cpp | 3 +- 13 files changed, 689 insertions(+), 591 deletions(-) delete mode 100644 src/gs_memory.h create mode 100644 src/gs_memory_arena.h diff --git a/src/foldhaus_app.cpp b/src/foldhaus_app.cpp index 3c5591b..b8edca6 100644 --- a/src/foldhaus_app.cpp +++ b/src/foldhaus_app.cpp @@ -122,12 +122,10 @@ SACNSendDMXBufferListJob (s32 ThreadID, void* JobData) internal void LoadAssembly (app_state* State, context Context, char* Path) { - arena_snapshot TempMemorySnapshot = TakeSnapshotOfArena(*State->Transient); - platform_memory_result TestAssemblyFile = Context.PlatformReadEntireFile(Path); Assert(TestAssemblyFile.Size > 0); - assembly_definition AssemblyDefinition = ParseAssemblyFile(TestAssemblyFile.Base, TestAssemblyFile.Size, State->Transient); + assembly_definition AssemblyDefinition = ParseAssemblyFile(TestAssemblyFile.Base, TestAssemblyFile.Size, &State->Transient); Context.PlatformFree(TestAssemblyFile.Base, TestAssemblyFile.Size); @@ -136,21 +134,19 @@ LoadAssembly (app_state* State, context Context, char* Path) string FileName = Substring(PathString, IndexOfLastSlash + 1); r32 Scale = 100; - s32 AssemblyMemorySize = GetAssemblyMemorySizeFromDefinition(AssemblyDefinition, FileName); - u8* AssemblyMemory = Context.PlatformAlloc(AssemblyMemorySize); + memory_arena AssemblyArena = {}; + AssemblyArena.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; + AssemblyArena.Realloc = (gs_memory_realloc*)Context.PlatformRealloc; assembly NewAssembly = ConstructAssemblyFromDefinition(AssemblyDefinition, FileName, v3{0, 0, 0}, Scale, - AssemblyMemory, - AssemblyMemorySize); + AssemblyArena); array_entry_handle NewAssemblyHandle = PushElement(NewAssembly, &State->AssemblyList); PushElement(NewAssemblyHandle, &State->ActiveAssemblyIndecies); State->TotalLEDsCount += NewAssembly.LEDCount; - - ClearArenaToSnapshot(State->Transient, TempMemorySnapshot); } internal void @@ -158,7 +154,7 @@ UnloadAssembly (s32 AssemblyIndex, app_state* State, context Context) { assembly* Assembly = GetElementAtIndex(AssemblyIndex, State->AssemblyList); State->TotalLEDsCount -= Assembly->LEDCount; - Context.PlatformFree(Assembly->Arena.Base, Assembly->Arena.Size); + FreeMemoryArena(&Assembly->Arena, (gs_memory_free*)Context.PlatformFree); RemoveElementAtIndex(AssemblyIndex, &State->AssemblyList); for (s32 i = 0; i < State->ActiveAssemblyIndecies.Used; i++) @@ -193,27 +189,22 @@ RELOAD_STATIC_DATA(ReloadStaticData) INITIALIZE_APPLICATION(InitializeApplication) { app_state* State = (app_state*)Context.MemoryBase; - u8* MemoryCursor = Context.MemoryBase + sizeof(app_state); - s32 PermanentStorageSize = Context.MemorySize; //Megabytes(32); - //s32 TransientStorageSize = Context.MemorySize - PermanentStorageSize; - State->Permanent = BootstrapArenaIntoMemory(MemoryCursor, PermanentStorageSize); - //State->Transient = BootstrapArenaIntoMemory(MemoryCursor + PermanentStorageSize, TransientStorageSize); + State->Permanent = {}; + State->Permanent.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; + State->Permanent.Realloc = (gs_memory_realloc*)Context.PlatformRealloc; + State->Transient = {}; +State->Transient.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; + State->Transient.Realloc = (gs_memory_realloc*)Context.PlatformRealloc; - u8* TransientMemory = Context.PlatformAlloc(Megabytes(32)); - InitMemoryArena(&State->TransientMemory, TransientMemory, Megabytes(32), Context.PlatformAlloc); - State->Transient = &State->TransientMemory; - - InitMemoryArena(&State->SACNMemory, 0, 0, Context.PlatformAlloc); - - InitializeInputCommandRegistry(&State->DefaultInputCommandRegistry, 32, State->Permanent); + InitializeInputCommandRegistry(&State->DefaultInputCommandRegistry, 32, &State->Permanent); s32 CommandQueueSize = 32; - command_queue_entry* CommandQueueMemory = PushArray(State->Permanent, + command_queue_entry* CommandQueueMemory = PushArray(&State->Permanent, command_queue_entry, CommandQueueSize); State->CommandQueue = InitializeCommandQueue(CommandQueueMemory, CommandQueueSize); - State->ActiveTextEntry.Buffer = MakeString(PushArray(State->Permanent, char, 256), 0, 256); + State->ActiveTextEntry.Buffer = MakeString(PushArray(&State->Permanent, char, 256), 0, 256); // TODO(Peter): put in InitializeInterface? r32 FontSize = 14; @@ -221,12 +212,12 @@ INITIALIZE_APPLICATION(InitializeApplication) platform_memory_result FontFile = Context.PlatformReadEntireFile("Anonymous Pro.ttf"); if (FontFile.Size) { - bitmap_font* Font = PushStruct(State->Permanent, bitmap_font); + bitmap_font* Font = PushStruct(&State->Permanent, bitmap_font); Font->BitmapWidth = 512; Font->BitmapHeight = 512; Font->BitmapBytesPerPixel = 4; - Font->BitmapMemory = PushArray(State->Permanent, u8, Font->BitmapWidth * Font->BitmapHeight * Font->BitmapBytesPerPixel); + Font->BitmapMemory = PushArray(&State->Permanent, u8, Font->BitmapWidth * Font->BitmapHeight * Font->BitmapBytesPerPixel); Font->BitmapStride = Font->BitmapWidth * Font->BitmapBytesPerPixel; GSMemSet(Font->BitmapMemory, 0, Font->BitmapStride * Font->BitmapHeight); @@ -239,8 +230,8 @@ INITIALIZE_APPLICATION(InitializeApplication) Font->CodepointDictionarySize = (FontInfo.CodepointOnePastLast - FontInfo.CodepointStart); Font->CodepointDictionaryCount = 0; - Font->CodepointKeys = PushArray(State->Permanent, char, Font->CodepointDictionarySize); - Font->CodepointValues = PushArray(State->Permanent, codepoint_bitmap, Font->CodepointDictionarySize); + Font->CodepointKeys = PushArray(&State->Permanent, char, Font->CodepointDictionarySize); + Font->CodepointValues = PushArray(&State->Permanent, codepoint_bitmap, Font->CodepointDictionarySize); for (s32 Codepoint = FontInfo.CodepointStart; Codepoint < FontInfo.CodepointOnePastLast; @@ -296,7 +287,7 @@ INITIALIZE_APPLICATION(InitializeApplication) State->AssemblyList.FreeList.Next = &State->AssemblyList.FreeList; State->ActiveAssemblyIndecies.BucketSize = 32; #if 1 - char Path[] = "blumen_lumen.fold"; + char Path[] = "radialumia.fold"; LoadAssembly(State, Context, Path); #endif @@ -308,10 +299,10 @@ INITIALIZE_APPLICATION(InitializeApplication) { // MODES PLAYGROUND State->Modes.ActiveModesCount = 0; - - s32 ModesMemorySize = Kilobytes(32); - u8* ModesMemory = PushSize(State->Permanent, ModesMemorySize); - InitMemoryArena(&State->Modes.Arena, ModesMemory, ModesMemorySize, 0); + State->Modes.Arena = {}; + State->Modes.Arena.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; + State->Modes.Arena.Realloc = (gs_memory_realloc*)Context.PlatformRealloc; + State->Modes.Arena.FindAddressRule = FindAddress_InLastBufferOnly; } } @@ -413,7 +404,7 @@ UPDATE_AND_RENDER(UpdateAndRender) // and need to persist beyond the end of the UpdateAndRender call. In the release version, we won't // zero the Transient arena when we clear it so it wouldn't be a problem, but it is technically // incorrect to clear the arena, and then access the memory later. - ClearArena(State->Transient); + ClearArena(&State->Transient); HandleInput(State, InputQueue, Mouse); @@ -515,7 +506,7 @@ UPDATE_AND_RENDER(UpdateAndRender) { array_entry_handle AssemblyHandle = *GetElementAtIndex(i, State->ActiveAssemblyIndecies); assembly Assembly = *GetElementWithHandle(AssemblyHandle, State->AssemblyList); - dmx_buffer_list* NewDMXBuffers = CreateDMXBuffers(Assembly, HeaderSize, State->Transient); + dmx_buffer_list* NewDMXBuffers = CreateDMXBuffers(Assembly, HeaderSize, &State->Transient); DMXBuffers = DMXBufferListAppend(DMXBuffers, NewDMXBuffers); } @@ -535,7 +526,7 @@ UPDATE_AND_RENDER(UpdateAndRender) CurrentDMXBuffer = CurrentDMXBuffer->Next; } - send_sacn_job_data* Job = PushStruct(State->Transient, send_sacn_job_data); + send_sacn_job_data* Job = PushStruct(&State->Transient, send_sacn_job_data); Job->SendSocket = State->SACN.SendSocket; Job->SendTo = Context.PlatformSendTo; Job->DMXBuffers = DMXBuffers; @@ -584,7 +575,7 @@ UPDATE_AND_RENDER(UpdateAndRender) for (s32 Job = 0; Job < JobsNeeded; Job++) { - draw_leds_job_data* JobData = PushStruct(State->Transient, draw_leds_job_data); + draw_leds_job_data* JobData = PushStruct(&State->Transient, draw_leds_job_data); JobData->LEDs = Assembly.LEDs; JobData->Colors = Assembly.Colors; JobData->StartIndex = Job * MaxLEDsPerJob; @@ -630,7 +621,7 @@ UPDATE_AND_RENDER(UpdateAndRender) MakeStringLiteral("Load Assembly"), State->Interface, Mouse); - string InterfaceString = MakeString(PushArray(State->Transient, char, 256), 256); + string InterfaceString = MakeString(PushArray(&State->Transient, char, 256), 256); for (s32 i = 0; i < State->ActiveAssemblyIndecies.Used; i++) { array_entry_handle AssemblyHandle = *GetElementAtIndex(i, State->ActiveAssemblyIndecies); @@ -669,7 +660,18 @@ UPDATE_AND_RENDER(UpdateAndRender) DrawDebugInterface(RenderBuffer, 25, State->Interface, Context.WindowWidth, Context.WindowHeight - TopBarHeight, - Context.DeltaTime, State, State->Camera, Mouse, State->Transient); + Context.DeltaTime, State, State->Camera, Mouse, &State->Transient); + } + + // Checking for overflows + { + DEBUG_TRACK_SCOPE(OverflowChecks); + AssertAllocationsNoOverflow(State->Permanent); + for (s32 i = 0; i < State->AssemblyList.Used; i++) + { + assembly* Assembly = GetElementAtIndex(i, State->AssemblyList); + AssertAllocationsNoOverflow(Assembly->Arena); + } } } diff --git a/src/foldhaus_app.h b/src/foldhaus_app.h index 32d7e10..037895b 100644 --- a/src/foldhaus_app.h +++ b/src/foldhaus_app.h @@ -35,10 +35,8 @@ enum network_protocol struct app_state { - memory_arena* Permanent; - memory_arena TransientMemory; - memory_arena* Transient; - memory_arena SACNMemory; + memory_arena Permanent; + memory_arena Transient; s32 NetworkProtocolHeaderSize; network_protocol NetworkProtocol; diff --git a/src/foldhaus_assembly.cpp b/src/foldhaus_assembly.cpp index 418b169..8d59332 100644 --- a/src/foldhaus_assembly.cpp +++ b/src/foldhaus_assembly.cpp @@ -12,11 +12,10 @@ ConstructAssemblyFromDefinition (assembly_definition Definition, string AssemblyName, v3 RootPosition, r32 Scale, - u8* MemoryBase, - s32 MemorySize) + memory_arena Arena) { assembly Assembly = {}; - Assembly.Arena = CreateMemoryArena(MemoryBase, MemorySize); + Assembly.Arena = Arena; Assembly.Name = MakeString(PushArray(&Assembly.Arena, char, AssemblyName.Length), AssemblyName.Length); CopyStringTo(AssemblyName, &Assembly.Name); diff --git a/src/foldhaus_assembly.h b/src/foldhaus_assembly.h index e413bbe..637ae5f 100644 --- a/src/foldhaus_assembly.h +++ b/src/foldhaus_assembly.h @@ -24,7 +24,7 @@ struct leds_in_universe_range struct assembly { - static_memory_arena Arena; + memory_arena Arena; string Name; string FilePath; diff --git a/src/foldhaus_debug_visuals.h b/src/foldhaus_debug_visuals.h index 8ce9557..4454460 100644 --- a/src/foldhaus_debug_visuals.h +++ b/src/foldhaus_debug_visuals.h @@ -197,7 +197,7 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c v2 TopOfDebugView = v2{StartX, WindowHeight - (NewLineYOffset(*Interface.Font) + 5)}; v2 TopOfScreenLinePos = TopOfDebugView; - arena_snapshot StartTempMemory = TakeSnapshotOfArena(*Transient); + //arena_snapshot StartTempMemory = TakeSnapshotOfArena(*Transient); string DebugString = InitializeEmptyString(PushArray(Transient, char, 256), 256); @@ -215,8 +215,8 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c 5, DeltaTime, (u32)FramesPerSecond, State->Modes.ActiveModesCount, - State->Modes.Arena.CurrentRegion->Used, - State->Modes.Arena.CurrentRegion->Size, + State->Modes.Arena.TotalUsed, + State->Modes.Arena.TotalSize, State->CommandQueue.Used); DrawString(RenderBuffer, DebugString, Interface.Font, TopOfScreenLinePos, WhiteV4); @@ -356,7 +356,4 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c } #endif } - - ZeroArenaToSnapshot(Transient, StartTempMemory); - ClearArenaToSnapshot(Transient, StartTempMemory); } diff --git a/src/foldhaus_memory.h b/src/foldhaus_memory.h index 16e2378..cdbc2db 100644 --- a/src/foldhaus_memory.h +++ b/src/foldhaus_memory.h @@ -1,5 +1,6 @@ #ifndef GS_MEMORY_H +#if 0 #ifndef GS_LANGUAGE_H typedef uint8_t u8; @@ -230,6 +231,7 @@ ClearArenaToSnapshot (memory_arena* Arena, arena_snapshot Snapshot) Assert(RegionCursor == Snapshot.CurrentRegion); RegionCursor->Used = Snapshot.UsedAtSnapshot; } +#endif // // Basic Memory Arena @@ -253,6 +255,7 @@ CreateMemoryArena (u8* Base, u32 Size) return Result; } +#define PushArrayOnStaticArena(arena, type, length) (type*)PushSize_((arena), sizeof(type) * length) static u8* PushSize_ (static_memory_arena* Arena, u32 Size) { @@ -262,80 +265,5 @@ PushSize_ (static_memory_arena* Arena, u32 Size) return Result; } -// -// Tracked Array Implementation -// - -#define ARRAY_CHECKSUM 0x51bada7b -struct array_header_ -{ - u32 Size; - s32 ElementMax; - s32 ElementCount; - s32 ElementSize; - u32 Checksum; -}; - -#define gs_PushArray(arena, type, size) (type*)gs_PushArray_(arena, sizeof(type), size) - -static u8* -gs_PushArray_ (memory_arena* Arena, u32 StepSize, u32 Count) -{ - u32 ArrayFootprint = sizeof(array_header_) + (StepSize * Count); - array_header_* Header = (array_header_*)PushSize_(Arena, ArrayFootprint); - - array_header_* Body = Header + 1; - u8* Result = (u8*)(Body); - - Header->Size = Count * StepSize; - Header->ElementMax = Count; - Header->ElementSize = StepSize; - Header->ElementCount = 0; - Header->Checksum = ARRAY_CHECKSUM; - - return Result; -} - -#define gs_ArrayHeader_(array) (((array_header_*)array) - 1) - -#ifdef DEBUG -#define gs_ArrayCheck(array) Assert(!array || gs_ArrayHeader_(array)->Checksum == ARRAY_CHECKSUM) -#else -#define gs_ArrayCheck(array) -#endif - -#define gs_ArrayCount(array) gs_ArrayCount_((u8*)array) -static s32 -gs_ArrayCount_ (u8* Base) -{ - gs_ArrayCheck(Base); - return gs_ArrayHeader_(Base)->ElementCount; -} - -#define gs_ArrayMax(array) gs_ArrayMax_((u8*)array) -static s32 -gs_ArrayMax_ (u8* Base) -{ - gs_ArrayCheck(Base); - return gs_ArrayHeader_(Base)->ElementMax; -} - -#define gs_ArrayAdd(array) ( gs_PushArrayElement_((u8*)array), (array) + (gs_ArrayCount(array) - 1) ) -#define gs_ArrayPush(array, ele) *( gs_ArrayAdd(array) ) = (ele) - -static void* -gs_PushArrayElement_ (u8* Base) -{ - gs_ArrayCheck(Base); - Assert(gs_ArrayHeader_(Base)->ElementCount + 1 <= gs_ArrayHeader_(Base)->ElementMax); - - void* Result = (void*)(Base + (gs_ArrayHeader_(Base)->ElementCount * gs_ArrayHeader_(Base)->ElementSize)); - gs_ArrayHeader_(Base)->ElementCount++; - - return Result; -} - - - #define GS_MEMORY_H #endif // GS_MEMORY_H \ No newline at end of file diff --git a/src/foldhaus_operation_mode.h b/src/foldhaus_operation_mode.h index ef7d043..37f464b 100644 --- a/src/foldhaus_operation_mode.h +++ b/src/foldhaus_operation_mode.h @@ -28,7 +28,7 @@ ActivateOperationMode (operation_mode_system* System) Assert(System->ActiveModesCount < OPERATION_MODES_MAX); s32 ModeIndex = System->ActiveModesCount++; - System->ModeMemorySnapshots[ModeIndex] = TakeSnapshotOfArena(System->Arena); + System->ModeMemorySnapshots[ModeIndex] = TakeSnapshotOfArena(&System->Arena); operation_mode NewMode = {}; System->ActiveModes[ModeIndex] = NewMode; diff --git a/src/foldhaus_platform.h b/src/foldhaus_platform.h index 1532634..5e3a4e5 100644 --- a/src/foldhaus_platform.h +++ b/src/foldhaus_platform.h @@ -3,6 +3,11 @@ #include "gs_array.h" #include "foldhaus_memory.h" + +#define GS_MEMORY_TRACK_ALLOCATIONS +#define GS_MEMORY_NO_STD_LIBS +#include "gs_memory_arena.h" + #include "gs_string.h" #include "foldhaus_debug.h" @@ -133,6 +138,7 @@ struct context platform_alloc* PlatformAlloc; platform_free* PlatformFree; + platform_realloc* PlatformRealloc; platform_read_entire_file* PlatformReadEntireFile; platform_write_entire_file* PlatformWriteEntireFile; platform_get_file_path* PlatformGetFilePath; diff --git a/src/gs_memory.h b/src/gs_memory.h deleted file mode 100644 index 9d1541d..0000000 --- a/src/gs_memory.h +++ /dev/null @@ -1,461 +0,0 @@ -#ifndef GS_MEMORY_H - -#define ArenaZeroStruct(data_ptr) ArenaZeroStruct_((u8*)data_ptr, sizeof(*data_ptr)) -inline void -ArenaZeroStruct_ (u8* Base, s32 Size) -{ - u8* Iter = Base; - for (s32 i = 0; i < Size; i++) { *Iter++ = 0; } -} - -struct grow_arena_result -{ - u8* Base; - s32 Size; -}; - -#define GROW_ARENA_MEMORY(name) grow_arena_result name(s32 Size) -typedef GROW_ARENA_MEMORY(grow_arena_memory); - -#define FREE_ARENA_MEMORY(name) b32 name(u8* Base, s32 Size) -typedef FREE_ARENA_MEMORY(free_arena_memory); - -struct memory_region_header -{ - memory_region_header* Prev; - s32 Size; - s32 Used; - u8* Base; -}; - -inline b32 -RegionCanFitSize (memory_region_header* Header, s32 Size) -{ - b32 Result = (Header->Used + Size) <= Header->Size; - return Result; -} - -inline b32 -AddressIsInRegion (memory_region_header* Header, u8* Address) -{ - b32 Result = (Header->Base <= Address) && (Header->Base + Header->Used > Address); - return Result; -} - -#ifndef DEFAULT_MEMORY_ALIGNMENT -#define DEFAULT_MEMORY_ALIGNMENT (2 * sizeof(void*)) -#endif - -b32 GSMemIsPowerOfTwo (u64 Address) -{ - return (Address & (Address - 1)) == 0; -} - -u64 AlignForward (u64 Base, u64 Align) -{ - u64 P, A, Modulo; - - Assert(GSMemIsPowerOfTwo(Align)); - - P = Base; - A = Align; - Modulo = P & (A - 1); - - if (Modulo != 0) - { - P = P + (A - Modulo); - } - - return P; -} - -////////////////////////////// -// Heap Memory -////////////////////////////// - -// heap_memory_arena -// a growable memory arena that has two ways to interact with it: push and clear. -// Push: returns a free region of continguous memory. If the arenas GrowArenaProc function is set, this may -// get called in order to obtain enough free memory to fulfil the push request -// Clear: clears the entire memory arena. If the arena has been grown at any point, those subsequent -// regions of memory will be freed back to the system. -struct heap_memory_arena -{ - memory_region_header* CurrentRegion; - - s32 RegionMemorySize; - grow_arena_memory* GrowArenaProc; - free_arena_memory* FreeArenaMemoryProc; -}; - -static void -GrowHeapArena (heap_memory_arena* Arena, s32 RequestedSize) -{ - if (Arena->GrowArenaProc) - { - Assert(Arena->RegionMemorySize > 0); - - s32 GrowthSize = GSMax(RequestedSize, Arena->RegionMemorySize); - grow_arena_result NewMemory = Arena->GrowArenaProc(GrowthSize + sizeof(memory_region_header)); - Assert(NewMemory.Size > 0); - - memory_region_header* Header = (memory_region_header*)NewMemory.Base; - Header->Base = (u8*)NewMemory.Base + sizeof(memory_region_header); - Header->Size = NewMemory.Size - sizeof(memory_region_header); - Header->Used = 0; - Header->Prev = Arena->CurrentRegion; - Arena->CurrentRegion = Header; - } - else - { - InvalidCodePath; - } -} - -#define PushStruct(arena, type) (type*)PushSize_(arena, sizeof(type)) -#define PushArray(arena, type, count) (type*)PushSize_(arena, sizeof(type)*count) -static u8* -PushSize_ (heap_memory_arena* Arena, s32 Size) -{ - if (!Arena->CurrentRegion) { GrowHeapArena(Arena, Size); } - - u8* CurrPointer = Arena->CurrentRegion->Base + Arena->CurrentRegion->Used; - u64 Offset = AlignForward((u64)CurrPointer, DEFAULT_MEMORY_ALIGNMENT); - Offset -= (u64)(Arena->CurrentRegion->Base + Arena->CurrentRegion->Used); - - if (!RegionCanFitSize(Arena->CurrentRegion, Size + Offset)) - { - // TODO(Peter): There might be empty space in the current region, its just not big enough for the - // requested size. We should search backwards to see if there is enough space in a previous region - // before growing the arena. - - GrowHeapArena(Arena, Size + Offset); - } - - u8* Result = Arena->CurrentRegion->Base + Arena->CurrentRegion->Used + Offset; - Arena->CurrentRegion->Used += Size + Offset; - - GSZeroMemory(Result, Size); - - return Result; -} - -static void -InitHeapMemoryArena (heap_memory_arena* Arena, s32 RegionMemorySize, - grow_arena_memory* GrowProc, free_arena_memory* FreeProc) -{ - ArenaZeroStruct(Arena); - Arena->RegionMemorySize = RegionMemorySize; - Arena->GrowArenaProc = GrowProc; - Arena->FreeArenaMemoryProc = FreeProc; -} - -static void -InitHeapMemoryArena (heap_memory_arena* Arena, u8* Base, s32 Size, - s32 RegionMemorySize = 0, - grow_arena_memory* GrowProc = 0, - free_arena_memory* FreeProc = 0) -{ - Assert(Size > sizeof(memory_region_header)); - - Arena->CurrentRegion = (memory_region_header*)Base; - Arena->CurrentRegion->Base = Base + sizeof(memory_region_header); - Arena->CurrentRegion->Size = Size - sizeof(memory_region_header); - Arena->CurrentRegion->Used = 0; - Arena->CurrentRegion->Prev = 0; - - Arena->RegionMemorySize = RegionMemorySize; - Arena->GrowArenaProc = GrowProc; - Arena->FreeArenaMemoryProc = FreeProc; -} - -static void -ClearHeapMemoryArena (heap_memory_arena* Arena) -{ - if (!Arena->CurrentRegion) { return; } - - memory_region_header* CurrentHead = Arena->CurrentRegion; - - if (CurrentHead->Prev) - { - Assert(Arena->FreeArenaMemoryProc); - while(CurrentHead->Prev) - { - memory_region_header* PrevHead = CurrentHead->Prev; - Arena->FreeArenaMemoryProc((u8*)CurrentHead, CurrentHead->Size + sizeof(memory_region_header)); - CurrentHead = PrevHead; - } - - Arena->CurrentRegion = CurrentHead; - } - - Arena->CurrentRegion->Used = 0; -} - - -////////////////////////////// -// Stack Memory -////////////////////////////// - -struct stack_memory_region -{ - stack_memory_region* Prev; -}; - -// stack_memory_arena -// Push: returns a free region of continguous memory. If the arenas GrowArenaProc function is set, this may -// get called in order to obtain enough free memory to fulfil the push request -// Pop: frees the last region allocated on the stack, returning it to the region of memory available to -// be used. -// Clear: clears the entire memory arena. If the arena has been grown at any point, those subsequent -// regions of memory will be freed back to the system. -struct stack_memory_arena -{ - memory_region_header* CurrentRegion; - stack_memory_region* UsedList; - - s32 RegionMemorySize; - grow_arena_memory* GrowArenaProc; - free_arena_memory* FreeArenaMemoryProc; -}; - -static u8* -PushSize_ (stack_memory_arena* Arena, s32 Size) -{ - if (!Arena->CurrentRegion || - !RegionCanFitSize(Arena->CurrentRegion, Size)) - { - if (Arena->GrowArenaProc) - { - Assert(Arena->RegionMemorySize > 0); - - grow_arena_result NewMemory = Arena->GrowArenaProc(Arena->RegionMemorySize + sizeof(memory_region_header)); - Assert(NewMemory.Size > 0); - - memory_region_header* Header = (memory_region_header*)NewMemory.Base; - Header->Base = (u8*)NewMemory.Base + sizeof(memory_region_header); - Header->Size = NewMemory.Size - sizeof(memory_region_header); - Header->Used = 0; - Header->Prev = Arena->CurrentRegion; - Arena->CurrentRegion = Header; - } - else - { - InvalidCodePath; - } - } - - u8* Region = Arena->CurrentRegion->Base + Arena->CurrentRegion->Used; - stack_memory_region* UsedListHeader = (stack_memory_region*)Region; - UsedListHeader->Prev = Arena->UsedList; - Arena->UsedList = UsedListHeader; - - u8* Result = Region + sizeof(stack_memory_region); - Arena->CurrentRegion->Used += Size + sizeof(stack_memory_region); - - return Result; -} - -// NOTE(Peter): Returns size available after the Pop operation -static s32 -PopLast (stack_memory_arena* Arena) -{ - s32 Result = Arena->CurrentRegion->Size - Arena->CurrentRegion->Used; - - if (Arena->UsedList) - { - u8* LastHead = (u8*)Arena->UsedList; - - if (!AddressIsInRegion(Arena->CurrentRegion, LastHead) && - Arena->FreeArenaMemoryProc) - { - memory_region_header* PrevHeader = Arena->CurrentRegion->Prev; - Arena->FreeArenaMemoryProc((u8*)Arena->CurrentRegion, - Arena->CurrentRegion->Size + sizeof(memory_region_header)); - Arena->CurrentRegion = PrevHeader; - - } - - Assert(LastHead >= Arena->CurrentRegion->Base && - LastHead <= Arena->CurrentRegion->Base + Arena->CurrentRegion->Size); - - stack_memory_region* PrevAlloc = Arena->UsedList->Prev; - - s32 SizeUsed = LastHead - Arena->CurrentRegion->Base; - Arena->CurrentRegion->Used = SizeUsed; - Result = Arena->CurrentRegion->Size - Arena->CurrentRegion->Used; - - Arena->UsedList = PrevAlloc; - } - - return Result; -} - -static void -InitStackMemoryArena (stack_memory_arena* Arena, s32 RegionMemorySize, - grow_arena_memory* GrowProc, free_arena_memory* FreeProc) -{ - ArenaZeroStruct(Arena); - Arena->RegionMemorySize = RegionMemorySize; - Arena->GrowArenaProc = GrowProc; - Arena->FreeArenaMemoryProc = FreeProc; -} - -static void -InitStackMemoryArena (stack_memory_arena* Arena, u8* Base, s32 Size, - s32 RegionMemorySize = 0, - grow_arena_memory* GrowProc = 0, - free_arena_memory* FreeProc = 0) -{ - Assert(Size > sizeof(memory_region_header)); - - Arena->CurrentRegion = (memory_region_header*)Base; - Arena->CurrentRegion->Base = Base + sizeof(memory_region_header); - Arena->CurrentRegion->Size = Size - sizeof(memory_region_header); - Arena->CurrentRegion->Used = 0; - Arena->CurrentRegion->Prev = 0; - - Arena->RegionMemorySize = RegionMemorySize; - Arena->GrowArenaProc = GrowProc; - Arena->FreeArenaMemoryProc = FreeProc; -} - -static void -ClearStackMemoryArena (stack_memory_arena* Arena) -{ - if (!Arena->CurrentRegion) { return; } - - memory_region_header* CurrentHead = Arena->CurrentRegion; - - if (CurrentHead->Prev) - { - Assert(Arena->FreeArenaMemoryProc); - while(CurrentHead->Prev) - { - memory_region_header* PrevHead = CurrentHead->Prev; - Arena->FreeArenaMemoryProc((u8*)CurrentHead, CurrentHead->Size + sizeof(memory_region_header)); - CurrentHead = PrevHead; - } - - Arena->CurrentRegion = CurrentHead; - } - - Arena->CurrentRegion->Used = 0; - Arena->UsedList = 0; -} - -////////////////////////////// -// Pool Memory -////////////////////////////// - -struct chunk_header -{ - chunk_header* Prev; -}; - -struct pool_memory_arena -{ - memory_region_header* CurrentRegion; - s32 ChunkSize; - chunk_header* FreeList; - - s32 RegionMemorySize; - grow_arena_memory* GrowArenaProc; - free_arena_memory* FreeArenaMemoryProc; -}; - -struct chunk_result -{ - s32 Size; - u8* Base; -}; - -static chunk_result -PushChunk (pool_memory_arena* Arena) -{ - chunk_result Result = {}; - - if (Arena->FreeList) - { - Result.Base = (u8*)Arena->FreeList; - Result.Size = Arena->ChunkSize; - - Arena->FreeList = Arena->FreeList->Prev; - } - else - { - if (!RegionCanFitSize(Arena->CurrentRegion, Arena->ChunkSize)) - { - if (Arena->GrowArenaProc) - { - grow_arena_result NewMemory = Arena->GrowArenaProc(Arena->RegionMemorySize + sizeof(memory_region_header)); - Assert(NewMemory.Size > 0); - - memory_region_header* Header = (memory_region_header*)NewMemory.Base; - Header->Base = (u8*)NewMemory.Base + sizeof(memory_region_header); - Header->Size = NewMemory.Size - sizeof(memory_region_header); - Header->Used = 0; - Header->Prev = Arena->CurrentRegion; - Arena->CurrentRegion = Header; - } - else - { - InvalidCodePath; - } - } - - Result.Base = Arena->CurrentRegion->Base + Arena->CurrentRegion->Used; - Result.Size = Arena->ChunkSize; - - Arena->CurrentRegion->Used += Arena->ChunkSize; - } - - return Result; -} - -static void -FreeChunk (pool_memory_arena* Arena, u8* Base, s32 Size) -{ - Assert(Arena->ChunkSize == Size); - - chunk_header* Header = (chunk_header*)Base; - Header->Prev = Arena->FreeList; - Arena->FreeList = Header; -} - -static void -InitPoolMemoryArena (pool_memory_arena* Arena, s32 ChunkSize, s32 ChunksPerRegion, - grow_arena_memory* GrowProc, free_arena_memory* FreeProc) -{ - Assert(ChunkSize > sizeof(chunk_header)); - - ArenaZeroStruct(Arena); - Arena->ChunkSize = ChunkSize; - Arena->RegionMemorySize = ChunkSize * ChunksPerRegion; - Arena->GrowArenaProc = GrowProc; - Arena->FreeArenaMemoryProc = FreeProc; -} - -static void -InitStackMemoryArena (pool_memory_arena* Arena, u8* Base, s32 Size, - s32 ChunkSize, s32 ChunksPerRegion, - grow_arena_memory* GrowProc = 0, - free_arena_memory* FreeProc = 0) -{ - Assert(Size > sizeof(memory_region_header)); - Assert(Size % ChunkSize == ChunksPerRegion); - - Arena->CurrentRegion = (memory_region_header*)Base; - Arena->CurrentRegion->Base = Base + sizeof(memory_region_header); - Arena->CurrentRegion->Size = Size - sizeof(memory_region_header); - Arena->CurrentRegion->Used = 0; - Arena->CurrentRegion->Prev = 0; - - Arena->ChunkSize = ChunkSize; - Arena->RegionMemorySize = ChunkSize * ChunksPerRegion; - Arena->GrowArenaProc = GrowProc; - Arena->FreeArenaMemoryProc = FreeProc; -} - -#define GS_MEMORY_H -#endif // GS_MEMORY_H diff --git a/src/gs_memory_arena.h b/src/gs_memory_arena.h new file mode 100644 index 0000000..bcf4746 --- /dev/null +++ b/src/gs_memory_arena.h @@ -0,0 +1,613 @@ +// File: gs_memory_arena.h +// Description: Single header file library that defines a push-only memory arena +// Author: Peter Slattery +// Date Created: 2019-12-22 +// +// +// ----------------- +// Set Up +// ----------------- +// +// Include 'gs_memory_arena.h' in a file and start using it! (Simple! Nice!) +// +// ----------------- +// Usage +// ----------------- +// Simply create a memory_arena and use PushSize, PushStruct, or PushArray +// to allocate out of it. +// See Example Program below. +// +// While there are options you can set (see Options below), the library adheres +// to a 'zero-is-initialization' policy, that is, a memory_arena initialized to +// zero, under all default options, will 'just work'. +// +// Alignment: +// By default, the Push functions use 4 byte alignment +// If you need to control the alignment of an allocation, there are variants of the +// Push functions that allow for this: PushSizeAligned, PushStructAligned, and PushArrayAligned +// These functions simply take a final parameter which specifies the alignment. +// Note: Alignment must be a power of two +// +// ----------------- +// Options +// ----------------- +// +// DEBUG: +// Define DEBUG for debug functionality. +// +// To override the default assert function define GSMem_Assert(expression) +// before inluding this file. +// +// GS_MEMORY_NO_STD_LIBS: +// if you don't want stdlib.h to be included, define GS_MEMORY_NO_STD_LIBS +// before including this file. +// Note that if you do this, zero-is-initialization will no longer work for +// memory_arenas. You must either: +// 1. Set each memory_arena's Alloc and Realloc so they can grow fields +// 2. Set each memory_arena's ExpansionRule to ExpansionRule_Disallowed +// If DEBUG is defined, the program will assert if one of the 2 rules above +// aren't followed. +// +// memory_arena.Alloc and memory_arena.Realloc +// By default, memory_arena's will use malloc and realloc to grow. +// You can override this by setting the Alloc and Realloc function pointers +// of a memory_arena. See the example program below for an implementation of this. +// +// GS_MEMORY_BUFFER_SIZE: +// This defines the minimum buffer size for memory_arena's. If an arena doesn't have +// room to fit an allocation, it will allocate a new buffer no smaller than GS_MEMORY_BUFFER_SIZE +// and place the allocation in the new buffer. +// By default this is 4096 bytes. To override, define GS_MEMORY_BUFFER_SIZE before including +// this file +// +// GS_MEMORY_TRACK_ALLOCATIONS: +// If you want to keep records of each allocation performed in every arena, define +// GS_MEMORY_TRACK_ALLOCATIONS before including this file. +// When defined, memory arenas gain fields that allow them to keep a list of every +// allocation they contain. It also adds a footer on the end of each allocation that +// can be checked to ensure there are no writes to allocations that overflow their bounds +// Note that calling ClearArena also clears this list +// You can then call AssertAllocationsNoOverflow occasionally throughout your program +// to check that no allocations have been written beyond their boundaries +// +// +// Example Program +// (this compiles - copy it into its own file though) +#if 0 +#include "gs_memory_arena.h" + +// Places the characters 'gs' at the end of each allocation. This would allow for an external +// function to check that we haven't written past the end of an allocation +void* MallocWrapper(gs_mem_u32 Size) +{ + int SizeWithFooter = Size + (sizeof(char) * 2); + void* Result = malloc(SizeWithFooter); + char* Footer = (char*)(Result + Size); + Footer[0] = 'g'; + Footer[1] = 's'; + return Result; +} + +void* ReallocWrapper(void* Address, gs_mem_u32 Size) +{ + return realloc(Address, Size); +} + +int +main(int ArgCount, char** Args) +{ + memory_arena Arena = {}; +// Uncomment these lines for an example of how you can implement custom allocation functions + // Arena.Alloc = MallocWrapper; + // Arena.Realloc = ReallocWrapper; + + int ArrayLength = 10; + +int* A = PushArray(&Arena, int, ArrayLength); + int* B = PushArray(&Arena, int, ArrayLength); + int* C = PushArray(&Arena, int, ArrayLength); + int* D = PushArrayAligned(&Arena, int, ArrayLength, 8); + int* E = PushArrayAligned(&Arena, int, ArrayLength, 16); + + // Just ensure that we can actually write to each address of each array + for (s32 i = 0; i < ArrayLength; i++) + { + A[i] = i; + B[i] = i; + C[i] = i; + D[i] = i; + E[i] = i; + } + + ClearArena(&Arena); + + A = PushArray(&Arena, int, ArrayLength); +for (s32 i = 0; i < ArrayLength; i++) + { + A[i] = i; + } + + return 0; +} +#endif + +// ------------------- +// Begin Library +// ------------------- +#ifndef GS_MEMORY_ARENA_H + +#ifndef GS_MEMORY_NO_STD_LIBS + +// NOTE(Peter): We use this so that we can fall back on malloc and realloc +// in the event that a memory_arena needs to grow but doesn't have a +// alloc or realloc function pointer assigned to it. +// +// See GrowArena to see where this is used +// +#include + +#endif + +typedef unsigned char gs_mem_u8; +typedef unsigned int gs_mem_u32; +typedef unsigned long long int gs_mem_u64; + +#ifdef DEBUG +#if !defined(GSMem_Assert) +#define GSMem_Assert(expression) \ +if(!(expression)) { \ +*((int *)0) = 5; \ +} + +#endif +#else +#define GSMem_Assert(expression) +#endif + +enum gs_memory_expansion_rule +{ + MemoryExpansion_Allowed, // Zero is initialization lets the memory grow on its own + MemoryExpansion_OnlyIfFunctionsProvided, + MemoryExpansion_Disallowed, + MemoryExpansion_Count, +}; + +// NOTE(Peter): +// This rule is only here to allow for taking arena snapshots. The problem this solves +// is if you take a snapshot while there are 'holes' in memory_buffers behind the +// most recently added memory_buffer, take a snapshot of that arena, then push something +// on that fits in one of those holes, we will fill the hole and be unable to track/free +// that addition via the snapshot construct. +// +// By requiring that allocations in a buffer only come from the most recent memory_buffer +// we can very easily rewind the buffer to the correct location. +// Hence FindAddress_InLastBufferOnly +enum gs_memory_find_address_rule +{ + FindAddress_InAnyBuffer, + FindAddress_InLastBufferOnly, + FindAddress_Count, +}; + +typedef void* gs_memory_alloc(gs_mem_u32 Size); +typedef void* gs_memory_realloc(void* Address, gs_mem_u32 OldSize, gs_mem_u32 NewSize); +typedef void gs_memory_free(void* Address, gs_mem_u32 Size); + +#ifndef GS_MEMORY_BUFFER_SIZE +#define GS_MEMORY_BUFFER_SIZE 1024 +#endif + +#define GS_MEMORY_FOOTER_SIZE 4 +#define GS_MEMORY_FOOTER_0 'g' +#define GS_MEMORY_FOOTER_1 's' +#define GS_MEMORY_FOOTER_2 'p' +#define GS_MEMORY_FOOTER_3 's' + +struct tracked_allocation +{ +gs_mem_u8* Head; + gs_mem_u8* Footer; + char* File; + gs_mem_u32 LineNumber; +}; + +#define GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE 512 +struct tracked_allocation_buffer +{ + tracked_allocation Buffer[GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE]; +}; + +struct memory_buffer +{ + gs_mem_u8* Buffer; + gs_mem_u32 Size; + gs_mem_u32 Used; +}; + +struct memory_arena +{ + memory_buffer* Buffers; + gs_mem_u32 BuffersCount; + + gs_mem_u32 TotalUsed; + gs_mem_u32 TotalSize; + + gs_memory_find_address_rule FindAddressRule; + gs_memory_expansion_rule ExpansionRule; + gs_memory_alloc* Alloc; + gs_memory_realloc* Realloc; + +#ifdef GS_MEMORY_TRACK_ALLOCATIONS + tracked_allocation_buffer** AllocationBuffers; + gs_mem_u32 AllocationBuffersCount; + gs_mem_u32 AllocationsUsed; + #endif +}; + +struct address_and_buffer +{ + memory_buffer* Buffer; + gs_mem_u64 Address; + gs_mem_u32 SizeWithAlignment; +}; + +struct arena_snapshot +{ + gs_mem_u32 ArenaUsedAtSnapshot; + gs_mem_u32 HeadBufferUsedAtSnapshot; + gs_mem_u32 HeadBufferAtSnapshot; + memory_arena* Arena; + +#ifdef GS_MEMORY_TRACK_ALLOCATIONS + gs_mem_u32 AllocationsUsedAtSnapshot; + #endif +}; + +static void +FreeMemoryArena(memory_arena* Arena, gs_memory_free* Free) +{ + if (Free) + { +for (gs_mem_u32 i = 0; i < Arena->BuffersCount; i++) + { + memory_buffer* Buffer = Arena->Buffers + i; + Free(Buffer->Buffer, Buffer->Size); + } + Free(Arena->Buffers, sizeof(memory_buffer) * Arena->BuffersCount); + } + else + { +#ifdef GS_MEMORY_NO_STD_LIBS + GSMem_Assert(0); +#else + for (gs_mem_u32 i = 0; i < Arena->BuffersCount; i++) + { + memory_buffer* Buffer = Arena->Buffers + i; + free(Buffer->Buffer); + } + free(Arena->Buffers); + #endif + } +} + +#define IsPowerOfTwo(v) ((v != 0) && ((v & (v - 1)) == 0)) + +inline gs_mem_u32 +GetAlignmentOffset (gs_mem_u64 Address, gs_mem_u32 Alignment, gs_mem_u32 AlignmentMask) +{ + gs_mem_u32 AlignmentOffset = 0; +if (Address & AlignmentMask) + { + AlignmentOffset = Alignment - (Address & AlignmentMask); + } + return AlignmentOffset; +} + +static address_and_buffer +GetAlignedAddressInBuffer(memory_buffer* Buffer, gs_mem_u32 Size, gs_mem_u32 Alignment, gs_mem_u32 AlignmentMask) +{ + address_and_buffer Result = {}; + + gs_mem_u64 HeadAddress = (gs_mem_u64)Buffer->Buffer + Buffer->Used; + gs_mem_u32 AlignmentOffset = GetAlignmentOffset(HeadAddress, Alignment, AlignmentMask); + gs_mem_u64 AlignedAddress = HeadAddress + AlignmentOffset; + +if (Buffer->Used + AlignmentOffset + Size <= Buffer->Size) + { + Result.Buffer = Buffer; + Result.Address = AlignedAddress; + Result.SizeWithAlignment = Size + AlignmentOffset; + } + + return Result; +} + +static address_and_buffer +FindAlignedAddressInBufferWithRoom(memory_arena* Arena, gs_mem_u32 Size, gs_mem_u32 Alignment, gs_mem_u32 AlignmentMask) +{ +address_and_buffer Result = {}; + for (gs_mem_u32 i = 0; i < Arena->BuffersCount; i++) + { + memory_buffer* At = Arena->Buffers + i; + GSMem_Assert(At); + + address_and_buffer AddressInCurrentBuffer = GetAlignedAddressInBuffer(At, Size, Alignment, AlignmentMask); + if (AddressInCurrentBuffer.Address != 0) + { + Result = AddressInCurrentBuffer; + break; + } + } + return Result; +} + +static gs_mem_u8* +ArenaAlloc(memory_arena* Arena, gs_mem_u32 Size) +{ + gs_mem_u8* Result = 0; + +if (Arena->Alloc) + { + Result = (gs_mem_u8*)Arena->Alloc(sizeof(gs_mem_u8) * Size); + } + else + { +#ifdef GS_MEMORY_NO_STD_LIBS + // NOTE(Peter): If you specify no std libs AND don't supply a allocation function + // we should assert as this is an invalid codepath + GSMem_Assert(0); +#else + Result = (gs_mem_u8*)malloc(sizeof(gs_mem_u8) * Size); + #endif + } + + return Result; +} + +static gs_mem_u8* +ArenaRealloc(memory_arena* Arena, gs_mem_u8* Head, gs_mem_u32 OldSize, gs_mem_u32 NewSize) +{ + gs_mem_u8* Result = 0; + + if (Arena->Realloc != 0) + { + Result = (gs_mem_u8*)Arena->Realloc(Head, OldSize, NewSize); + } + else + { +#ifdef GS_MEMORY_NO_STD_LIBS + // NOTE(Peter): If you specify no std libs AND don't supply a reallocation function + // we should assert as this is an invalid codepath + GSMem_Assert(0); + #else + Result = (gs_mem_u8*)realloc(Head, NewSize); + #endif + } + + return Result; +} + +static memory_buffer* +GrowArena(memory_arena* Arena, gs_mem_u32 SizeNeeded) +{ + GSMem_Assert(Arena->ExpansionRule != MemoryExpansion_Disallowed); + if (Arena->ExpansionRule == MemoryExpansion_OnlyIfFunctionsProvided) + { + GSMem_Assert((Arena->Alloc != 0) && (Arena->Realloc != 0)); + } + + gs_mem_u32 NewBuffersCount = (Arena->BuffersCount + 1); + gs_mem_u32 OldBuffersSize = sizeof(memory_buffer) * Arena->BuffersCount; + gs_mem_u32 NewBuffersSize = sizeof(memory_buffer) * NewBuffersCount; + Arena->Buffers = (memory_buffer*)ArenaRealloc(Arena, (gs_mem_u8*)Arena->Buffers, OldBuffersSize, NewBuffersSize); + Arena->BuffersCount = NewBuffersCount; + + memory_buffer* NewBuffer = Arena->Buffers + (Arena->BuffersCount - 1); + NewBuffer->Size = GS_MEMORY_BUFFER_SIZE; + if (SizeNeeded > NewBuffer->Size) + { + NewBuffer->Size = SizeNeeded; + } + + NewBuffer->Buffer = ArenaAlloc(Arena, sizeof(gs_mem_u8) * NewBuffer->Size); + NewBuffer->Used = 0; + + Arena->TotalSize += NewBuffer->Size; + return NewBuffer; +} + +#ifdef GS_MEMORY_TRACK_ALLOCATIONS + +#define DetermineAllocationSize(size) (size) + GS_MEMORY_FOOTER_SIZE +#define ClearAllocationsUsed(arena) (arena)->AllocationsUsed = 0 +#define ClearAllocationsUsedToSnapshot(arena, snapshot) \ +(arena)->AllocationsUsed = (snapshot).AllocationsUsedAtSnapshot; + +static void +TrackAllocation(memory_arena* Arena, gs_mem_u8* Head, gs_mem_u32 Size, char* Filename, gs_mem_u32 LineNumber) +{ +gs_mem_u32 AllocationsMax = Arena->AllocationBuffersCount * GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE; + if (Arena->AllocationsUsed >= AllocationsMax) + { + gs_mem_u32 NewAllocationBuffersCount = Arena->AllocationBuffersCount + 1; + Arena->AllocationBuffers = (tracked_allocation_buffer**)ArenaRealloc(Arena, + (gs_mem_u8*)Arena->AllocationBuffers, + Arena->AllocationBuffersCount * sizeof(void*), +NewAllocationBuffersCount * sizeof(void*)); + Arena->AllocationBuffersCount = NewAllocationBuffersCount; + + gs_mem_u32 NewBufferIndex = Arena->AllocationBuffersCount - 1; + Arena->AllocationBuffers[NewBufferIndex] = (tracked_allocation_buffer*)ArenaAlloc(Arena, sizeof(tracked_allocation_buffer)); + } + + gs_mem_u32 AllocationIndex = Arena->AllocationsUsed++; + gs_mem_u32 BufferIndex = AllocationIndex / GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE; + gs_mem_u32 IndexInBuffer = AllocationIndex % GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE; + tracked_allocation_buffer* Buffer = Arena->AllocationBuffers[BufferIndex]; + tracked_allocation* NewAllocationTracker = Buffer->Buffer + IndexInBuffer; + + NewAllocationTracker->Head = Head; + +NewAllocationTracker->Footer = Head + Size - GS_MEMORY_FOOTER_SIZE; + NewAllocationTracker->Footer[0] = GS_MEMORY_FOOTER_0; +NewAllocationTracker->Footer[1] = GS_MEMORY_FOOTER_1; +NewAllocationTracker->Footer[2] = GS_MEMORY_FOOTER_2; +NewAllocationTracker->Footer[3] = GS_MEMORY_FOOTER_3; + + NewAllocationTracker->File = Filename; + NewAllocationTracker->LineNumber = LineNumber; +} + + inline bool +VerifyAllocationNoOverflow (tracked_allocation Allocation) +{ + bool Result = ((Allocation.Footer[0] == GS_MEMORY_FOOTER_0) && +(Allocation.Footer[1] == GS_MEMORY_FOOTER_1) && +(Allocation.Footer[2] == GS_MEMORY_FOOTER_2) && + (Allocation.Footer[3] == GS_MEMORY_FOOTER_3)); + return Result; +} + + static void +AssertAllocationsNoOverflow (memory_arena Arena) +{ + for (gs_mem_u32 AllocationIndex = 0; + AllocationIndex< Arena.AllocationsUsed; + AllocationIndex++) + { + gs_mem_u32 BufferIndex = AllocationIndex / GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE; + gs_mem_u32 IndexInBuffer = AllocationIndex % GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE; + + tracked_allocation_buffer* Buffer = Arena.AllocationBuffers[BufferIndex]; + tracked_allocation Allocation = Buffer->Buffer[IndexInBuffer]; + +GSMem_Assert(VerifyAllocationNoOverflow(Allocation)); + } +} + +#define PushSize(arena, size) PushSize_((arena), (size), 4, __FILE__, __LINE__) +#define PushArray(arena, type, length) (type*)PushSize_((arena), sizeof(type) * length, 4, __FILE__, __LINE__) +#define PushStruct(arena, type) (type*)PushSize_((arena), sizeof(type), 4, __FILE__, __LINE__) +#define PushSizeAligned(arena, size, alignment) PushSize_((arena), (size), (alignment), __FILE__, __LINE__) +#define PushArrayAligned(arena, type, length, alignment) (type*)PushSize_((arena), sizeof(type) * length, (alignment), __FILE__, __LINE__) +#define PushStructAligned(arena, type, alignment) (type*)PushSize_((arena), sizeof(type), (alignment), __FILE__, __LINE__) + +#else // GS_MEMORY_TRACK_ALLOCATIONS + +#define AssertAllocationsNoOverflow(arena) +#define DetermineAllocationSize(size) size +#define ClearAllocationsUsed(arena) +#define ClearAllocationsUsedToSnapshot(arena, snapshot) + +#define TrackAllocation(arena, head, size, filename, linenumber) + +#define PushSize(arena, size) PushSize_((arena), (size)) +#define PushArray(arena, type, length) (type*)PushSize_((arena), sizeof(type) * length) +#define PushStruct(arena, type) (type*)PushSize_((arena), sizeof(type)) +#define PushSizeAligned(arena, size, alignment) PushSize_((arena), (size), (alignment)) +#define PushArrayAligned(arena, type, length, alignment) (type*)PushSize_((arena), sizeof(type) * length, (alignment)) +#define PushStructAligned(arena, type, alignment) (type*)PushSize_((arena), sizeof(type), (alignment)) + +#endif // GS_MEMORY_TRACK_ALLOCATIONS + +static gs_mem_u8* +PushSize_(memory_arena* Arena, gs_mem_u32 Size, gs_mem_u32 Alignment = 4, char* Filename = 0, gs_mem_u32 LineNumber = 0) +{ + // ie. Alignment = 4 = 100 (binary) + // 4 - 1 = 3 + // 100 - 1 = 011 which is a mask of the bits we don't want set in the start address + GSMem_Assert(IsPowerOfTwo(Alignment)); +gs_mem_u32 AlignmentMask = Alignment - 1; + + gs_mem_u32 AllocationSize = DetermineAllocationSize(Size); + + address_and_buffer ResultAddress = {}; + if (Arena->FindAddressRule == FindAddress_InAnyBuffer) + { + ResultAddress = FindAlignedAddressInBufferWithRoom(Arena, AllocationSize, Alignment, AlignmentMask); + } + else if (Arena->FindAddressRule == FindAddress_InLastBufferOnly + && Arena->BuffersCount > 0) + { + memory_buffer* LastBuffer = Arena->Buffers + Arena->BuffersCount - 1; + ResultAddress = GetAlignedAddressInBuffer(LastBuffer, Size, Alignment, AlignmentMask); + } + + if (ResultAddress.Address == 0) + { + memory_buffer* Buffer = GrowArena(Arena, AllocationSize); + ResultAddress = GetAlignedAddressInBuffer(Buffer, AllocationSize, Alignment, AlignmentMask); + } + GSMem_Assert(ResultAddress.Address != 0); +GSMem_Assert((ResultAddress.Address & AlignmentMask) == 0); + + gs_mem_u8* Result = (gs_mem_u8*)ResultAddress.Address; + ResultAddress.Buffer->Used += ResultAddress.SizeWithAlignment; + Arena->TotalUsed += ResultAddress.SizeWithAlignment; + + TrackAllocation(Arena, Result, AllocationSize, Filename, LineNumber); + + return Result; +} + +static void +ClearArena(memory_arena* Arena) +{ + for (gs_mem_u32 i = 0; i < Arena->BuffersCount; i++) + { + memory_buffer* At = Arena->Buffers + i; + At->Used = 0; + } + + Arena->TotalUsed = 0; + ClearAllocationsUsed(Arena); +} + +static arena_snapshot +TakeSnapshotOfArena(memory_arena* Arena) +{ + Assert(Arena->FindAddressRule == FindAddress_InLastBufferOnly); + +arena_snapshot Result = {}; + Result.Arena = Arena; + Result.ArenaUsedAtSnapshot = Arena->TotalUsed; + if (Arena->BuffersCount > 0) + { + Result.HeadBufferAtSnapshot = Arena->BuffersCount - 1; + } + else + { + Result.HeadBufferAtSnapshot = 0; + } + + memory_buffer* HeadBuffer = Arena->Buffers + Result.HeadBufferAtSnapshot; + if (HeadBuffer) + { + Result.HeadBufferUsedAtSnapshot = HeadBuffer->Used; + } + +return Result; +} + +static void +ClearArenaToSnapshot(memory_arena* Arena, arena_snapshot Snapshot) +{ + Assert(Arena == Snapshot.Arena); + + memory_buffer* HeadBufferAtSnapshot = Arena->Buffers + Snapshot.HeadBufferAtSnapshot; + if (HeadBufferAtSnapshot) + { + HeadBufferAtSnapshot->Used = Snapshot.HeadBufferUsedAtSnapshot; + + for (gs_mem_u32 i = Snapshot.HeadBufferAtSnapshot + 1; i < Arena->BuffersCount; i++) + { + memory_buffer* Buffer = Arena->Buffers + i; + Buffer->Used = 0; + } + } + + Arena->TotalUsed = Snapshot.ArenaUsedAtSnapshot; + ClearAllocationsUsedToSnapshot(Arena, Snapshot); +} +#define GS_MEMORY_ARENA_H +#endif // GS_MEMORY_ARENA_H \ No newline at end of file diff --git a/src/gs_platform.h b/src/gs_platform.h index 190c89e..9f1b5a7 100644 --- a/src/gs_platform.h +++ b/src/gs_platform.h @@ -75,6 +75,9 @@ typedef PLATFORM_ALLOC(platform_alloc); #define PLATFORM_FREE(name) b32 name(u8* Base, s32 Size) typedef PLATFORM_FREE(platform_free); +#define PLATFORM_REALLOC(name) u8* name(u8* Base, u32 OldSize, u32 NewSize) +typedef PLATFORM_REALLOC(platform_realloc); + #define PLATFORM_READ_ENTIRE_FILE(name) platform_memory_result name(char* Path) typedef PLATFORM_READ_ENTIRE_FILE(platform_read_entire_file); diff --git a/src/gs_win32.cpp b/src/gs_win32.cpp index 277b1aa..fd89867 100644 --- a/src/gs_win32.cpp +++ b/src/gs_win32.cpp @@ -61,6 +61,7 @@ internal void Win32DisplayBufferInWindow(win32_offscreen_buffer* Buffer internal PLATFORM_ALLOC(Win32Alloc); internal PLATFORM_FREE(Win32Free); +internal PLATFORM_REALLOC(Win32Realloc); // File IO internal PLATFORM_READ_ENTIRE_FILE(Win32ReadEntireFile); @@ -610,6 +611,17 @@ PLATFORM_FREE(Win32Free) return Result; } +PLATFORM_REALLOC(Win32Realloc) +{ + u8* NewMemory = Win32BasicAlloc(NewSize); + if (Base) + { + GSMemCopy(Base, NewMemory, OldSize); + Win32Free(Base, OldSize); + } + return NewMemory; +} + // File IO PLATFORM_READ_ENTIRE_FILE(Win32ReadEntireFile) { diff --git a/src/win32_foldhaus.cpp b/src/win32_foldhaus.cpp index e11a349..154a2c3 100644 --- a/src/win32_foldhaus.cpp +++ b/src/win32_foldhaus.cpp @@ -567,7 +567,8 @@ INT NCmdShow Context.GeneralWorkQueue = &WorkQueue; Context.PlatformAlloc = Win32Alloc; Context.PlatformFree = Win32Free; - Context.PlatformReadEntireFile = Win32ReadEntireFile; + Context.PlatformRealloc = Win32Realloc; +Context.PlatformReadEntireFile = Win32ReadEntireFile; Context.PlatformWriteEntireFile = Win32WriteEntireFile; Context.PlatformGetFilePath = Win32SystemDialogueOpenFile; Context.PlatformGetGPUTextureHandle = Win32GetGPUTextureHandle;