Lumenarium/gs_memory.h

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