462 lines
14 KiB
C
462 lines
14 KiB
C
#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
|