imported gs_memory_arena.h which is an improved rewrite of gs_memory.h. Integrated this new library in place of gs_memory.h
This commit is contained in:
parent
4d9f28dc6e
commit
dbc3886e91
|
@ -122,12 +122,10 @@ SACNSendDMXBufferListJob (s32 ThreadID, void* JobData)
|
||||||
internal void
|
internal void
|
||||||
LoadAssembly (app_state* State, context Context, char* Path)
|
LoadAssembly (app_state* State, context Context, char* Path)
|
||||||
{
|
{
|
||||||
arena_snapshot TempMemorySnapshot = TakeSnapshotOfArena(*State->Transient);
|
|
||||||
|
|
||||||
platform_memory_result TestAssemblyFile = Context.PlatformReadEntireFile(Path);
|
platform_memory_result TestAssemblyFile = Context.PlatformReadEntireFile(Path);
|
||||||
Assert(TestAssemblyFile.Size > 0);
|
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);
|
Context.PlatformFree(TestAssemblyFile.Base, TestAssemblyFile.Size);
|
||||||
|
|
||||||
|
@ -136,21 +134,19 @@ LoadAssembly (app_state* State, context Context, char* Path)
|
||||||
string FileName = Substring(PathString, IndexOfLastSlash + 1);
|
string FileName = Substring(PathString, IndexOfLastSlash + 1);
|
||||||
|
|
||||||
r32 Scale = 100;
|
r32 Scale = 100;
|
||||||
s32 AssemblyMemorySize = GetAssemblyMemorySizeFromDefinition(AssemblyDefinition, FileName);
|
memory_arena AssemblyArena = {};
|
||||||
u8* AssemblyMemory = Context.PlatformAlloc(AssemblyMemorySize);
|
AssemblyArena.Alloc = (gs_memory_alloc*)Context.PlatformAlloc;
|
||||||
|
AssemblyArena.Realloc = (gs_memory_realloc*)Context.PlatformRealloc;
|
||||||
|
|
||||||
assembly NewAssembly = ConstructAssemblyFromDefinition(AssemblyDefinition,
|
assembly NewAssembly = ConstructAssemblyFromDefinition(AssemblyDefinition,
|
||||||
FileName,
|
FileName,
|
||||||
v3{0, 0, 0},
|
v3{0, 0, 0},
|
||||||
Scale,
|
Scale,
|
||||||
AssemblyMemory,
|
AssemblyArena);
|
||||||
AssemblyMemorySize);
|
|
||||||
array_entry_handle NewAssemblyHandle = PushElement(NewAssembly, &State->AssemblyList);
|
array_entry_handle NewAssemblyHandle = PushElement(NewAssembly, &State->AssemblyList);
|
||||||
PushElement(NewAssemblyHandle, &State->ActiveAssemblyIndecies);
|
PushElement(NewAssemblyHandle, &State->ActiveAssemblyIndecies);
|
||||||
|
|
||||||
State->TotalLEDsCount += NewAssembly.LEDCount;
|
State->TotalLEDsCount += NewAssembly.LEDCount;
|
||||||
|
|
||||||
ClearArenaToSnapshot(State->Transient, TempMemorySnapshot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
|
@ -158,7 +154,7 @@ UnloadAssembly (s32 AssemblyIndex, app_state* State, context Context)
|
||||||
{
|
{
|
||||||
assembly* Assembly = GetElementAtIndex(AssemblyIndex, State->AssemblyList);
|
assembly* Assembly = GetElementAtIndex(AssemblyIndex, State->AssemblyList);
|
||||||
State->TotalLEDsCount -= Assembly->LEDCount;
|
State->TotalLEDsCount -= Assembly->LEDCount;
|
||||||
Context.PlatformFree(Assembly->Arena.Base, Assembly->Arena.Size);
|
FreeMemoryArena(&Assembly->Arena, (gs_memory_free*)Context.PlatformFree);
|
||||||
|
|
||||||
RemoveElementAtIndex(AssemblyIndex, &State->AssemblyList);
|
RemoveElementAtIndex(AssemblyIndex, &State->AssemblyList);
|
||||||
for (s32 i = 0; i < State->ActiveAssemblyIndecies.Used; i++)
|
for (s32 i = 0; i < State->ActiveAssemblyIndecies.Used; i++)
|
||||||
|
@ -193,27 +189,22 @@ RELOAD_STATIC_DATA(ReloadStaticData)
|
||||||
INITIALIZE_APPLICATION(InitializeApplication)
|
INITIALIZE_APPLICATION(InitializeApplication)
|
||||||
{
|
{
|
||||||
app_state* State = (app_state*)Context.MemoryBase;
|
app_state* State = (app_state*)Context.MemoryBase;
|
||||||
u8* MemoryCursor = Context.MemoryBase + sizeof(app_state);
|
State->Permanent = {};
|
||||||
s32 PermanentStorageSize = Context.MemorySize; //Megabytes(32);
|
State->Permanent.Alloc = (gs_memory_alloc*)Context.PlatformAlloc;
|
||||||
//s32 TransientStorageSize = Context.MemorySize - PermanentStorageSize;
|
State->Permanent.Realloc = (gs_memory_realloc*)Context.PlatformRealloc;
|
||||||
State->Permanent = BootstrapArenaIntoMemory(MemoryCursor, PermanentStorageSize);
|
State->Transient = {};
|
||||||
//State->Transient = BootstrapArenaIntoMemory(MemoryCursor + PermanentStorageSize, TransientStorageSize);
|
State->Transient.Alloc = (gs_memory_alloc*)Context.PlatformAlloc;
|
||||||
|
State->Transient.Realloc = (gs_memory_realloc*)Context.PlatformRealloc;
|
||||||
|
|
||||||
u8* TransientMemory = Context.PlatformAlloc(Megabytes(32));
|
InitializeInputCommandRegistry(&State->DefaultInputCommandRegistry, 32, &State->Permanent);
|
||||||
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);
|
|
||||||
|
|
||||||
s32 CommandQueueSize = 32;
|
s32 CommandQueueSize = 32;
|
||||||
command_queue_entry* CommandQueueMemory = PushArray(State->Permanent,
|
command_queue_entry* CommandQueueMemory = PushArray(&State->Permanent,
|
||||||
command_queue_entry,
|
command_queue_entry,
|
||||||
CommandQueueSize);
|
CommandQueueSize);
|
||||||
State->CommandQueue = InitializeCommandQueue(CommandQueueMemory, 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?
|
// TODO(Peter): put in InitializeInterface?
|
||||||
r32 FontSize = 14;
|
r32 FontSize = 14;
|
||||||
|
@ -221,12 +212,12 @@ INITIALIZE_APPLICATION(InitializeApplication)
|
||||||
platform_memory_result FontFile = Context.PlatformReadEntireFile("Anonymous Pro.ttf");
|
platform_memory_result FontFile = Context.PlatformReadEntireFile("Anonymous Pro.ttf");
|
||||||
if (FontFile.Size)
|
if (FontFile.Size)
|
||||||
{
|
{
|
||||||
bitmap_font* Font = PushStruct(State->Permanent, bitmap_font);
|
bitmap_font* Font = PushStruct(&State->Permanent, bitmap_font);
|
||||||
|
|
||||||
Font->BitmapWidth = 512;
|
Font->BitmapWidth = 512;
|
||||||
Font->BitmapHeight = 512;
|
Font->BitmapHeight = 512;
|
||||||
Font->BitmapBytesPerPixel = 4;
|
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;
|
Font->BitmapStride = Font->BitmapWidth * Font->BitmapBytesPerPixel;
|
||||||
GSMemSet(Font->BitmapMemory, 0, Font->BitmapStride * Font->BitmapHeight);
|
GSMemSet(Font->BitmapMemory, 0, Font->BitmapStride * Font->BitmapHeight);
|
||||||
|
|
||||||
|
@ -239,8 +230,8 @@ INITIALIZE_APPLICATION(InitializeApplication)
|
||||||
|
|
||||||
Font->CodepointDictionarySize = (FontInfo.CodepointOnePastLast - FontInfo.CodepointStart);
|
Font->CodepointDictionarySize = (FontInfo.CodepointOnePastLast - FontInfo.CodepointStart);
|
||||||
Font->CodepointDictionaryCount = 0;
|
Font->CodepointDictionaryCount = 0;
|
||||||
Font->CodepointKeys = PushArray(State->Permanent, char, Font->CodepointDictionarySize);
|
Font->CodepointKeys = PushArray(&State->Permanent, char, Font->CodepointDictionarySize);
|
||||||
Font->CodepointValues = PushArray(State->Permanent, codepoint_bitmap, Font->CodepointDictionarySize);
|
Font->CodepointValues = PushArray(&State->Permanent, codepoint_bitmap, Font->CodepointDictionarySize);
|
||||||
|
|
||||||
for (s32 Codepoint = FontInfo.CodepointStart;
|
for (s32 Codepoint = FontInfo.CodepointStart;
|
||||||
Codepoint < FontInfo.CodepointOnePastLast;
|
Codepoint < FontInfo.CodepointOnePastLast;
|
||||||
|
@ -296,7 +287,7 @@ INITIALIZE_APPLICATION(InitializeApplication)
|
||||||
State->AssemblyList.FreeList.Next = &State->AssemblyList.FreeList;
|
State->AssemblyList.FreeList.Next = &State->AssemblyList.FreeList;
|
||||||
State->ActiveAssemblyIndecies.BucketSize = 32;
|
State->ActiveAssemblyIndecies.BucketSize = 32;
|
||||||
#if 1
|
#if 1
|
||||||
char Path[] = "blumen_lumen.fold";
|
char Path[] = "radialumia.fold";
|
||||||
LoadAssembly(State, Context, Path);
|
LoadAssembly(State, Context, Path);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -308,10 +299,10 @@ INITIALIZE_APPLICATION(InitializeApplication)
|
||||||
|
|
||||||
{ // MODES PLAYGROUND
|
{ // MODES PLAYGROUND
|
||||||
State->Modes.ActiveModesCount = 0;
|
State->Modes.ActiveModesCount = 0;
|
||||||
|
State->Modes.Arena = {};
|
||||||
s32 ModesMemorySize = Kilobytes(32);
|
State->Modes.Arena.Alloc = (gs_memory_alloc*)Context.PlatformAlloc;
|
||||||
u8* ModesMemory = PushSize(State->Permanent, ModesMemorySize);
|
State->Modes.Arena.Realloc = (gs_memory_realloc*)Context.PlatformRealloc;
|
||||||
InitMemoryArena(&State->Modes.Arena, ModesMemory, ModesMemorySize, 0);
|
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
|
// 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
|
// 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.
|
// incorrect to clear the arena, and then access the memory later.
|
||||||
ClearArena(State->Transient);
|
ClearArena(&State->Transient);
|
||||||
|
|
||||||
HandleInput(State, InputQueue, Mouse);
|
HandleInput(State, InputQueue, Mouse);
|
||||||
|
|
||||||
|
@ -515,7 +506,7 @@ UPDATE_AND_RENDER(UpdateAndRender)
|
||||||
{
|
{
|
||||||
array_entry_handle AssemblyHandle = *GetElementAtIndex(i, State->ActiveAssemblyIndecies);
|
array_entry_handle AssemblyHandle = *GetElementAtIndex(i, State->ActiveAssemblyIndecies);
|
||||||
assembly Assembly = *GetElementWithHandle(AssemblyHandle, State->AssemblyList);
|
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);
|
DMXBuffers = DMXBufferListAppend(DMXBuffers, NewDMXBuffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,7 +526,7 @@ UPDATE_AND_RENDER(UpdateAndRender)
|
||||||
CurrentDMXBuffer = CurrentDMXBuffer->Next;
|
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->SendSocket = State->SACN.SendSocket;
|
||||||
Job->SendTo = Context.PlatformSendTo;
|
Job->SendTo = Context.PlatformSendTo;
|
||||||
Job->DMXBuffers = DMXBuffers;
|
Job->DMXBuffers = DMXBuffers;
|
||||||
|
@ -584,7 +575,7 @@ UPDATE_AND_RENDER(UpdateAndRender)
|
||||||
|
|
||||||
for (s32 Job = 0; Job < JobsNeeded; Job++)
|
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->LEDs = Assembly.LEDs;
|
||||||
JobData->Colors = Assembly.Colors;
|
JobData->Colors = Assembly.Colors;
|
||||||
JobData->StartIndex = Job * MaxLEDsPerJob;
|
JobData->StartIndex = Job * MaxLEDsPerJob;
|
||||||
|
@ -630,7 +621,7 @@ UPDATE_AND_RENDER(UpdateAndRender)
|
||||||
MakeStringLiteral("Load Assembly"),
|
MakeStringLiteral("Load Assembly"),
|
||||||
State->Interface, Mouse);
|
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++)
|
for (s32 i = 0; i < State->ActiveAssemblyIndecies.Used; i++)
|
||||||
{
|
{
|
||||||
array_entry_handle AssemblyHandle = *GetElementAtIndex(i, State->ActiveAssemblyIndecies);
|
array_entry_handle AssemblyHandle = *GetElementAtIndex(i, State->ActiveAssemblyIndecies);
|
||||||
|
@ -669,7 +660,18 @@ UPDATE_AND_RENDER(UpdateAndRender)
|
||||||
|
|
||||||
DrawDebugInterface(RenderBuffer, 25,
|
DrawDebugInterface(RenderBuffer, 25,
|
||||||
State->Interface, Context.WindowWidth, Context.WindowHeight - TopBarHeight,
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,8 @@ enum network_protocol
|
||||||
|
|
||||||
struct app_state
|
struct app_state
|
||||||
{
|
{
|
||||||
memory_arena* Permanent;
|
memory_arena Permanent;
|
||||||
memory_arena TransientMemory;
|
memory_arena Transient;
|
||||||
memory_arena* Transient;
|
|
||||||
memory_arena SACNMemory;
|
|
||||||
|
|
||||||
s32 NetworkProtocolHeaderSize;
|
s32 NetworkProtocolHeaderSize;
|
||||||
network_protocol NetworkProtocol;
|
network_protocol NetworkProtocol;
|
||||||
|
|
|
@ -12,11 +12,10 @@ ConstructAssemblyFromDefinition (assembly_definition Definition,
|
||||||
string AssemblyName,
|
string AssemblyName,
|
||||||
v3 RootPosition,
|
v3 RootPosition,
|
||||||
r32 Scale,
|
r32 Scale,
|
||||||
u8* MemoryBase,
|
memory_arena Arena)
|
||||||
s32 MemorySize)
|
|
||||||
{
|
{
|
||||||
assembly Assembly = {};
|
assembly Assembly = {};
|
||||||
Assembly.Arena = CreateMemoryArena(MemoryBase, MemorySize);
|
Assembly.Arena = Arena;
|
||||||
|
|
||||||
Assembly.Name = MakeString(PushArray(&Assembly.Arena, char, AssemblyName.Length), AssemblyName.Length);
|
Assembly.Name = MakeString(PushArray(&Assembly.Arena, char, AssemblyName.Length), AssemblyName.Length);
|
||||||
CopyStringTo(AssemblyName, &Assembly.Name);
|
CopyStringTo(AssemblyName, &Assembly.Name);
|
||||||
|
|
|
@ -24,7 +24,7 @@ struct leds_in_universe_range
|
||||||
|
|
||||||
struct assembly
|
struct assembly
|
||||||
{
|
{
|
||||||
static_memory_arena Arena;
|
memory_arena Arena;
|
||||||
|
|
||||||
string Name;
|
string Name;
|
||||||
string FilePath;
|
string FilePath;
|
||||||
|
|
|
@ -197,7 +197,7 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c
|
||||||
v2 TopOfDebugView = v2{StartX, WindowHeight - (NewLineYOffset(*Interface.Font) + 5)};
|
v2 TopOfDebugView = v2{StartX, WindowHeight - (NewLineYOffset(*Interface.Font) + 5)};
|
||||||
v2 TopOfScreenLinePos = TopOfDebugView;
|
v2 TopOfScreenLinePos = TopOfDebugView;
|
||||||
|
|
||||||
arena_snapshot StartTempMemory = TakeSnapshotOfArena(*Transient);
|
//arena_snapshot StartTempMemory = TakeSnapshotOfArena(*Transient);
|
||||||
|
|
||||||
string DebugString = InitializeEmptyString(PushArray(Transient, char, 256), 256);
|
string DebugString = InitializeEmptyString(PushArray(Transient, char, 256), 256);
|
||||||
|
|
||||||
|
@ -215,8 +215,8 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c
|
||||||
5, DeltaTime,
|
5, DeltaTime,
|
||||||
(u32)FramesPerSecond,
|
(u32)FramesPerSecond,
|
||||||
State->Modes.ActiveModesCount,
|
State->Modes.ActiveModesCount,
|
||||||
State->Modes.Arena.CurrentRegion->Used,
|
State->Modes.Arena.TotalUsed,
|
||||||
State->Modes.Arena.CurrentRegion->Size,
|
State->Modes.Arena.TotalSize,
|
||||||
State->CommandQueue.Used);
|
State->CommandQueue.Used);
|
||||||
DrawString(RenderBuffer, DebugString, Interface.Font, TopOfScreenLinePos, WhiteV4);
|
DrawString(RenderBuffer, DebugString, Interface.Font, TopOfScreenLinePos, WhiteV4);
|
||||||
|
|
||||||
|
@ -356,7 +356,4 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ZeroArenaToSnapshot(Transient, StartTempMemory);
|
|
||||||
ClearArenaToSnapshot(Transient, StartTempMemory);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#ifndef GS_MEMORY_H
|
#ifndef GS_MEMORY_H
|
||||||
|
|
||||||
|
#if 0
|
||||||
#ifndef GS_LANGUAGE_H
|
#ifndef GS_LANGUAGE_H
|
||||||
|
|
||||||
typedef uint8_t u8;
|
typedef uint8_t u8;
|
||||||
|
@ -230,6 +231,7 @@ ClearArenaToSnapshot (memory_arena* Arena, arena_snapshot Snapshot)
|
||||||
Assert(RegionCursor == Snapshot.CurrentRegion);
|
Assert(RegionCursor == Snapshot.CurrentRegion);
|
||||||
RegionCursor->Used = Snapshot.UsedAtSnapshot;
|
RegionCursor->Used = Snapshot.UsedAtSnapshot;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// Basic Memory Arena
|
// Basic Memory Arena
|
||||||
|
@ -253,6 +255,7 @@ CreateMemoryArena (u8* Base, u32 Size)
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define PushArrayOnStaticArena(arena, type, length) (type*)PushSize_((arena), sizeof(type) * length)
|
||||||
static u8*
|
static u8*
|
||||||
PushSize_ (static_memory_arena* Arena, u32 Size)
|
PushSize_ (static_memory_arena* Arena, u32 Size)
|
||||||
{
|
{
|
||||||
|
@ -262,80 +265,5 @@ PushSize_ (static_memory_arena* Arena, u32 Size)
|
||||||
return Result;
|
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
|
#define GS_MEMORY_H
|
||||||
#endif // GS_MEMORY_H
|
#endif // GS_MEMORY_H
|
|
@ -28,7 +28,7 @@ ActivateOperationMode (operation_mode_system* System)
|
||||||
Assert(System->ActiveModesCount < OPERATION_MODES_MAX);
|
Assert(System->ActiveModesCount < OPERATION_MODES_MAX);
|
||||||
s32 ModeIndex = System->ActiveModesCount++;
|
s32 ModeIndex = System->ActiveModesCount++;
|
||||||
|
|
||||||
System->ModeMemorySnapshots[ModeIndex] = TakeSnapshotOfArena(System->Arena);
|
System->ModeMemorySnapshots[ModeIndex] = TakeSnapshotOfArena(&System->Arena);
|
||||||
|
|
||||||
operation_mode NewMode = {};
|
operation_mode NewMode = {};
|
||||||
System->ActiveModes[ModeIndex] = NewMode;
|
System->ActiveModes[ModeIndex] = NewMode;
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
#include "gs_array.h"
|
#include "gs_array.h"
|
||||||
|
|
||||||
#include "foldhaus_memory.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 "gs_string.h"
|
||||||
#include "foldhaus_debug.h"
|
#include "foldhaus_debug.h"
|
||||||
|
|
||||||
|
@ -133,6 +138,7 @@ struct context
|
||||||
|
|
||||||
platform_alloc* PlatformAlloc;
|
platform_alloc* PlatformAlloc;
|
||||||
platform_free* PlatformFree;
|
platform_free* PlatformFree;
|
||||||
|
platform_realloc* PlatformRealloc;
|
||||||
platform_read_entire_file* PlatformReadEntireFile;
|
platform_read_entire_file* PlatformReadEntireFile;
|
||||||
platform_write_entire_file* PlatformWriteEntireFile;
|
platform_write_entire_file* PlatformWriteEntireFile;
|
||||||
platform_get_file_path* PlatformGetFilePath;
|
platform_get_file_path* PlatformGetFilePath;
|
||||||
|
|
461
src/gs_memory.h
461
src/gs_memory.h
|
@ -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
|
|
|
@ -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 <stdlib.h>
|
||||||
|
|
||||||
|
#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
|
|
@ -75,6 +75,9 @@ typedef PLATFORM_ALLOC(platform_alloc);
|
||||||
#define PLATFORM_FREE(name) b32 name(u8* Base, s32 Size)
|
#define PLATFORM_FREE(name) b32 name(u8* Base, s32 Size)
|
||||||
typedef PLATFORM_FREE(platform_free);
|
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)
|
#define PLATFORM_READ_ENTIRE_FILE(name) platform_memory_result name(char* Path)
|
||||||
typedef PLATFORM_READ_ENTIRE_FILE(platform_read_entire_file);
|
typedef PLATFORM_READ_ENTIRE_FILE(platform_read_entire_file);
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ internal void Win32DisplayBufferInWindow(win32_offscreen_buffer* Buffer
|
||||||
|
|
||||||
internal PLATFORM_ALLOC(Win32Alloc);
|
internal PLATFORM_ALLOC(Win32Alloc);
|
||||||
internal PLATFORM_FREE(Win32Free);
|
internal PLATFORM_FREE(Win32Free);
|
||||||
|
internal PLATFORM_REALLOC(Win32Realloc);
|
||||||
|
|
||||||
// File IO
|
// File IO
|
||||||
internal PLATFORM_READ_ENTIRE_FILE(Win32ReadEntireFile);
|
internal PLATFORM_READ_ENTIRE_FILE(Win32ReadEntireFile);
|
||||||
|
@ -610,6 +611,17 @@ PLATFORM_FREE(Win32Free)
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PLATFORM_REALLOC(Win32Realloc)
|
||||||
|
{
|
||||||
|
u8* NewMemory = Win32BasicAlloc(NewSize);
|
||||||
|
if (Base)
|
||||||
|
{
|
||||||
|
GSMemCopy(Base, NewMemory, OldSize);
|
||||||
|
Win32Free(Base, OldSize);
|
||||||
|
}
|
||||||
|
return NewMemory;
|
||||||
|
}
|
||||||
|
|
||||||
// File IO
|
// File IO
|
||||||
PLATFORM_READ_ENTIRE_FILE(Win32ReadEntireFile)
|
PLATFORM_READ_ENTIRE_FILE(Win32ReadEntireFile)
|
||||||
{
|
{
|
||||||
|
|
|
@ -567,7 +567,8 @@ INT NCmdShow
|
||||||
Context.GeneralWorkQueue = &WorkQueue;
|
Context.GeneralWorkQueue = &WorkQueue;
|
||||||
Context.PlatformAlloc = Win32Alloc;
|
Context.PlatformAlloc = Win32Alloc;
|
||||||
Context.PlatformFree = Win32Free;
|
Context.PlatformFree = Win32Free;
|
||||||
Context.PlatformReadEntireFile = Win32ReadEntireFile;
|
Context.PlatformRealloc = Win32Realloc;
|
||||||
|
Context.PlatformReadEntireFile = Win32ReadEntireFile;
|
||||||
Context.PlatformWriteEntireFile = Win32WriteEntireFile;
|
Context.PlatformWriteEntireFile = Win32WriteEntireFile;
|
||||||
Context.PlatformGetFilePath = Win32SystemDialogueOpenFile;
|
Context.PlatformGetFilePath = Win32SystemDialogueOpenFile;
|
||||||
Context.PlatformGetGPUTextureHandle = Win32GetGPUTextureHandle;
|
Context.PlatformGetGPUTextureHandle = Win32GetGPUTextureHandle;
|
||||||
|
|
Loading…
Reference in New Issue