Add alignment to allocators

This commit is contained in:
doyle 2020-03-23 23:33:31 +11:00
parent f95836c5d5
commit 35ade5dfc1
4 changed files with 311 additions and 140 deletions

View File

@ -591,19 +591,21 @@ DQN_HEADER_COPY_PROTOTYPE(template <typename T> T *, Dqn_MemZero(T *src))
// @ NOTE: Dqn_Allocator // @ NOTE: Dqn_Allocator
// @ // @
// @ ------------------------------------------------------------------------------------------------- // @ -------------------------------------------------------------------------------------------------
// @ 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 // NOTE: The default allocator is the heap allocator
enum struct Dqn_Allocator_Type enum struct Dqn_Allocator_Type
{ {
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, Null,
}; };
#define DQN_ALLOCATOR_ALLOCATE_PROC(name) void *name(Dqn_usize size) #define DQN_ALLOCATOR_ALLOCATE_PROC(name) void *name(Dqn_isize size, Dqn_u8 alignment)
#define DQN_ALLOCATOR_REALLOC_PROC(name) void *name(void *old_ptr, Dqn_usize old_size, Dqn_usize new_size) #define DQN_ALLOCATOR_REALLOC_PROC(name) void *name(void *old_ptr, Dqn_isize old_size, Dqn_isize new_size)
#define DQN_ALLOCATOR_FREE_PROC(name) void name(void *ptr) #define DQN_ALLOCATOR_FREE_PROC(name) void name(void *ptr)
typedef DQN_ALLOCATOR_ALLOCATE_PROC(Dqn_Allocator_AllocateProc); typedef DQN_ALLOCATOR_ALLOCATE_PROC(Dqn_Allocator_AllocateProc);
typedef DQN_ALLOCATOR_REALLOC_PROC(Dqn_Allocator_ReallocProc); typedef DQN_ALLOCATOR_REALLOC_PROC(Dqn_Allocator_ReallocProc);
@ -611,7 +613,11 @@ typedef DQN_ALLOCATOR_FREE_PROC(Dqn_Allocator_FreeProc);
struct Dqn_Allocator struct Dqn_Allocator
{ {
Dqn_Allocator_Type type; Dqn_Allocator_Type type;
void *user_context; union
{
void *user;
struct Dqn_MemArena *arena;
} context;
isize bytes_allocated; isize bytes_allocated;
isize total_bytes_allocated; isize total_bytes_allocated;
@ -627,6 +633,20 @@ struct Dqn_Allocator
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 // @ NOTE: Dqn_MemArena
@ -665,21 +685,14 @@ struct Dqn_MemArenaScopedRegion
Dqn_MemBlock *top_mem_block; Dqn_MemBlock *top_mem_block;
}; };
#if defined(DQN_DEBUG_DQN_MEM_ARENA_LOGGING) void * Dqn_MemArena_Allocate(Dqn_MemArena *arena, Dqn_usize size, Dqn_u8 alignment);
#define DQN_DEBUG_ARGS , char const *file, Dqn_isize file_len, char const *func, Dqn_isize func_len, Dqn_isize line Dqn_b32 Dqn_MemArena_Reserve(Dqn_MemArena *arena, Dqn_usize size);
#define DQN_DEBUG_PARAMS , DQN_STR_AND_LEN(__FILE__), DQN_STR_AND_LEN(__func__), __LINE__ DQN_HEADER_COPY_PROTOTYPE(template <typename T> T *, Dqn_MemArena_AllocateType(Dqn_MemArena *arena, Dqn_isize num))
#else {
#define DQN_DEBUG_ARGS auto *result = DQN_CAST(T *)Dqn_MemArena_Allocate(arena, sizeof(T) * num, alignof(T));
#define DQN_DEBUG_PARAMS return result;
#endif }
#define DQN_MEM_ARENA_INIT_WITH_ALLOCATOR(allocator, size) Dqn_MemArena_InitWithAllocator(allocator, size DQN_DEBUG_PARAMS)
#define DQN_MEM_ARENA_INIT_MEMORY(src, size) Dqn_MemArena_InitMemory(src, size DQN_DEBUG_PARAMS)
#define DQN_MEM_ARENA_ALLOC(arena, size) Dqn_MemArena_Alloc(arena, size DQN_DEBUG_PARAMS)
#define DQN_MEM_ARENA_ALLOC_ARRAY(arena, T, num) (T *)Dqn_MemArena_Alloc(arena, sizeof(T) * num DQN_DEBUG_PARAMS)
#define DQN_MEM_ARENA_ALLOC_STRUCT(arena, T) (T *)Dqn_MemArena_Alloc(arena, sizeof(T) DQN_DEBUG_PARAMS)
#define DQN_MEM_ARENA_RESERVE(arena, size) Dqn_MemArena_Reserve(arena, size DQN_DEBUG_PARAMS)
#define DQN_MEM_ARENA_FREE(arena) Dqn_MemArena_Free(arena)
DQN_HEADER_COPY_END DQN_HEADER_COPY_END
// @ ------------------------------------------------------------------------------------------------- // @ -------------------------------------------------------------------------------------------------
@ -712,10 +725,18 @@ DQN_HEADER_COPY_PROTOTYPE(Dqn_Allocator, inline Dqn_Allocator_Arena(Dqn_MemArena
{ {
Dqn_Allocator result = {}; Dqn_Allocator result = {};
result.type = Dqn_Allocator_Type::Arena; result.type = Dqn_Allocator_Type::Arena;
result.user_context = arena; result.context.arena = arena;
return result; return result;
} }
void *Dqn_Allocator_Allocate(Dqn_Allocator *allocator, Dqn_isize size, Dqn_u8 alignment);
DQN_HEADER_COPY_PROTOTYPE(template <typename T> T *, Dqn_Allocator_AllocateType(Dqn_Allocator *allocator, Dqn_isize num))
{
auto *result = DQN_CAST(T *)Dqn_Allocator_Allocate(allocator, sizeof(T) * num, alignof(T));
return result;
}
// @ ------------------------------------------------------------------------------------------------- // @ -------------------------------------------------------------------------------------------------
// @ // @
// @ NOTE: String // @ NOTE: String
@ -785,7 +806,7 @@ template <Dqn_usize N> DQN_FILE_SCOPE char *Dqn_StringBuilder__GetWriteBufferAnd
{ {
// NOTE: Need to allocate new buf // NOTE: Need to allocate new buf
Dqn_usize allocation_size = sizeof(*builder->last_mem_buf) + DQN_MAX(size_required, DQN_STRING_BUILDER_MIN_MEM_BUF_ALLOC_SIZE); Dqn_usize allocation_size = sizeof(*builder->last_mem_buf) + DQN_MAX(size_required, DQN_STRING_BUILDER_MIN_MEM_BUF_ALLOC_SIZE);
void *memory = Dqn_Allocator_Allocate(&builder->allocator, allocation_size); void *memory = Dqn_Allocator_Allocate(&builder->allocator, allocation_size, alignof(Dqn_StringBuilderBuffer));
if (!memory) return nullptr; if (!memory) return nullptr;
auto *new_buf = DQN_CAST(Dqn_StringBuilderBuffer *)memory; auto *new_buf = DQN_CAST(Dqn_StringBuilderBuffer *)memory;
*new_buf = {}; *new_buf = {};
@ -863,14 +884,10 @@ DQN_HEADER_COPY_PROTOTYPE(template <Dqn_usize N> void, Dqn_StringBuilder_BuildIn
Dqn_StringBuilder__BuildOutput(builder, dest, dest_size); Dqn_StringBuilder__BuildOutput(builder, dest, dest_size);
} }
void *Dqn_Allocator_Allocate(Dqn_Allocator *allocator, Dqn_usize size);
void * Dqn_MemArena_Alloc(Dqn_MemArena *arena, Dqn_usize size DQN_DEBUG_ARGS);
Dqn_b32 Dqn_MemArena_Reserve(Dqn_MemArena *arena, Dqn_usize size DQN_DEBUG_ARGS);
DQN_HEADER_COPY_PROTOTYPE(template <Dqn_usize N> char *, Dqn_StringBuilder_Build(Dqn_StringBuilder<N> *builder, Dqn_Allocator *allocator, Dqn_isize *len = nullptr)) DQN_HEADER_COPY_PROTOTYPE(template <Dqn_usize N> char *, Dqn_StringBuilder_Build(Dqn_StringBuilder<N> *builder, Dqn_Allocator *allocator, Dqn_isize *len = nullptr))
{ {
Dqn_isize len_w_null_terminator = Dqn_StringBuilder_BuildLen(builder); Dqn_isize len_w_null_terminator = Dqn_StringBuilder_BuildLen(builder);
auto *result = DQN_CAST(char *)Dqn_Allocator_Allocate(allocator, sizeof(char) * len_w_null_terminator); auto *result = DQN_CAST(char *)Dqn_Allocator_Allocate(allocator, len_w_null_terminator, alignof(char));
if (len) *len = (len_w_null_terminator - 1); if (len) *len = (len_w_null_terminator - 1);
Dqn_StringBuilder__BuildOutput(builder, result, len_w_null_terminator); Dqn_StringBuilder__BuildOutput(builder, result, len_w_null_terminator);
return result; return result;
@ -968,34 +985,34 @@ struct Dqn_Slice
T *operator+ (Dqn_isize i) { DQN_ASSERT_MSG(i >= 0 && i < len, "%d >= 0 && %d < %d", i, len); return data + i; } T *operator+ (Dqn_isize i) { DQN_ASSERT_MSG(i >= 0 && i < len, "%d >= 0 && %d < %d", i, len); return data + i; }
}; };
DQN_HEADER_COPY_PROTOTYPE(template <typename T> inline Dqn_Slice<T>, Dqn_Slice_CopyNullTerminated(Dqn_MemArena *arena, T const *src, Dqn_isize len)) DQN_HEADER_COPY_PROTOTYPE(template <typename T> inline Dqn_Slice<T>, Dqn_Slice_CopyNullTerminated(Dqn_Allocator *allocator, T const *src, Dqn_isize len))
{ {
Dqn_Slice<T> result = {}; Dqn_Slice<T> result = {};
result.len = len; result.len = len;
result.buf = DQN_MEM_ARENA_ALLOC_ARRAY(arena, T, len + 1); result.buf = DQN_CAST(T *)Dqn_Allocator_Allocate(arena, (sizeof(T) * len) + 1, alignof(T));
memcpy(result.buf, src, len * sizeof(T)); memcpy(result.buf, src, len * sizeof(T));
result.buf[len] = 0; result.buf[len] = 0;
return result; return result;
} }
DQN_HEADER_COPY_PROTOTYPE(template <typename T> inline Dqn_Slice<T>, Dqn_Slice_CopyNullTerminated(Dqn_MemArena *arena, Dqn_Slice<T> const src)) DQN_HEADER_COPY_PROTOTYPE(template <typename T> inline Dqn_Slice<T>, Dqn_Slice_CopyNullTerminated(Dqn_Allocator *allocator, Dqn_Slice<T> const src))
{ {
Dqn_Slice<T> result = Dqn_Slice_CopyNullTerminated(arena, src.buf, src.len); Dqn_Slice<T> result = Dqn_Slice_CopyNullTerminated(arena, src.buf, src.len);
return result; return result;
} }
DQN_HEADER_COPY_PROTOTYPE(template <typename T> inline Dqn_Slice<T>, Dqn_Slice_Copy(Dqn_MemArena *arena, T const *src, Dqn_isize len)) DQN_HEADER_COPY_PROTOTYPE(template <typename T> inline Dqn_Slice<T>, Dqn_Slice_Copy(Dqn_Allocator *allocator, T const *src, Dqn_isize len))
{ {
Dqn_Slice<T> result = {}; Dqn_Slice<T> result = {};
result.len = len; result.len = len;
result.buf = DQN_MEM_ARENA_ALLOC_ARRAY(arena, T, len); result.buf = DQN_CAST(T *)Dqn_Allocator_Allocate(arena, sizeof(T) * len, alignof(T));
memcpy(result.buf, src, len * sizeof(T)); memcpy(result.buf, src, len * sizeof(T));
return result; return result;
} }
DQN_HEADER_COPY_PROTOTYPE(template <typename T> inline Dqn_Slice<T>, Dqn_Slice_Copy(Dqn_MemArena *arena, Dqn_Slice<T> const src)) DQN_HEADER_COPY_PROTOTYPE(template <typename T> inline Dqn_Slice<T>, Dqn_Slice_Copy(Dqn_Allocator *allocator, Dqn_Slice<T> const src))
{ {
Dqn_Slice<T> result = Dqn_Slice_Copy(arena, src.buf, src.len); Dqn_Slice<T> result = Dqn_Slice_Copy(allocator, src.buf, src.len);
return result; return result;
} }
@ -1153,7 +1170,6 @@ DQN_HEADER_COPY_PROTOTYPE(T *, Dqn_FixedArray_Find(DQN_FIXED_ARRAY_TEMPLATE_DECL
// @ // @
// @ ------------------------------------------------------------------------------------------------- // @ -------------------------------------------------------------------------------------------------
DQN_HEADER_COPY_BEGIN DQN_HEADER_COPY_BEGIN
// TODO(doyle): Make this either initialised from memory or dynamically allocating
template <typename T> struct Dqn_Array template <typename T> struct Dqn_Array
{ {
Dqn_Allocator allocator; Dqn_Allocator allocator;
@ -1187,7 +1203,7 @@ DQN_HEADER_COPY_PROTOTYPE(template <typename T> bool, Dqn_Array_Reserve(Dqn_Arra
if (size <= a->len) return true; if (size <= a->len) return true;
T *new_ptr = nullptr; T *new_ptr = nullptr;
if (a->data) new_ptr = DQN_CAST(T *)Dqn_Allocator_Realloc (&a->allocator, a->data, sizeof(T) * a->max, sizeof(T) * size); if (a->data) new_ptr = DQN_CAST(T *)Dqn_Allocator_Realloc (&a->allocator, a->data, sizeof(T) * a->max, sizeof(T) * size);
else new_ptr = DQN_CAST(T *)Dqn_Allocator_Allocate(&a->allocator, sizeof(T) * size); else new_ptr = DQN_CAST(T *)Dqn_Allocator_Allocate(&a->allocator, sizeof(T) * size, alignof(T));
if (!new_ptr) return false; if (!new_ptr) return false;
a->data = new_ptr; a->data = new_ptr;
a->max = size; a->max = size;
@ -1378,6 +1394,7 @@ DQN_HEADER_COPY_END
#ifdef DQN_IMPLEMENTATION #ifdef DQN_IMPLEMENTATION
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
#define STB_SPRINTF_IMPLEMENTATION #define STB_SPRINTF_IMPLEMENTATION
#include <inttypes.h>
#include <limits.h> #include <limits.h>
#include <math.h> #include <math.h>
#include <memory.h> #include <memory.h>
@ -1457,9 +1474,73 @@ DQN_HEADER_COPY_PROTOTYPE(Dqn_b32, Dqn_Log(Dqn_LogType type, char const *file, D
return true; return true;
} }
DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Allocate(Dqn_Allocator *allocator, Dqn_usize size)) // @ -------------------------------------------------------------------------------------------------
// @
// @ NOTE: Dqn_AllocateMetadata
// @
// @ -------------------------------------------------------------------------------------------------
DQN_HEADER_COPY_PROTOTYPE(char *, Dqn_AllocateMetadata_Init(void *ptr, Dqn_u8 alignment))
{ {
void *result = nullptr; // NOTE: Given a pointer, it can misaligned by up to (Alignment - 1) bytes.
// After calculating the offset to apply on the aligned ptr, we store the
// allocation metadata right before the ptr, so that we just walk back
// sizeof(metadata) bytes to get to the metadata from the pointer.
// [Metadata Is Stored Here]
// [Raw Pointer] -> [Metadata Storage] [Unaligned Pointer] [Offset to Align Pointer] [Aligned Pointer]
// NOTE: In the scenario where the pointer is already aligned after the
// [Metadata Storage] bytes, the allocation looks like this.
// [Metadata Is Stored Here]
// [Raw Pointer] -> [Metadata Storage] [Aligned Pointer]
// Offset is [0->Alignment-1] bytes from the Unaligned ptr.
auto *raw_ptr = DQN_CAST(char *) ptr;
auto *unaligned_ptr = DQN_CAST(char *) raw_ptr + sizeof(Dqn_AllocateMetadata);
Dqn_u8 unaligned_to_aligned_offset = alignment - (DQN_CAST(uintptr_t) unaligned_ptr % alignment);
char *result = unaligned_ptr + unaligned_to_aligned_offset;
ptrdiff_t difference = result - raw_ptr;
DQN_ASSERT(difference <= DQN_CAST(Dqn_u8) - 1);
auto *metadata_ptr = DQN_CAST(Dqn_AllocateMetadata *)(result - sizeof(Dqn_AllocateMetadata));
metadata_ptr->alignment = alignment;
metadata_ptr->offset = DQN_CAST(Dqn_u8)difference;
return result;
}
DQN_HEADER_COPY_PROTOTYPE(Dqn_AllocateMetadata, Dqn_AllocateMetadata_Get(void *ptr))
{
auto *aligned_ptr = DQN_CAST(char *) ptr;
auto result = *DQN_CAST(Dqn_AllocateMetadata *)(aligned_ptr - sizeof(Dqn_AllocateMetadata));
return result;
}
DQN_HEADER_COPY_PROTOTYPE(char *, Dqn_AllocateMetadata_GetRawPointer(void *ptr))
{
Dqn_AllocateMetadata metadata = Dqn_AllocateMetadata_Get(ptr);
char *result = DQN_CAST(char *)ptr - metadata.offset;
return result;
}
DQN_HEADER_COPY_PROTOTYPE(Dqn_isize, Dqn_AllocateMetadata_SizeRequired(Dqn_isize size, Dqn_u8 alignment))
{
DQN_ASSERT(alignment > 0);
if (alignment <= 0) alignment = 1;
Dqn_isize result = size + (alignment - 1) + sizeof(Dqn_AllocateMetadata);
return result;
}
// @ -------------------------------------------------------------------------------------------------
// @
// @ NOTE: Dqn_Allocator
// @
// @ -------------------------------------------------------------------------------------------------
DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Allocate(Dqn_Allocator *allocator, Dqn_isize size, Dqn_u8 alignment))
{
Dqn_isize allocation_size = Dqn_AllocateMetadata_SizeRequired(size, alignment);
char *result = nullptr;
switch (allocator->type) switch (allocator->type)
{ {
case Dqn_Allocator_Type::Null: case Dqn_Allocator_Type::Null:
@ -1468,7 +1549,8 @@ DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Allocate(Dqn_Allocator *allocato
case Dqn_Allocator_Type::Heap: case Dqn_Allocator_Type::Heap:
case Dqn_Allocator_Type::XHeap: case Dqn_Allocator_Type::XHeap:
{ {
result = calloc(1, size); void *ptr = calloc(1, DQN_CAST(size_t)allocation_size);
result = Dqn_AllocateMetadata_Init(ptr, alignment);
if (!result && allocator->type == Dqn_Allocator_Type::XHeap) if (!result && allocator->type == Dqn_Allocator_Type::XHeap)
{ {
DQN_ASSERT(result); DQN_ASSERT(result);
@ -1478,15 +1560,14 @@ DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Allocate(Dqn_Allocator *allocato
case Dqn_Allocator_Type::Arena: case Dqn_Allocator_Type::Arena:
{ {
auto *arena = static_cast<Dqn_MemArena *>(allocator->user_context); result = DQN_CAST(char *) Dqn_MemArena_Allocate(allocator->context.arena, size, alignment);
result = DQN_MEM_ARENA_ALLOC(arena, size);
} }
break; break;
case Dqn_Allocator_Type::Custom: case Dqn_Allocator_Type::Custom:
{ {
if (allocator->allocate) if (allocator->allocate)
result = allocator->allocate(size); result = DQN_CAST(char *)allocator->allocate(size, alignment);
} }
break; break;
} }
@ -1494,7 +1575,7 @@ DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Allocate(Dqn_Allocator *allocato
if (result) if (result)
{ {
allocator->allocations++; allocator->allocations++;
allocator->total_bytes_allocated += size; allocator->total_bytes_allocated += allocation_size;
} }
return result; return result;
} }
@ -1505,7 +1586,9 @@ DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Realloc(Dqn_Allocator *allocator
DQN_IF_ASSERT(new_size >= 0) new_size = 0; DQN_IF_ASSERT(new_size >= 0) new_size = 0;
DQN_ASSERT(new_size > old_size); DQN_ASSERT(new_size > old_size);
void *result = nullptr; auto metadata = Dqn_AllocateMetadata_Get(old_ptr);
Dqn_isize allocation_size = Dqn_AllocateMetadata_SizeRequired(new_size, metadata.alignment);
void *result = nullptr;
switch (allocator->type) switch (allocator->type)
{ {
case Dqn_Allocator_Type::Null: case Dqn_Allocator_Type::Null:
@ -1514,22 +1597,24 @@ DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Realloc(Dqn_Allocator *allocator
case Dqn_Allocator_Type::Heap: case Dqn_Allocator_Type::Heap:
case Dqn_Allocator_Type::XHeap: case Dqn_Allocator_Type::XHeap:
{ {
result = realloc(old_ptr, DQN_CAST(size_t)new_size); char *original_ptr = Dqn_AllocateMetadata_GetRawPointer(old_ptr);
if (!result && allocator->type == Dqn_Allocator_Type::XHeap) void *new_ptr = realloc(original_ptr, DQN_CAST(size_t) allocation_size);
if (new_ptr)
{ {
DQN_ASSERT(result); result = Dqn_AllocateMetadata_Init(new_ptr, metadata.alignment);
}
else if (allocator->type == Dqn_Allocator_Type::XHeap)
{
DQN_ASSERT(DQN_INVALID_CODE_PATH);
} }
} }
break; break;
case Dqn_Allocator_Type::Arena: case Dqn_Allocator_Type::Arena:
{ {
auto *arena = static_cast<Dqn_MemArena *>(allocator->user_context); Dqn_MemArena *arena = allocator->context.arena;
if (DQN_MEM_ARENA_RESERVE(arena, DQN_CAST(size_t)new_size)) result = Dqn_MemArena_Allocate(arena, DQN_CAST(size_t)new_size, metadata.alignment);
{ if (result) memcpy(result, old_ptr, DQN_CAST(size_t) old_size);
result = DQN_MEM_ARENA_ALLOC(arena, DQN_CAST(size_t)new_size);
if (result) memcpy(result, old_ptr, DQN_CAST(size_t)old_size);
}
} }
break; break;
@ -1543,7 +1628,7 @@ DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Realloc(Dqn_Allocator *allocator
if (result) if (result)
{ {
allocator->total_bytes_allocated += new_size; allocator->total_bytes_allocated += allocation_size;
allocator->total_allocations++; allocator->total_allocations++;
} }
return result; return result;
@ -1559,7 +1644,8 @@ DQN_HEADER_COPY_PROTOTYPE(void, Dqn_Allocator_Free(Dqn_Allocator *allocator, voi
case Dqn_Allocator_Type::Heap: case Dqn_Allocator_Type::Heap:
case Dqn_Allocator_Type::XHeap: case Dqn_Allocator_Type::XHeap:
{ {
free(ptr); char *raw_ptr = Dqn_AllocateMetadata_GetRawPointer(ptr);
free(raw_ptr);
} }
break; break;
@ -1591,7 +1677,7 @@ DQN_FILE_SCOPE Dqn_MemBlock *Dqn_MemArena__AllocateBlock(Dqn_MemArena *arena, Dq
DQN_ASSERT(arena->min_block_size > 0); DQN_ASSERT(arena->min_block_size > 0);
Dqn_usize mem_block_size = DQN_MAX(DQN_CAST(Dqn_usize)arena->min_block_size, requested_size); Dqn_usize mem_block_size = DQN_MAX(DQN_CAST(Dqn_usize)arena->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;
Dqn_MemBlock *result = DQN_CAST(Dqn_MemBlock *) Dqn_Allocator_Allocate(&arena->allocator, allocate_size); auto *result = DQN_CAST(Dqn_MemBlock *)Dqn_Allocator_Allocate(&arena->allocator, allocate_size, alignof(Dqn_MemBlock));
if (!result) return result; if (!result) return result;
*result = {}; *result = {};
@ -1631,15 +1717,13 @@ DQN_FILE_SCOPE void Dqn_MemArena__AttachBlock(Dqn_MemArena *arena, Dqn_MemBlock
} }
} }
DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_MemArena_Alloc(Dqn_MemArena *arena, Dqn_usize size DQN_DEBUG_ARGS)) DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_MemArena_Allocate(Dqn_MemArena *arena, Dqn_usize size, u8 alignment))
{ {
#if defined(DQN_DEBUG_DQN_MEM_ARENA_LOGGING) Dqn_usize allocation_size = Dqn_AllocateMetadata_SizeRequired(size, alignment);
(void)file; (void)file_len; (void)func; (void)func_len; (void)line;
#endif
Dqn_b32 need_new_mem_block = true; Dqn_b32 need_new_mem_block = true;
for (Dqn_MemBlock *mem_block = arena->curr_mem_block; mem_block; mem_block = mem_block->next) for (Dqn_MemBlock *mem_block = arena->curr_mem_block; mem_block; mem_block = mem_block->next)
{ {
Dqn_b32 can_fit_in_block = (mem_block->used + size) <= mem_block->size; Dqn_b32 can_fit_in_block = (mem_block->used + allocation_size) <= mem_block->size;
if (can_fit_in_block) if (can_fit_in_block)
{ {
arena->curr_mem_block = mem_block; arena->curr_mem_block = mem_block;
@ -1650,23 +1734,22 @@ DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_MemArena_Alloc(Dqn_MemArena *arena, Dqn_us
if (need_new_mem_block) if (need_new_mem_block)
{ {
Dqn_MemBlock *new_block = Dqn_MemArena__AllocateBlock(arena, size); Dqn_MemBlock *new_block = Dqn_MemArena__AllocateBlock(arena, allocation_size);
if (!new_block) return nullptr; if (!new_block) return nullptr;
Dqn_MemArena__AttachBlock(arena, new_block); Dqn_MemArena__AttachBlock(arena, new_block);
arena->curr_mem_block = arena->top_mem_block; arena->curr_mem_block = arena->top_mem_block;
} }
void *result = static_cast<Dqn_u8 *>(arena->curr_mem_block->memory) + arena->curr_mem_block->used; char *ptr = DQN_CAST(char *) arena->curr_mem_block->memory + arena->curr_mem_block->used;
arena->curr_mem_block->used += size; char *result = Dqn_AllocateMetadata_Init(ptr, alignment);
arena->curr_mem_block->used += allocation_size;
DQN_ASSERT(arena->curr_mem_block->used <= arena->curr_mem_block->size); DQN_ASSERT(arena->curr_mem_block->used <= arena->curr_mem_block->size);
return result; return result;
} }
DQN_HEADER_COPY_PROTOTYPE(void, Dqn_MemArena_Free(Dqn_MemArena *arena DQN_DEBUG_ARGS)) DQN_HEADER_COPY_PROTOTYPE(void, Dqn_MemArena_Free(Dqn_MemArena *arena))
{ {
#if defined(DQN_DEBUG_DQN_MEM_ARENA_LOGGING)
(void)file; (void)file_len; (void)func; (void)func_len; (void)line;
#endif
for (Dqn_MemBlock *mem_block = arena->top_mem_block; mem_block;) for (Dqn_MemBlock *mem_block = arena->top_mem_block; mem_block;)
{ {
Dqn_MemBlock *block_to_free = mem_block; Dqn_MemBlock *block_to_free = mem_block;
@ -1681,11 +1764,8 @@ DQN_HEADER_COPY_PROTOTYPE(void, Dqn_MemArena_Free(Dqn_MemArena *arena DQN_DEBUG_
arena->allocator = allocator; arena->allocator = allocator;
} }
DQN_HEADER_COPY_PROTOTYPE(Dqn_b32, Dqn_MemArena_Reserve(Dqn_MemArena *arena, Dqn_usize size DQN_DEBUG_ARGS)) DQN_HEADER_COPY_PROTOTYPE(Dqn_b32, Dqn_MemArena_Reserve(Dqn_MemArena *arena, Dqn_usize size))
{ {
#if defined(DQN_DEBUG_DQN_MEM_ARENA_LOGGING)
(void)file; (void)file_len; (void)func; (void)func_len; (void)line;
#endif
if (arena->top_mem_block) if (arena->top_mem_block)
{ {
Dqn_usize remaining_space = arena->top_mem_block->size - arena->top_mem_block->used; Dqn_usize remaining_space = arena->top_mem_block->size - arena->top_mem_block->used;
@ -1698,11 +1778,8 @@ DQN_HEADER_COPY_PROTOTYPE(Dqn_b32, Dqn_MemArena_Reserve(Dqn_MemArena *arena, Dqn
return true; return true;
} }
DQN_HEADER_COPY_PROTOTYPE(Dqn_MemArena, Dqn_MemArena_InitWithAllocator(Dqn_Allocator allocator, Dqn_usize size DQN_DEBUG_ARGS)) DQN_HEADER_COPY_PROTOTYPE(Dqn_MemArena, Dqn_MemArena_InitWithAllocator(Dqn_Allocator allocator, Dqn_usize size))
{ {
#if defined(DQN_DEBUG_DQN_MEM_ARENA_LOGGING)
(void)file; (void)file_len; (void)func; (void)func_len; (void)line;
#endif
Dqn_MemArena result = {}; Dqn_MemArena result = {};
DQN_ASSERT_MSG(size >= sizeof(*result.curr_mem_block), "(%zu >= %zu) There needs to be enough space to encode the Dqn_MemBlock struct into the memory buffer", size, sizeof(*result.curr_mem_block)); DQN_ASSERT_MSG(size >= sizeof(*result.curr_mem_block), "(%zu >= %zu) There needs to be enough space to encode the Dqn_MemBlock struct into the memory buffer", size, sizeof(*result.curr_mem_block));
result.allocator = allocator; result.allocator = allocator;
@ -1711,11 +1788,8 @@ DQN_HEADER_COPY_PROTOTYPE(Dqn_MemArena, Dqn_MemArena_InitWithAllocator(Dqn_Alloc
return result; return result;
} }
DQN_HEADER_COPY_PROTOTYPE(Dqn_MemArena, Dqn_MemArena_InitMemory(void *memory, Dqn_usize size DQN_DEBUG_ARGS)) DQN_HEADER_COPY_PROTOTYPE(Dqn_MemArena, Dqn_MemArena_InitMemory(void *memory, Dqn_usize size))
{ {
#if defined(DQN_DEBUG_DQN_MEM_ARENA_LOGGING)
(void)file; (void)file_len; (void)func; (void)func_len; (void)line;
#endif
Dqn_MemArena result = {}; Dqn_MemArena result = {};
DQN_ASSERT_MSG(size >= sizeof(*result.curr_mem_block), "(%zu >= %zu) There needs to be enough space to encode the Dqn_MemBlock struct into the memory buffer", size, sizeof(*result.curr_mem_block)); DQN_ASSERT_MSG(size >= sizeof(*result.curr_mem_block), "(%zu >= %zu) There needs to be enough space to encode the Dqn_MemBlock struct into the memory buffer", size, sizeof(*result.curr_mem_block));
result.allocator = Dqn_Allocator_Null(); result.allocator = Dqn_Allocator_Null();
@ -1778,7 +1852,7 @@ DQN_HEADER_COPY_PROTOTYPE(Dqn_String, Dqn_Asprintf(Dqn_Allocator *allocator, cha
{ {
Dqn_String result = {}; Dqn_String result = {};
result.len = stbsp_vsnprintf(nullptr, 0, fmt, va) + 1; result.len = stbsp_vsnprintf(nullptr, 0, fmt, va) + 1;
result.str = DQN_CAST(char *)Dqn_Allocator_Allocate(allocator, sizeof(char) * result.len); result.str = DQN_CAST(char *)Dqn_Allocator_Allocate(allocator, result.len, alignof(char));
stbsp_vsnprintf(result.str, Dqn_Safe_TruncateISizeToInt(result.len), fmt, va); stbsp_vsnprintf(result.str, Dqn_Safe_TruncateISizeToInt(result.len), fmt, va);
result.str[result.len - 1] = 0; result.str[result.len - 1] = 0;
return result; return result;
@ -2460,7 +2534,7 @@ DQN_HEADER_COPY_PROTOTYPE(Dqn_b32, Dqn_String_CompareCaseInsensitive(Dqn_String
DQN_HEADER_COPY_PROTOTYPE(Dqn_String, Dqn_String_Copy(Dqn_Allocator *allocator, Dqn_String const src)) DQN_HEADER_COPY_PROTOTYPE(Dqn_String, Dqn_String_Copy(Dqn_Allocator *allocator, Dqn_String const src))
{ {
Dqn_String result = src; Dqn_String result = src;
result.str = DQN_CAST(decltype(result.str))Dqn_Allocator_Allocate(allocator, sizeof(*result.str) * result.len); result.str = DQN_CAST(char *)Dqn_Allocator_Allocate(allocator, result.len, alignof(char));
memcpy(result.str, src.str, result.len); memcpy(result.str, src.str, result.len);
return result; return result;
} }
@ -2485,7 +2559,7 @@ DQN_HEADER_COPY_PROTOTYPE(char *, Dqn_File_ReadAll(Dqn_Allocator *allocator, cha
rewind(file_handle); rewind(file_handle);
auto *result = DQN_CAST(char *)Dqn_Allocator_Allocate(allocator, DQN_CAST(Dqn_usize)(file_size_ + 1)); auto *result = DQN_CAST(char *)Dqn_Allocator_Allocate(allocator, file_size_ + 1, alignof(char));
DQN_ASSERT(result); DQN_ASSERT(result);
result[file_size_] = 0; result[file_size_] = 0;

View File

@ -165,7 +165,7 @@ int main(int argc, char *argv[])
Dqn_MemArena arena = {}; Dqn_MemArena arena = {};
Dqn_Allocator allocator = Dqn_Allocator_Arena(&arena); Dqn_Allocator allocator = Dqn_Allocator_Arena(&arena);
DQN_MEM_ARENA_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)
{ {
char const *file = argv[arg_index]; char const *file = argv[arg_index];
@ -205,7 +205,7 @@ int main(int argc, char *argv[])
} }
} }
HeaderEntry *header_entries = DQN_MEM_ARENA_ALLOC_ARRAY(&arena, HeaderEntry, num_header_entries); auto *header_entries = Dqn_MemArena_AllocateType<HeaderEntry>(&arena, num_header_entries);
isize header_entries_index = 0; isize header_entries_index = 0;
isize max_prototype_return_val = 0; isize max_prototype_return_val = 0;
@ -244,7 +244,7 @@ int main(int argc, char *argv[])
ptr++; ptr++;
isize comment_len = ptr - comment_start; isize comment_len = ptr - comment_start;
entry->comment.str = DQN_MEM_ARENA_ALLOC_ARRAY(&arena, char, comment_len); entry->comment.str = Dqn_MemArena_AllocateType<char>(&arena, comment_len);
DQN_FOR_EACH(comment_index, comment_len) DQN_FOR_EACH(comment_index, comment_len)
{ {
// NOTE: We capture "// @", and we want to skip the @ symbol, its ugly which is at the index 3 // NOTE: We capture "// @", and we want to skip the @ symbol, its ugly which is at the index 3
@ -272,7 +272,7 @@ int main(int argc, char *argv[])
} }
isize copy_len = copy_end - copy_start; isize copy_len = copy_end - copy_start;
entry->copy_range.str = (char *)DQN_MEM_ARENA_ALLOC(&arena, copy_len); entry->copy_range.str = Dqn_MemArena_AllocateType<char>(&arena, copy_len);
DQN_FOR_EACH(copy_index, copy_len) DQN_FOR_EACH(copy_index, copy_len)
{ {
char ch = copy_start[copy_index]; char ch = copy_start[copy_index];

View File

@ -201,18 +201,19 @@ template <typename T> T * Dqn_MemZero(T *src);
// NOTE: Dqn_Allocator // NOTE: Dqn_Allocator
// //
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
// Custom allocations must include Dqn_AllocateMetadata before the aligned_ptr, see Dqn_AllocateMetadata for more information
// NOTE: The default allocator is the heap allocator // NOTE: The default allocator is the heap allocator
enum struct Dqn_Allocator_Type enum struct Dqn_Allocator_Type
{ {
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, Null,
}; };
#define DQN_ALLOCATOR_ALLOCATE_PROC(name) void *name(Dqn_usize size) #define DQN_ALLOCATOR_ALLOCATE_PROC(name) void *name(Dqn_isize size, Dqn_u8 alignment)
#define DQN_ALLOCATOR_REALLOC_PROC(name) void *name(void *old_ptr, Dqn_usize old_size, Dqn_usize new_size) #define DQN_ALLOCATOR_REALLOC_PROC(name) void *name(void *old_ptr, Dqn_isize old_size, Dqn_isize new_size)
#define DQN_ALLOCATOR_FREE_PROC(name) void name(void *ptr) #define DQN_ALLOCATOR_FREE_PROC(name) void name(void *ptr)
typedef DQN_ALLOCATOR_ALLOCATE_PROC(Dqn_Allocator_AllocateProc); typedef DQN_ALLOCATOR_ALLOCATE_PROC(Dqn_Allocator_AllocateProc);
typedef DQN_ALLOCATOR_REALLOC_PROC(Dqn_Allocator_ReallocProc); typedef DQN_ALLOCATOR_REALLOC_PROC(Dqn_Allocator_ReallocProc);
@ -220,7 +221,11 @@ typedef DQN_ALLOCATOR_FREE_PROC(Dqn_Allocator_FreeProc);
struct Dqn_Allocator struct Dqn_Allocator
{ {
Dqn_Allocator_Type type; Dqn_Allocator_Type type;
void *user_context; union
{
void *user;
struct Dqn_MemArena *arena;
} context;
isize bytes_allocated; isize bytes_allocated;
isize total_bytes_allocated; isize total_bytes_allocated;
@ -235,6 +240,18 @@ struct Dqn_Allocator
}; };
// -------------------------------------------------------------------------------------------------
//
// NOTE: Dqn_AllocatorMetadata
//
// -------------------------------------------------------------------------------------------------
// Custom Dqn_Allocator implementations must include allocation metadata exactly (aligned_ptr - sizeof(Dqn_AllocatorMetadata)) bytes from the aligned ptr.
struct Dqn_AllocateMetadata
{
Dqn_u8 alignment;
Dqn_u8 offset; // Subtract offset from aligned ptr to return to the allocation ptr
};
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
// //
// NOTE: Dqn_MemArena // NOTE: Dqn_MemArena
@ -272,21 +289,14 @@ struct Dqn_MemArenaScopedRegion
Dqn_MemBlock *top_mem_block; Dqn_MemBlock *top_mem_block;
}; };
#if defined(DQN_DEBUG_DQN_MEM_ARENA_LOGGING) void * Dqn_MemArena_Allocate(Dqn_MemArena *arena, Dqn_usize size, Dqn_u8 alignment);
#define DQN_DEBUG_ARGS , char const *file, Dqn_isize file_len, char const *func, Dqn_isize func_len, Dqn_isize line Dqn_b32 Dqn_MemArena_Reserve(Dqn_MemArena *arena, Dqn_usize size);
#define DQN_DEBUG_PARAMS , DQN_STR_AND_LEN(__FILE__), DQN_STR_AND_LEN(__func__), __LINE__ DQN_HEADER_COPY_PROTOTYPE(template <typename T> T *, Dqn_MemArena_AllocateType(Dqn_MemArena *arena, Dqn_isize num))
#else {
#define DQN_DEBUG_ARGS auto *result = DQN_CAST(T *)Dqn_MemArena_Allocate(arena, sizeof(T) * num, alignof(T));
#define DQN_DEBUG_PARAMS return result;
#endif }
#define DQN_MEM_ARENA_INIT_WITH_ALLOCATOR(allocator, size) Dqn_MemArena_InitWithAllocator(allocator, size DQN_DEBUG_PARAMS)
#define DQN_MEM_ARENA_INIT_MEMORY(src, size) Dqn_MemArena_InitMemory(src, size DQN_DEBUG_PARAMS)
#define DQN_MEM_ARENA_ALLOC(arena, size) Dqn_MemArena_Alloc(arena, size DQN_DEBUG_PARAMS)
#define DQN_MEM_ARENA_ALLOC_ARRAY(arena, T, num) (T *)Dqn_MemArena_Alloc(arena, sizeof(T) * num DQN_DEBUG_PARAMS)
#define DQN_MEM_ARENA_ALLOC_STRUCT(arena, T) (T *)Dqn_MemArena_Alloc(arena, sizeof(T) DQN_DEBUG_PARAMS)
#define DQN_MEM_ARENA_RESERVE(arena, size) Dqn_MemArena_Reserve(arena, size DQN_DEBUG_PARAMS)
#define DQN_MEM_ARENA_FREE(arena) Dqn_MemArena_Free(arena)
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
// //
@ -297,6 +307,7 @@ Dqn_Allocator inline Dqn_Allocator_Null();
Dqn_Allocator inline Dqn_Allocator_Heap(); Dqn_Allocator inline Dqn_Allocator_Heap();
Dqn_Allocator inline Dqn_Allocator_XHeap(); Dqn_Allocator inline Dqn_Allocator_XHeap();
Dqn_Allocator inline Dqn_Allocator_Arena(Dqn_MemArena *arena); Dqn_Allocator inline Dqn_Allocator_Arena(Dqn_MemArena *arena);
template <typename T> T * Dqn_Allocator_AllocateType(Dqn_Allocator *allocator, Dqn_isize num);
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
// //
// NOTE: String // NOTE: String
@ -360,10 +371,10 @@ template <Dqn_usize N> void Dqn_StringBuilder_Free(Dqn_StringBuild
// NOTE: Dqn_Slices // NOTE: Dqn_Slices
// //
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
template <typename T> inline Dqn_Slice<T> Dqn_Slice_CopyNullTerminated(Dqn_MemArena *arena, T const *src, Dqn_isize len); template <typename T> inline Dqn_Slice<T> Dqn_Slice_CopyNullTerminated(Dqn_Allocator *allocator, T const *src, Dqn_isize len);
template <typename T> inline Dqn_Slice<T> Dqn_Slice_CopyNullTerminated(Dqn_MemArena *arena, Dqn_Slice<T> const src); template <typename T> inline Dqn_Slice<T> Dqn_Slice_CopyNullTerminated(Dqn_Allocator *allocator, Dqn_Slice<T> const src);
template <typename T> inline Dqn_Slice<T> Dqn_Slice_Copy(Dqn_MemArena *arena, T const *src, Dqn_isize len); template <typename T> inline Dqn_Slice<T> Dqn_Slice_Copy(Dqn_Allocator *allocator, T const *src, Dqn_isize len);
template <typename T> inline Dqn_Slice<T> Dqn_Slice_Copy(Dqn_MemArena *arena, Dqn_Slice<T> const src); template <typename T> inline Dqn_Slice<T> Dqn_Slice_Copy(Dqn_Allocator *allocator, Dqn_Slice<T> const src);
template <typename T> inline bool Dqn_Slice_Equals(Dqn_Slice<T> const a, Dqn_Slice<T> const b); template <typename T> inline bool Dqn_Slice_Equals(Dqn_Slice<T> const a, Dqn_Slice<T> const b);
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
// //
@ -406,7 +417,6 @@ T * Dqn_FixedArray_Find(DQN_FIXED_ARRAY_TE
// NOTE: Dqn_Array // NOTE: Dqn_Array
// //
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
// TODO(doyle): Make this either initialised from memory or dynamically allocating
template <typename T> struct Dqn_Array template <typename T> struct Dqn_Array
{ {
Dqn_Allocator allocator; Dqn_Allocator allocator;
@ -502,7 +512,21 @@ char * Dqn_U64Str_ToStr(Dqn_u64 val, Dqn_U64S
void Dqn_LogV(Dqn_LogType type, char const *file, Dqn_usize file_len, char const *func, Dqn_usize func_len, Dqn_usize line, char const *fmt, va_list va); void Dqn_LogV(Dqn_LogType type, char const *file, Dqn_usize file_len, char const *func, Dqn_usize func_len, Dqn_usize line, char const *fmt, va_list va);
// return: This returns a boolean as a hack so you can combine it in if expressions. I use it for my IF_ASSERT macro // return: This returns a boolean as a hack so you can combine it in if expressions. I use it for my IF_ASSERT macro
Dqn_b32 Dqn_Log(Dqn_LogType type, char const *file, Dqn_usize file_len, char const *func, Dqn_usize func_len, Dqn_usize line, char const *fmt, ...); Dqn_b32 Dqn_Log(Dqn_LogType type, char const *file, Dqn_usize file_len, char const *func, Dqn_usize func_len, Dqn_usize line, char const *fmt, ...);
void * Dqn_Allocator_Allocate(Dqn_Allocator *allocator, Dqn_usize size); // -------------------------------------------------------------------------------------------------
//
// NOTE: Dqn_AllocateMetadata
//
// -------------------------------------------------------------------------------------------------
char * Dqn_AllocateMetadata_Init(void *ptr, Dqn_u8 alignment);
Dqn_AllocateMetadata Dqn_AllocateMetadata_Get(void *ptr);
char * Dqn_AllocateMetadata_GetRawPointer(void *ptr);
Dqn_isize Dqn_AllocateMetadata_SizeRequired(Dqn_isize size, Dqn_u8 alignment);
// -------------------------------------------------------------------------------------------------
//
// NOTE: Dqn_Allocator
//
// -------------------------------------------------------------------------------------------------
void * Dqn_Allocator_Allocate(Dqn_Allocator *allocator, Dqn_isize size, Dqn_u8 alignment);
void * Dqn_Allocator_Realloc(Dqn_Allocator *allocator, void *old_ptr, Dqn_isize old_size, Dqn_isize new_size); void * Dqn_Allocator_Realloc(Dqn_Allocator *allocator, void *old_ptr, Dqn_isize old_size, Dqn_isize new_size);
void Dqn_Allocator_Free(Dqn_Allocator *allocator, void *ptr); void Dqn_Allocator_Free(Dqn_Allocator *allocator, void *ptr);
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
@ -510,11 +534,11 @@ void Dqn_Allocator_Free(Dqn_Allocator *allo
// NOTE: Dqn_MemArena // NOTE: Dqn_MemArena
// //
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
void * Dqn_MemArena_Alloc(Dqn_MemArena *arena, Dqn_usize size DQN_DEBUG_ARGS); void * Dqn_MemArena_Allocate(Dqn_MemArena *arena, Dqn_usize size, u8 alignment);
void Dqn_MemArena_Free(Dqn_MemArena *arena DQN_DEBUG_ARGS); void Dqn_MemArena_Free(Dqn_MemArena *arena);
Dqn_b32 Dqn_MemArena_Reserve(Dqn_MemArena *arena, Dqn_usize size DQN_DEBUG_ARGS); Dqn_b32 Dqn_MemArena_Reserve(Dqn_MemArena *arena, Dqn_usize size);
Dqn_MemArena Dqn_MemArena_InitWithAllocator(Dqn_Allocator allocator, Dqn_usize size DQN_DEBUG_ARGS); Dqn_MemArena Dqn_MemArena_InitWithAllocator(Dqn_Allocator allocator, Dqn_usize size);
Dqn_MemArena Dqn_MemArena_InitMemory(void *memory, Dqn_usize size DQN_DEBUG_ARGS); Dqn_MemArena Dqn_MemArena_InitMemory(void *memory, Dqn_usize size);
void Dqn_MemArena_ResetUsage(Dqn_MemArena *arena, Dqn_ZeroMem zero_mem); void Dqn_MemArena_ResetUsage(Dqn_MemArena *arena, Dqn_ZeroMem zero_mem);
Dqn_MemArenaScopedRegion Dqn_MemArena_MakeScopedRegion(Dqn_MemArena *arena); Dqn_MemArenaScopedRegion Dqn_MemArena_MakeScopedRegion(Dqn_MemArena *arena);
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------

View File

@ -53,8 +53,8 @@ struct TestingState
DQN_ASSERT(testing_state.test.scope_started); \ DQN_ASSERT(testing_state.test.scope_started); \
if (!(expr)) \ if (!(expr)) \
{ \ { \
testing_state.test.fail_expr = Dqn_Asprintf(&testing_state.allocator, #expr); \ testing_state.test.fail_expr = Dqn_Asprintf(&testing_state.allocator, #expr); \
testing_state.test.fail_msg = Dqn_Asprintf(&testing_state.allocator, msg, ##__VA_ARGS__); \ testing_state.test.fail_msg = Dqn_Asprintf(&testing_state.allocator, msg, ##__VA_ARGS__); \
} }
#define TEST_EXPECT(testing_state, expr) TEST_EXPECT_MSG(testing_state, expr, "") #define TEST_EXPECT(testing_state, expr) TEST_EXPECT_MSG(testing_state, expr, "")
@ -116,35 +116,108 @@ FILE_SCOPE void UnitTests()
// --------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------
{ {
TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Allocator"); TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Allocator");
// NOTE: Various allocator test
{ {
TEST_START_SCOPE(testing_state, "HeapAllocator - Allocate Small"); {
Dqn_Allocator allocator = Dqn_Allocator_Heap(); TEST_START_SCOPE(testing_state, "HeapAllocator - Allocate Small");
char constexpr EXPECT[] = "hello_world"; Dqn_Allocator allocator = Dqn_Allocator_Heap();
char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT)); char constexpr EXPECT[] = "hello_world";
DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT), alignof(char));
memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT)); DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); };
TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT));
TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT);
}
{
TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocate Small");
Dqn_Allocator allocator = Dqn_Allocator_XHeap();
char constexpr EXPECT[] = "hello_world";
char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT), alignof(char));
DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); };
memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT));
TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT);
}
{
TEST_START_SCOPE(testing_state, "ArenaAllocator - Allocate Small");
Dqn_MemArena arena = {};
Dqn_Allocator allocator = Dqn_Allocator_Arena(&arena);
char constexpr EXPECT[] = "hello_world";
char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT), alignof(char));
DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); };
memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT));
TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT);
}
} }
// NOTE: Alignment Test
{ {
TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocate Small"); Dqn_u8 const ALIGNMENT3 = 3;
Dqn_Allocator allocator = Dqn_Allocator_XHeap(); Dqn_u8 const NUM_BYTES = sizeof(Dqn_u32);
char constexpr EXPECT[] = "hello_world"; {
char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT)); TEST_START_SCOPE(testing_state, "HeapAllocator - Align to 3 bytes");
DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; Dqn_Allocator allocator = Dqn_Allocator_Heap();
memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT)); auto *buf = DQN_CAST(Dqn_u32 *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3);
TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); };
int buf_mod_alignment = DQN_CAST(int)(DQN_CAST(uintptr_t)buf % ALIGNMENT3);
TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment);
}
{
TEST_START_SCOPE(testing_state, "XHeapAllocator - Align to 3 bytes");
Dqn_Allocator allocator = Dqn_Allocator_XHeap();
auto *buf = DQN_CAST(Dqn_u32 *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3);
DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); };
int buf_mod_alignment = DQN_CAST(int)(DQN_CAST(uintptr_t)buf % ALIGNMENT3);
TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment);
}
{
TEST_START_SCOPE(testing_state, "ArenaAllocator - Align to 3 bytes");
Dqn_MemArena arena = {};
Dqn_Allocator allocator = Dqn_Allocator_Arena(&arena);
auto *buf = DQN_CAST(Dqn_u32 *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3);
int buf_mod_alignment = DQN_CAST(int)(DQN_CAST(uintptr_t)buf % ALIGNMENT3);
TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment);
}
} }
// NOTE: Dqn_AllocateMetadata tests
{ {
TEST_START_SCOPE(testing_state, "ArenaAllocator - Allocate Small"); Dqn_u8 const ALIGNMENT3 = 3;
Dqn_MemArena arena = {}; Dqn_u8 const NUM_BYTES = 4;
Dqn_Allocator allocator = Dqn_Allocator_Arena(&arena); Dqn_u8 const MAX_OFFSET = (ALIGNMENT3 - 1) + sizeof(Dqn_AllocateMetadata);
char constexpr EXPECT[] = "hello_world"; {
char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT)); TEST_START_SCOPE(testing_state, "HeapAllocator - Allocation metadata initialised");
DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; Dqn_Allocator allocator = Dqn_Allocator_Heap();
memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT)); char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3);
TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); };
Dqn_AllocateMetadata metadata = Dqn_AllocateMetadata_Get(buf);
TEST_EXPECT_MSG(testing_state, metadata.alignment == ALIGNMENT3, "metadata.alignment: %u, ALIGNMENT3: %u", metadata.alignment, ALIGNMENT3);
TEST_EXPECT_MSG(testing_state, metadata.offset <= MAX_OFFSET, "metadata.offset: %u, MAX_OFFSET: %u", metadata.offset, MAX_OFFSET);
}
{
TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocation metadata initialised");
Dqn_Allocator allocator = Dqn_Allocator_XHeap();
char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3);
DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); };
Dqn_AllocateMetadata metadata = Dqn_AllocateMetadata_Get(buf);
TEST_EXPECT_MSG(testing_state, metadata.alignment == ALIGNMENT3, "metadata.alignment: %u, ALIGNMENT3: %u", metadata.alignment, ALIGNMENT3);
TEST_EXPECT_MSG(testing_state, metadata.offset <= MAX_OFFSET, "metadata.offset: %u, MAX_OFFSET: %u", metadata.offset, MAX_OFFSET);
}
{
TEST_START_SCOPE(testing_state, "ArenaAllocator - Allocation metadata initialised");
Dqn_MemArena arena = {};
Dqn_Allocator allocator = Dqn_Allocator_Arena(&arena);
char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3);
DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); };
Dqn_AllocateMetadata metadata = Dqn_AllocateMetadata_Get(buf);
TEST_EXPECT_MSG(testing_state, metadata.alignment == ALIGNMENT3, "metadata.alignment: %u, ALIGNMENT3: %u", metadata.alignment, ALIGNMENT3);
TEST_EXPECT_MSG(testing_state, metadata.offset <= MAX_OFFSET, "metadata.offset: %u, MAX_OFFSET: %u", metadata.offset, MAX_OFFSET);
}
} }
} }