Default allocator = null, move allocator definitions

Allocator is null by default, this is to catch any unwanted used of the heap.
This commit is contained in:
doyle 2020-04-05 08:50:30 +10:00
parent f743c230e7
commit ce63bb7755
3 changed files with 119 additions and 99 deletions

View File

@ -592,16 +592,14 @@ DQN_HEADER_COPY_PROTOTYPE(template <typename T> T *, Dqn_MemZero(T *src))
// @ // @
// @ ------------------------------------------------------------------------------------------------- // @ -------------------------------------------------------------------------------------------------
// @ Custom allocations must include Dqn_AllocateMetadata before the aligned_ptr, see Dqn_AllocateMetadata for more information // @ Custom allocations must include Dqn_AllocateMetadata before the aligned_ptr, see Dqn_AllocateMetadata for more information
DQN_HEADER_COPY_BEGIN DQN_HEADER_COPY_BEGIN
// NOTE: The default allocator is the heap allocator
enum struct Dqn_Allocator_Type enum struct Dqn_Allocator_Type
{ {
Null,
Heap, // Malloc, realloc, free Heap, // Malloc, realloc, free
XHeap, // Malloc realloc, free, crash on failure XHeap, // Malloc realloc, free, crash on failure
Arena, Arena,
Custom, Custom,
Null,
}; };
#define DQN_ALLOCATOR_ALLOCATE_PROC(name) void *name(Dqn_isize size, Dqn_u8 alignment) #define DQN_ALLOCATOR_ALLOCATE_PROC(name) void *name(Dqn_isize size, Dqn_u8 alignment)
@ -630,76 +628,8 @@ struct Dqn_Allocator
Dqn_Allocator_ReallocProc *realloc; Dqn_Allocator_ReallocProc *realloc;
Dqn_Allocator_FreeProc *free; Dqn_Allocator_FreeProc *free;
}; };
DQN_HEADER_COPY_END DQN_HEADER_COPY_END
// @ -------------------------------------------------------------------------------------------------
// @
// @ NOTE: Dqn_AllocatorMetadata
// @
// @ -------------------------------------------------------------------------------------------------
// @ Custom Dqn_Allocator implementations must include allocation metadata exactly (aligned_ptr - sizeof(Dqn_AllocatorMetadata)) bytes from the aligned ptr.
DQN_HEADER_COPY_BEGIN
struct Dqn_AllocateMetadata
{
Dqn_u8 alignment;
Dqn_u8 offset; // Subtract offset from aligned ptr to return to the allocation ptr
};
DQN_HEADER_COPY_END
// @ -------------------------------------------------------------------------------------------------
// @
// @ NOTE: Dqn_MemArena
// @
// @ -------------------------------------------------------------------------------------------------
DQN_HEADER_COPY_BEGIN
struct Dqn_MemBlock
{
void *memory;
Dqn_usize size;
Dqn_usize used;
Dqn_MemBlock *prev;
Dqn_MemBlock *next;
};
struct Dqn_MemArena
{
// NOTE: Configuration (fill once after "Zero Initialisation {}")
int min_block_size = DQN_KILOBYTES(4);
Dqn_Allocator allocator;
// NOTE: Read Only
Dqn_MemBlock *curr_mem_block;
Dqn_MemBlock *top_mem_block;
Dqn_usize highest_used_mark;
int total_allocated_mem_blocks;
};
struct Dqn_MemArenaScopedRegion
{
Dqn_MemArenaScopedRegion(Dqn_MemArena *arena);
~Dqn_MemArenaScopedRegion();
Dqn_MemArena *arena;
Dqn_MemBlock *curr_mem_block;
Dqn_usize curr_mem_block_used;
Dqn_MemBlock *top_mem_block;
};
void * Dqn_MemArena_Allocate(Dqn_MemArena *arena, Dqn_usize size, Dqn_u8 alignment);
Dqn_b32 Dqn_MemArena_Reserve(Dqn_MemArena *arena, Dqn_usize size);
DQN_HEADER_COPY_PROTOTYPE(template <typename T> T *, Dqn_MemArena_AllocateType(Dqn_MemArena *arena, Dqn_isize num))
{
auto *result = DQN_CAST(T *)Dqn_MemArena_Allocate(arena, sizeof(T) * num, alignof(T));
return result;
}
DQN_HEADER_COPY_END
// @ -------------------------------------------------------------------------------------------------
// @
// @ NOTE: Dqn_Allocator
// @
// @ -------------------------------------------------------------------------------------------------
DQN_HEADER_COPY_PROTOTYPE(Dqn_Allocator, inline Dqn_Allocator_Null()) DQN_HEADER_COPY_PROTOTYPE(Dqn_Allocator, inline Dqn_Allocator_Null())
{ {
Dqn_Allocator result = {}; Dqn_Allocator result = {};
@ -737,6 +667,73 @@ DQN_HEADER_COPY_PROTOTYPE(template <typename T> T *, Dqn_Allocator_AllocateType(
} }
// @ -------------------------------------------------------------------------------------------------
// @
// @ NOTE: Dqn_AllocatorMetadata
// @
// @ -------------------------------------------------------------------------------------------------
// @ Custom Dqn_Allocator implementations must include allocation metadata exactly (aligned_ptr - sizeof(Dqn_AllocatorMetadata)) bytes from the aligned ptr.
DQN_HEADER_COPY_BEGIN
struct Dqn_AllocateMetadata
{
Dqn_u8 alignment;
Dqn_u8 offset; // Subtract offset from aligned ptr to return to the allocation ptr
};
DQN_HEADER_COPY_END
// @ -------------------------------------------------------------------------------------------------
// @
// @ NOTE: Dqn_MemArena
// @
// @ -------------------------------------------------------------------------------------------------
DQN_HEADER_COPY_BEGIN
struct Dqn_MemBlock
{
void *memory;
Dqn_usize size;
Dqn_usize used;
Dqn_MemBlock *prev;
Dqn_MemBlock *next;
};
Dqn_usize const DQN_MEM_ARENA_DEFAULT_MIN_BLOCK_SIZE = DQN_KILOBYTES(4);
struct Dqn_MemArena
{
// NOTE: Configuration (fill once after "Zero Initialisation {}")
Dqn_usize min_block_size;
Dqn_Allocator allocator;
// NOTE: Read Only
Dqn_MemBlock *curr_mem_block;
Dqn_MemBlock *top_mem_block;
Dqn_usize highest_used_mark;
int total_allocated_mem_blocks;
};
struct Dqn_MemArenaRegion
{
Dqn_MemArena *arena;
Dqn_MemBlock *curr_mem_block;
Dqn_usize curr_mem_block_used;
Dqn_MemBlock *top_mem_block;
};
struct Dqn_MemArenaScopedRegion
{
Dqn_MemArenaScopedRegion(Dqn_MemArena *arena);
~Dqn_MemArenaScopedRegion();
Dqn_MemArenaRegion region;
};
void * Dqn_MemArena_Allocate(Dqn_MemArena *arena, Dqn_usize size, Dqn_u8 alignment);
Dqn_b32 Dqn_MemArena_Reserve(Dqn_MemArena *arena, Dqn_usize size);
DQN_HEADER_COPY_PROTOTYPE(template <typename T> T *, Dqn_MemArena_AllocateType(Dqn_MemArena *arena, Dqn_isize num))
{
auto *result = DQN_CAST(T *)Dqn_MemArena_Allocate(arena, sizeof(T) * num, alignof(T));
return result;
}
DQN_HEADER_COPY_END
// @ ------------------------------------------------------------------------------------------------- // @ -------------------------------------------------------------------------------------------------
// @ // @
// @ NOTE: String // @ NOTE: String
@ -849,6 +846,12 @@ template <Dqn_usize N> DQN_FILE_SCOPE void Dqn_StringBuilder__BuildOutput(Dqn_St
return; return;
} }
if (!dest)
{
DQN_ASSERT(dest);
return;
}
char const *end = dest + dest_size; char const *end = dest + dest_size;
char *buf_ptr = dest; char *buf_ptr = dest;
@ -1576,7 +1579,7 @@ DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Allocate(Dqn_Allocator *allocato
char *result = nullptr; char *result = nullptr;
switch (allocator->type) switch (allocator->type)
{ {
case Dqn_Allocator_Type::Null: case Dqn_Allocator_Type::Null: DQN_ASSERT(DQN_INVALID_CODE_PATH);
default: break; default: break;
case Dqn_Allocator_Type::Heap: case Dqn_Allocator_Type::Heap:
@ -1707,8 +1710,10 @@ DQN_HEADER_COPY_PROTOTYPE(void, Dqn_Allocator_Free(Dqn_Allocator *allocator, voi
// @ ------------------------------------------------------------------------------------------------- // @ -------------------------------------------------------------------------------------------------
DQN_FILE_SCOPE Dqn_MemBlock *Dqn_MemArena__AllocateBlock(Dqn_MemArena *arena, Dqn_usize requested_size) DQN_FILE_SCOPE Dqn_MemBlock *Dqn_MemArena__AllocateBlock(Dqn_MemArena *arena, Dqn_usize requested_size)
{ {
DQN_ASSERT(arena->min_block_size > 0); Dqn_usize min_block_size = arena->min_block_size;
Dqn_usize mem_block_size = DQN_MAX(DQN_CAST(Dqn_usize)arena->min_block_size, requested_size); if (min_block_size == 0) min_block_size = DQN_MEM_ARENA_DEFAULT_MIN_BLOCK_SIZE;
Dqn_usize mem_block_size = DQN_MAX(min_block_size, requested_size);
Dqn_usize const allocate_size = sizeof(*arena->curr_mem_block) + mem_block_size; Dqn_usize const allocate_size = sizeof(*arena->curr_mem_block) + mem_block_size;
auto *result = DQN_CAST(Dqn_MemBlock *)Dqn_Allocator_Allocate(&arena->allocator, allocate_size, alignof(Dqn_MemBlock)); auto *result = DQN_CAST(Dqn_MemBlock *)Dqn_Allocator_Allocate(&arena->allocator, allocate_size, alignof(Dqn_MemBlock));
if (!result) return result; if (!result) return result;
@ -1845,34 +1850,48 @@ DQN_HEADER_COPY_PROTOTYPE(void, Dqn_MemArena_ResetUsage(Dqn_MemArena *arena, Dqn
} }
} }
DQN_HEADER_COPY_PROTOTYPE(Dqn_MemArenaScopedRegion, Dqn_MemArena_MakeScopedRegion(Dqn_MemArena *arena)) Dqn_MemArenaRegion Dqn_MemArena_BeginRegion(Dqn_MemArena *arena)
{ {
return Dqn_MemArenaScopedRegion(arena); Dqn_MemArenaRegion result = {};
result.arena = arena;
result.curr_mem_block = arena->curr_mem_block;
result.curr_mem_block_used = (arena->curr_mem_block) ? arena->curr_mem_block->used : 0;
result.top_mem_block = arena->top_mem_block;
return result;
}
void Dqn_MemArena_EndRegion(Dqn_MemArenaRegion region)
{
while (region.top_mem_block != region.arena->top_mem_block)
{
Dqn_MemBlock *block_to_free = region.arena->top_mem_block;
if (region.arena->curr_mem_block == block_to_free)
region.arena->curr_mem_block = block_to_free->prev;
region.arena->top_mem_block = block_to_free->prev;
Dqn_MemArena__FreeBlock(region.arena, block_to_free);
}
for (Dqn_MemBlock *mem_block = region.arena->top_mem_block; mem_block != region.curr_mem_block; mem_block = mem_block->prev)
mem_block->used = 0;
if (region.arena->curr_mem_block)
region.arena->curr_mem_block->used = region.curr_mem_block_used;
region = {};
} }
Dqn_MemArenaScopedRegion::Dqn_MemArenaScopedRegion(Dqn_MemArena *arena) Dqn_MemArenaScopedRegion::Dqn_MemArenaScopedRegion(Dqn_MemArena *arena)
{ {
this->arena = arena; this->region = Dqn_MemArena_BeginRegion(arena);
this->curr_mem_block = arena->curr_mem_block;
this->curr_mem_block_used = (arena->curr_mem_block) ? arena->curr_mem_block->used : 0;
this->top_mem_block = arena->top_mem_block;
} }
Dqn_MemArenaScopedRegion::~Dqn_MemArenaScopedRegion() Dqn_MemArenaScopedRegion::~Dqn_MemArenaScopedRegion()
{ {
while (this->top_mem_block != this->arena->top_mem_block) Dqn_MemArena_EndRegion(this->region);
{
Dqn_MemBlock *block_to_free = this->arena->top_mem_block;
if (this->arena->curr_mem_block == block_to_free)
this->arena->curr_mem_block = block_to_free->prev;
this->arena->top_mem_block = block_to_free->prev;
Dqn_MemArena__FreeBlock(this->arena, block_to_free);
} }
for (Dqn_MemBlock *mem_block = this->arena->top_mem_block; mem_block != this->curr_mem_block; mem_block = mem_block->prev) Dqn_MemArenaScopedRegion Dqn_MemArena_MakeScopedRegion(Dqn_MemArena *arena)
mem_block->used = 0; {
return Dqn_MemArenaScopedRegion(arena);
this->arena->curr_mem_block->used = this->curr_mem_block_used;
} }
// @ ------------------------------------------------------------------------------------------------- // @ -------------------------------------------------------------------------------------------------

View File

@ -164,6 +164,7 @@ int main(int argc, char *argv[])
} }
Dqn_MemArena arena = {}; Dqn_MemArena arena = {};
arena.allocator = Dqn_Allocator_XHeap();
Dqn_Allocator allocator = Dqn_Allocator_Arena(&arena); Dqn_Allocator allocator = Dqn_Allocator_Arena(&arena);
Dqn_MemArena_Reserve(&arena, DQN_MEGABYTES(16)); Dqn_MemArena_Reserve(&arena, DQN_MEGABYTES(16));
for (isize arg_index = 1; arg_index < argc; ++arg_index) for (isize arg_index = 1; arg_index < argc; ++arg_index)

View File

@ -228,12 +228,12 @@ FILE_SCOPE void UnitTests()
// --------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------
{ {
TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Array"); TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Array");
// NOTE: Dqn_Array_InitWithMemory // NOTE: Dqn_Array_InitMemory
{ {
{ {
TEST_START_SCOPE(testing_state, "Fixed Memory: Test add single item and can't allocate more"); TEST_START_SCOPE(testing_state, "Fixed Memory: Test add single item and can't allocate more");
int memory[4] = {}; int memory[4] = {};
Dqn_Array<int> array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/); Dqn_Array<int> array = Dqn_Array_InitMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/);
Dqn_Array_Add(&array, 1); Dqn_Array_Add(&array, 1);
Dqn_Array_Add(&array, 2); Dqn_Array_Add(&array, 2);
Dqn_Array_Add(&array, 3); Dqn_Array_Add(&array, 3);
@ -254,7 +254,7 @@ FILE_SCOPE void UnitTests()
TEST_START_SCOPE(testing_state, "Fixed Memory: Test add array of items"); TEST_START_SCOPE(testing_state, "Fixed Memory: Test add array of items");
int memory[4] = {}; int memory[4] = {};
int DATA[] = {1, 2, 3}; int DATA[] = {1, 2, 3};
Dqn_Array<int> array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/); Dqn_Array<int> array = Dqn_Array_InitMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/);
Dqn_Array_Add(&array, DATA, Dqn_ArrayCount(DATA)); Dqn_Array_Add(&array, DATA, Dqn_ArrayCount(DATA));
TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]); TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]);
TEST_EXPECT_MSG(testing_state, array.data[1] == 2, "array.data %d", array.data[1]); TEST_EXPECT_MSG(testing_state, array.data[1] == 2, "array.data %d", array.data[1]);
@ -267,7 +267,7 @@ FILE_SCOPE void UnitTests()
TEST_START_SCOPE(testing_state, "Fixed Memory: Test clear and clear with memory zeroed"); TEST_START_SCOPE(testing_state, "Fixed Memory: Test clear and clear with memory zeroed");
int memory[4] = {}; int memory[4] = {};
int DATA[] = {1, 2, 3}; int DATA[] = {1, 2, 3};
Dqn_Array<int> array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/); Dqn_Array<int> array = Dqn_Array_InitMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/);
Dqn_Array_Add(&array, DATA, Dqn_ArrayCount(DATA)); Dqn_Array_Add(&array, DATA, Dqn_ArrayCount(DATA));
Dqn_Array_Clear(&array, false /*zero_mem*/); Dqn_Array_Clear(&array, false /*zero_mem*/);
TEST_EXPECT_MSG(testing_state, array.len == 0, "array.len: %d", array.len); TEST_EXPECT_MSG(testing_state, array.len == 0, "array.len: %d", array.len);
@ -282,7 +282,7 @@ FILE_SCOPE void UnitTests()
TEST_START_SCOPE(testing_state, "Fixed Memory: Test erase stable and erase unstable"); TEST_START_SCOPE(testing_state, "Fixed Memory: Test erase stable and erase unstable");
int memory[4] = {}; int memory[4] = {};
int DATA[] = {1, 2, 3, 4}; int DATA[] = {1, 2, 3, 4};
Dqn_Array<int> array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/); Dqn_Array<int> array = Dqn_Array_InitMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/);
Dqn_Array_Add(&array, DATA, Dqn_ArrayCount(DATA)); Dqn_Array_Add(&array, DATA, Dqn_ArrayCount(DATA));
Dqn_Array_EraseUnstable(&array, 1); Dqn_Array_EraseUnstable(&array, 1);
TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]); TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d", array.data[0]);
@ -300,7 +300,7 @@ FILE_SCOPE void UnitTests()
TEST_START_SCOPE(testing_state, "Fixed Memory: Test array pop and peek"); TEST_START_SCOPE(testing_state, "Fixed Memory: Test array pop and peek");
int memory[4] = {}; int memory[4] = {};
int DATA[] = {1, 2, 3}; int DATA[] = {1, 2, 3};
Dqn_Array<int> array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/); Dqn_Array<int> array = Dqn_Array_InitMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/);
Dqn_Array_Add(&array, DATA, Dqn_ArrayCount(DATA)); Dqn_Array_Add(&array, DATA, Dqn_ArrayCount(DATA));
Dqn_Array_Pop(&array, 2); Dqn_Array_Pop(&array, 2);
TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data: %d", array.data[0]); TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data: %d", array.data[0]);
@ -316,7 +316,7 @@ FILE_SCOPE void UnitTests()
{ {
TEST_START_SCOPE(testing_state, "Fixed Memory: Test free on fixed memory array does nothing"); TEST_START_SCOPE(testing_state, "Fixed Memory: Test free on fixed memory array does nothing");
int memory[4] = {}; int memory[4] = {};
Dqn_Array<int> array = Dqn_Array_InitWithMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/); Dqn_Array<int> array = Dqn_Array_InitMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/);
DQN_DEFER { Dqn_Array_Free(&array); }; DQN_DEFER { Dqn_Array_Free(&array); };
} }
} }