From d31c63cbb74e81805bcb8582be5670bb7da78ddc Mon Sep 17 00:00:00 2001 From: doyle Date: Thu, 19 Sep 2019 21:49:11 +1000 Subject: [PATCH] Add unit tests for allocators/arrays --- Code/Build.bat | 2 +- Code/Dqn.h | 288 ++++++++++++++++++++++++++++++++----- Code/DqnHeader_Generated.h | 97 ++++++++----- Code/Dqn_UnitTests.cpp | 169 +++++++++++++++++++++- 4 files changed, 488 insertions(+), 68 deletions(-) diff --git a/Code/Build.bat b/Code/Build.bat index ecfe9f8..c7bf797 100644 --- a/Code/Build.bat +++ b/Code/Build.bat @@ -14,5 +14,5 @@ REM Tp Treat header file as CPP source file cl /MT /EHa /GR- /Od /Oi /Z7 /W4 /WX /wd4201 /D DQN_HEADER_IMPLEMENTATION /Tp ../Code/DqnHeader.h /link /nologo DqnHeader.exe ..\Code\Dqn.h > ..\Code\DqnHeader_Generated.h -cl /MT /EHa /GR- /Od /Oi /Z7 /W4 /WX /wd4201 /D DQN_IMPLEMENTATION ../Code/Dqn_UnitTests.cpp /link /nologo +cl /MT /EHa /GR- /Od /Oi /Z7 /W4 /WX /wd4201 ../Code/Dqn_UnitTests.cpp /link /nologo popd diff --git a/Code/Dqn.h b/Code/Dqn.h index 65f9108..113b0f0 100644 --- a/Code/Dqn.h +++ b/Code/Dqn.h @@ -1,3 +1,7 @@ +// #define DQN_USE_PRIMITIVE_TYPEDEFS to enable typical typedefs such as i32 = int32_t .. etc +// #define DQN_IMPLEMENTATION in one and only one C++ file to enable the header file + +#define _CRT_SECURE_NO_WARNINGS // NOTE: Undefined at end of header file #if defined(DQN_IMPLEMENTATION) #define STB_SPRINTF_IMPLEMENTATION #endif @@ -198,6 +202,7 @@ STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char comma, char peri // @ NOTE: Typedefs, Macros, Utils // @ // @ ------------------------------------------------------------------------------------------------- +#define DQN_CAST(val) (val) #define DQN_ABS(val) (((val) < 0) ? (-(val)) : (val)) #define DQN_SQUARED(val) ((val) * (val)) #define DQN_MIN(a, b) ((a < b) ? (a) : (b)) @@ -606,12 +611,12 @@ struct Dqn_MemArenaScopedRegion #define DQN_DEBUG_PARAMS #endif -#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_RESERVE_FROM(arena, src, size) Dqn_MemArena_ReserveFrom(arena, src, size DQN_DEBUG_PARAMS); -#define DQN_MEM_ARENA_CLEAR_USED(arena) Dqn_MemArena_ClearUsed(arena 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_RESERVE_FROM(arena, src, size) Dqn_MemArena_ReserveFrom(arena, src, size DQN_DEBUG_PARAMS) +#define DQN_MEM_ARENA_CLEAR_USED(arena) Dqn_MemArena_ClearUsed(arena DQN_DEBUG_PARAMS) #define DQN_MEM_ARENA_FREE(arena) Dqn_MemArena_Free DQN_HEADER_COPY_END @@ -1057,37 +1062,142 @@ template T *Dqn_FixedStack_Push (Dqn_FixedStack void Dqn_FixedStack_Clear(Dqn_FixedStack *array) { Dqn_FixedArray_Clear(array); } DQN_HEADER_COPY_END -// @ ------------------------------------------------------------------------------------------------- -// @ -// @ NOTE: Dqn_StaticArray -// @ -// @ ------------------------------------------------------------------------------------------------- DQN_HEADER_COPY_BEGIN -template struct Dqn_StaticArray +enum struct Dqn_Allocator_Type { - T *data; - Dqn_isize len; - Dqn_isize max; - T const operator[](Dqn_isize i) const { 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]; } - T const *begin () const { return data; } - T const *end () const { return data + len; } - T *begin () { return data; } - T *end () { return data + len; } - T const *operator+(Dqn_isize i) const { 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; } + Heap, // Malloc, realloc, free + XHeap, // Malloc realloc, free, crash on failure + Arena, + NullAllocator, +}; + +struct Dqn_Allocator +{ + Dqn_Allocator_Type type; + void *data; }; DQN_HEADER_COPY_END -DQN_HEADER_COPY_PROTOTYPE(template Dqn_StaticArray , Dqn_StaticArray_InitMemory (T *memory, Dqn_isize max, Dqn_isize len = 0)) { Dqn_StaticArray result = {}; result.data = memory; result.len = len; result.max = max; return result; } -DQN_HEADER_COPY_PROTOTYPE(template T *, Dqn_StaticArray_Add (Dqn_StaticArray *a, T const *items, Dqn_isize num)) { DQN_ASSERT(a->len + num <= a->max); T *result = static_cast(Dqn_MemCopy(a->data + a->len, items, sizeof(T) * num)); a->len += num; return result; } -DQN_HEADER_COPY_PROTOTYPE(template T *, Dqn_StaticArray_Add (Dqn_StaticArray *a, T const item)) { DQN_ASSERT(a->len < a->max); a->data[a->len++] = item; return &a->data[a->len - 1]; } -DQN_HEADER_COPY_PROTOTYPE(template T *, Dqn_StaticArray_Make (Dqn_StaticArray *a, Dqn_isize num)) { DQN_ASSERT(a->len + num <= a->max); T *result = a->data + a->len; a->len += num; return result;} -DQN_HEADER_COPY_PROTOTYPE(template void , Dqn_StaticArray_Clear (Dqn_StaticArray *a)) { a->len = 0; } -DQN_HEADER_COPY_PROTOTYPE(template void , Dqn_StaticArray_EraseStable (Dqn_StaticArray *a, Dqn_isize index)) { Dqn__EraseStableFromCArray(a->data, a->len--, a->max, index); } -DQN_HEADER_COPY_PROTOTYPE(template void , Dqn_StaticArray_EraseUnstable(Dqn_StaticArray *a, Dqn_isize index)) { DQN_ASSERT(index >= 0 && index < a->len); if (--a->len == 0) return; a->data[index] = a->data[a->len]; } -DQN_HEADER_COPY_PROTOTYPE(template void , Dqn_StaticArray_Pop (Dqn_StaticArray *a, Dqn_isize num)) { DQN_ASSERT(a->len - num >= 0); a->len -= num; } -DQN_HEADER_COPY_PROTOTYPE(template T *, Dqn_StaticArray_Peek (Dqn_StaticArray *a)) { T *result = (a->len == 0) ? nullptr : a->data + (a->len - 1); return result; } +// @ ------------------------------------------------------------------------------------------------- +// @ +// @ NOTE: Dqn_Array +// @ +// @ ------------------------------------------------------------------------------------------------- +DQN_HEADER_COPY_BEGIN +// TODO(doyle): Make this either initialised from memory or dynamically allocating +template struct Dqn_Array +{ + Dqn_Allocator allocator; + T *data; + Dqn_isize len; + Dqn_isize max; + + T const operator[](Dqn_isize i) const { 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]; } + T const *begin () const { return data; } + T const *end () const { return data + len; } + T *begin () { return data; } + T *end () { return data + len; } + T const *operator+(Dqn_isize i) const { 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_END + +DQN_HEADER_COPY_PROTOTYPE(template Dqn_Array, Dqn_Array_InitMemory(T *memory, Dqn_isize max, Dqn_isize len = 0)) +{ + Dqn_Array result = {}; + result.allocator = Dqn_Allocator_NullAllocator(); + result.data = memory; + result.len = len; + result.max = max; + return result; +} + +DQN_HEADER_COPY_PROTOTYPE(template bool, Dqn_Array_Reserve(Dqn_Array *a, Dqn_isize size)) +{ + if (size <= a->len) return true; + 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); + else new_ptr = DQN_CAST(T *)Dqn_Allocator_Allocate(&a->allocator, sizeof(T) * size); + if (!new_ptr) return false; + a->data = new_ptr; + a->max = size; + return true; +} + +DQN_HEADER_COPY_PROTOTYPE(template void, Dqn_Array_Free(Dqn_Array *a)) +{ + Dqn_Allocator_Free(&a->allocator, a->data); +} + + +template bool Dqn_Array__GrowIfNeeded(Dqn_Array *a, Dqn_isize num_to_add) +{ + Dqn_isize new_len = a->len + num_to_add; + bool result = true; + if (new_len > a->max) + { + Dqn_isize num_items = DQN_MAX(4, DQN_MAX(new_len, (a->max * 2))); + result = Dqn_Array_Reserve(a, num_items); + } + + return result; +} + +DQN_HEADER_COPY_PROTOTYPE(template T *, Dqn_Array_Add(Dqn_Array *a, T const *items, Dqn_isize num)) +{ + if (!Dqn_Array__GrowIfNeeded(a, num)) + return nullptr; + T *result = static_cast(Dqn_MemCopy(a->data + a->len, items, sizeof(T) * num)); + a->len += num; + return result; +} + +DQN_HEADER_COPY_PROTOTYPE(template T *, Dqn_Array_Add(Dqn_Array *a, T const item)) +{ + if (!Dqn_Array__GrowIfNeeded(a, 1)) + return nullptr; + a->data[a->len++] = item; + return &a->data[a->len - 1]; +} +DQN_HEADER_COPY_PROTOTYPE(template T *, Dqn_Array_Make(Dqn_Array *a, Dqn_isize num)) +{ + if (!Dqn_Array__GrowIfNeeded(a, num)) + return nullptr; + T *result = a->data + a->len; + a->len += num; + return result; +} + +DQN_HEADER_COPY_PROTOTYPE(template void, Dqn_Array_Clear(Dqn_Array *a, bool zero_mem = false)) +{ + a->len = 0; + if (zero_mem) memset(a->data, 0, sizeof(T) * a->max); +} + +DQN_HEADER_COPY_PROTOTYPE(template void, Dqn_Array_EraseStable(Dqn_Array *a, Dqn_isize index)) +{ + Dqn__EraseStableFromCArray(a->data, a->len--, a->max, index); +} +DQN_HEADER_COPY_PROTOTYPE(template void, + Dqn_Array_EraseUnstable(Dqn_Array *a, Dqn_isize index)) +{ + DQN_ASSERT(index >= 0 && index < a->len); + if (--a->len == 0) return; + a->data[index] = a->data[a->len]; +} + +DQN_HEADER_COPY_PROTOTYPE(template void, Dqn_Array_Pop(Dqn_Array *a, Dqn_isize num)) +{ + DQN_ASSERT(a->len - num >= 0); + a->len -= num; +} + +DQN_HEADER_COPY_PROTOTYPE(template T *, Dqn_Array_Peek(Dqn_Array *a)) +{ + T *result = (a->len == 0) ? nullptr : a->data + (a->len - 1); + return result; +} // @ ------------------------------------------------------------------------------------------------- // @ @@ -1163,9 +1273,11 @@ struct Dqn_U64Str int len; }; DQN_HEADER_COPY_END +#undef _CRT_SECURE_NO_WARNINGS #endif // DQN_H #ifdef DQN_IMPLEMENTATION +#define _CRT_SECURE_NO_WARNINGS #include #include #include @@ -1451,6 +1563,114 @@ Dqn_MemArenaScopedRegion::~Dqn_MemArenaScopedRegion() this->arena->curr_mem_block->used = this->curr_mem_block_used; } +// @ ------------------------------------------------------------------------------------------------- +// @ +// @ NOTE: Dqn_Allocator +// @ +// @ ------------------------------------------------------------------------------------------------- +DQN_HEADER_COPY_PROTOTYPE(Dqn_Allocator, Dqn_Allocator_NullAllocator()) +{ + Dqn_Allocator result = {}; + result.type = Dqn_Allocator_Type::NullAllocator; + return result; +} + +DQN_HEADER_COPY_PROTOTYPE(Dqn_Allocator, Dqn_Allocator_HeapAllocator()) +{ + Dqn_Allocator result = {}; + result.type = Dqn_Allocator_Type::Heap; + return result; +} + +DQN_HEADER_COPY_PROTOTYPE(Dqn_Allocator, Dqn_Allocator_XHeapAllocator()) +{ + Dqn_Allocator result = {}; + result.type = Dqn_Allocator_Type::XHeap; + return result; +} + +DQN_HEADER_COPY_PROTOTYPE(Dqn_Allocator, Dqn_Allocator_ArenaAllocator(Dqn_MemArena *arena)) +{ + Dqn_Allocator result = {}; + result.type = Dqn_Allocator_Type::Arena; + result.data = arena; + return result; +} + +DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Allocate(Dqn_Allocator *allocator, Dqn_usize size)) +{ + void *result = nullptr; + switch (allocator->type) + { + default: break; + case Dqn_Allocator_Type::Heap: + case Dqn_Allocator_Type::XHeap: + { + result = malloc(size); + if (!result && allocator->type == Dqn_Allocator_Type::XHeap) + DQN_ASSERT(result); + } + break; + + case Dqn_Allocator_Type::Arena: + { + auto *arena = static_cast(allocator->data); + result = DQN_MEM_ARENA_ALLOC(arena, size); + } + break; + } + + return result; +} + +DQN_HEADER_COPY_PROTOTYPE(void *, Dqn_Allocator_Realloc(Dqn_Allocator *allocator, void *old_ptr, Dqn_isize old_size, Dqn_isize new_size)) +{ + void *result = nullptr; + switch (allocator->type) + { + default: break; + case Dqn_Allocator_Type::Heap: + case Dqn_Allocator_Type::XHeap: + { + result = realloc(old_ptr, new_size); + if (!result && allocator->type == Dqn_Allocator_Type::XHeap) + DQN_ASSERT(result); + } + break; + + case Dqn_Allocator_Type::Arena: + { + auto *arena = static_cast(allocator->data); + if (DQN_MEM_ARENA_RESERVE(arena, new_size)) + { + result = DQN_MEM_ARENA_ALLOC(arena, new_size); + if (result) memcpy(result, old_ptr, old_size); + } + } + break; + } + + return result; +} + +DQN_HEADER_COPY_PROTOTYPE(void, Dqn_Allocator_Free(Dqn_Allocator *allocator, void *ptr)) +{ + switch (allocator->type) + { + default: break; + case Dqn_Allocator_Type::Heap: + case Dqn_Allocator_Type::XHeap: + { + free(ptr); + } + break; + + case Dqn_Allocator_Type::Arena: + break; + } +} + + // @ ------------------------------------------------------------------------------------------------- // @ // @ NOTE: Vectors @@ -1842,8 +2062,9 @@ DQN_HEADER_COPY_PROTOTYPE(Dqn_b32, Dqn_Char_IsWhitespace(char ch)) // @ NOTE: String Helpers // @ // @ ------------------------------------------------------------------------------------------------- -DQN_HEADER_COPY_PROTOTYPE(Dqn_b32, Dqn_Str_Equals(char const *a, Dqn_isize a_len, char const *b, Dqn_isize b_len = -1)) +DQN_HEADER_COPY_PROTOTYPE(Dqn_b32, Dqn_Str_Equals(char const *a, char const *b, Dqn_isize a_len = -1, Dqn_isize b_len = -1)) { + if (a_len == -1) a_len = strlen(a); if (b_len == -1) b_len = strlen(b); if (a_len != b_len) return false; return (strncmp(a, b, a_len) == 0); @@ -2081,6 +2302,7 @@ DQN_HEADER_COPY_PROTOTYPE(char *, Dqn_File_ReadWithArena(Dqn_MemArena *arena, ch if (file_size) *file_size = file_size_; return result; } +#undef _CRT_SECURE_NO_WARNINGS #endif // DQN_IMPLEMENTATION #ifdef STB_SPRINTF_IMPLEMENTATION diff --git a/Code/DqnHeader_Generated.h b/Code/DqnHeader_Generated.h index b137b1f..4f64f64 100644 --- a/Code/DqnHeader_Generated.h +++ b/Code/DqnHeader_Generated.h @@ -252,12 +252,12 @@ struct Dqn_MemArenaScopedRegion #define DQN_DEBUG_PARAMS #endif -#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_RESERVE_FROM(arena, src, size) Dqn_MemArena_ReserveFrom(arena, src, size DQN_DEBUG_PARAMS); -#define DQN_MEM_ARENA_CLEAR_USED(arena) Dqn_MemArena_ClearUsed(arena 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_RESERVE_FROM(arena, src, size) Dqn_MemArena_ReserveFrom(arena, src, size DQN_DEBUG_PARAMS) +#define DQN_MEM_ARENA_CLEAR_USED(arena) Dqn_MemArena_ClearUsed(arena DQN_DEBUG_PARAMS) #define DQN_MEM_ARENA_FREE(arena) Dqn_MemArena_Free // ------------------------------------------------------------------------------------------------- @@ -362,35 +362,54 @@ template T *Dqn_FixedStack_Peek (Dqn_FixedStack T *Dqn_FixedStack_Push (Dqn_FixedStack *array, T item) { return Dqn_FixedArray_Add(array, item); } template void Dqn_FixedStack_Clear(Dqn_FixedStack *array) { Dqn_FixedArray_Clear(array); } -// ------------------------------------------------------------------------------------------------- -// -// NOTE: Dqn_StaticArray -// -// ------------------------------------------------------------------------------------------------- -template struct Dqn_StaticArray +enum struct Dqn_Allocator_Type { - T *data; - Dqn_isize len; - Dqn_isize max; - T const operator[](Dqn_isize i) const { 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]; } - T const *begin () const { return data; } - T const *end () const { return data + len; } - T *begin () { return data; } - T *end () { return data + len; } - T const *operator+(Dqn_isize i) const { 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; } + Heap, // Malloc, realloc, free + XHeap, // Malloc realloc, free, crash on failure + Arena, + NullAllocator, }; -template Dqn_StaticArray Dqn_StaticArray_InitMemory (T *memory, Dqn_isize max, Dqn_isize len = 0); -template T * Dqn_StaticArray_Add (Dqn_StaticArray *a, T const *items, Dqn_isize num); -template T * Dqn_StaticArray_Add (Dqn_StaticArray *a, T const item); -template T * Dqn_StaticArray_Make (Dqn_StaticArray *a, Dqn_isize num); -template void Dqn_StaticArray_Clear (Dqn_StaticArray *a); -template void Dqn_StaticArray_EraseStable (Dqn_StaticArray *a, Dqn_isize index); -template void Dqn_StaticArray_EraseUnstable(Dqn_StaticArray *a, Dqn_isize index); -template void Dqn_StaticArray_Pop (Dqn_StaticArray *a, Dqn_isize num); -template T * Dqn_StaticArray_Peek (Dqn_StaticArray *a); +struct Dqn_Allocator +{ + Dqn_Allocator_Type type; + void *data; +}; + +// ------------------------------------------------------------------------------------------------- +// +// NOTE: Dqn_Array +// +// ------------------------------------------------------------------------------------------------- +// TODO(doyle): Make this either initialised from memory or dynamically allocating +template struct Dqn_Array +{ + Dqn_Allocator allocator; + T *data; + Dqn_isize len; + Dqn_isize max; + + T const operator[](Dqn_isize i) const { 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]; } + T const *begin () const { return data; } + T const *end () const { return data + len; } + T *begin () { return data; } + T *end () { return data + len; } + T const *operator+(Dqn_isize i) const { 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; } +}; + +template Dqn_Array Dqn_Array_InitMemory(T *memory, Dqn_isize max, Dqn_isize len = 0); +template bool Dqn_Array_Reserve(Dqn_Array *a, Dqn_isize size); +template void Dqn_Array_Free(Dqn_Array *a); +template T * Dqn_Array_Add(Dqn_Array *a, T const *items, Dqn_isize num); +template T * Dqn_Array_Add(Dqn_Array *a, T const item); +template T * Dqn_Array_Make(Dqn_Array *a, Dqn_isize num); +template void Dqn_Array_Clear(Dqn_Array *a, bool zero_mem = false); +template void Dqn_Array_EraseStable(Dqn_Array *a, Dqn_isize index); +template void Dqn_Array_EraseUnstable(Dqn_Array *a, Dqn_isize index); +template void Dqn_Array_Pop(Dqn_Array *a, Dqn_isize num); +template T * Dqn_Array_Peek(Dqn_Array *a); // ------------------------------------------------------------------------------------------------- // // NOTE: Dqn_FixedString @@ -468,6 +487,18 @@ void Dqn_MemArena_ClearUsed(Dqn_MemArena *a Dqn_MemArenaScopedRegion Dqn_MemArena_MakeScopedRegion(Dqn_MemArena *arena); // ------------------------------------------------------------------------------------------------- // +// NOTE: Dqn_Allocator +// +// ------------------------------------------------------------------------------------------------- +Dqn_Allocator Dqn_Allocator_NullAllocator(); +Dqn_Allocator Dqn_Allocator_HeapAllocator(); +Dqn_Allocator Dqn_Allocator_XHeapAllocator(); +Dqn_Allocator Dqn_Allocator_ArenaAllocator(Dqn_MemArena *arena); +void * Dqn_Allocator_Allocate(Dqn_Allocator *allocator, Dqn_usize 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); +// ------------------------------------------------------------------------------------------------- +// // NOTE: Vectors // // ------------------------------------------------------------------------------------------------- @@ -549,7 +580,7 @@ Dqn_b32 Dqn_Char_IsWhitespace(char ch); // NOTE: String Helpers // // ------------------------------------------------------------------------------------------------- -Dqn_b32 Dqn_Str_Equals(char const *a, Dqn_isize a_len, char const *b, Dqn_isize b_len = -1); +Dqn_b32 Dqn_Str_Equals(char const *a, char const *b, Dqn_isize a_len = -1, Dqn_isize b_len = -1); char const * Dqn_Str_FindMulti(char const *buf, char const *find_list[], Dqn_isize const *find_string_lens, Dqn_isize find_len, Dqn_isize *match_index, Dqn_isize buf_len = -1); char const * Dqn_Str_Find(char const *buf, char const *find, Dqn_isize buf_len = -1, Dqn_isize find_len = -1); Dqn_b32 Dqn_Str_Match(char const *src, char const *find, int find_len); diff --git a/Code/Dqn_UnitTests.cpp b/Code/Dqn_UnitTests.cpp index 2c77118..83c472a 100644 --- a/Code/Dqn_UnitTests.cpp +++ b/Code/Dqn_UnitTests.cpp @@ -1,5 +1,5 @@ #define DQN_USE_PRIMITIVE_TYPEDEFS -#define _CRT_SECURE_NO_WARNINGS +#define DQN_IMPLEMENTATION #include "Dqn.h" struct TestState @@ -106,6 +106,173 @@ void TestState_PrintResult(TestState const *result) FILE_SCOPE void UnitTests() { TestingState testing_state = {}; + // --------------------------------------------------------------------------------------------- + // + // NOTE: Dqn_Allocator + // + // --------------------------------------------------------------------------------------------- + { + TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Allocator"); + { + TEST_START_SCOPE(testing_state, "HeapAllocator - Allocate Small"); + Dqn_Allocator allocator = Dqn_Allocator_HeapAllocator(); + char constexpr EXPECT[] = "hello_world"; + char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT)); + 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, "XHeapAllocator - Allocate Small"); + Dqn_Allocator allocator = Dqn_Allocator_XHeapAllocator(); + char constexpr EXPECT[] = "hello_world"; + char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT)); + 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_ArenaAllocator(&arena); + char constexpr EXPECT[] = "hello_world"; + char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT)); + 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: Dqn_Array + // + // --------------------------------------------------------------------------------------------- + { + TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Array"); + // NOTE: Dqn_Array_InitMemory + { + { + TEST_START_SCOPE(testing_state, "Fixed Memory: Test add single item and can't allocate more"); + int memory[4] = {}; + Dqn_Array array = Dqn_Array_InitMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/); + Dqn_Array_Add(&array, 1); + Dqn_Array_Add(&array, 2); + Dqn_Array_Add(&array, 3); + Dqn_Array_Add(&array, 4); + 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[2] == 3, "array.data %d", array.data[2]); + TEST_EXPECT_MSG(testing_state, array.data[3] == 4, "array.data %d", array.data[3]); + TEST_EXPECT_MSG(testing_state, array.len == 4, "array.len: %d", array.len); + + int *added_item = Dqn_Array_Add(&array, 5); + TEST_EXPECT(testing_state, added_item == nullptr); + TEST_EXPECT_MSG(testing_state, array.len == 4, "array.len: %d", array.len); + TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + } + + { + TEST_START_SCOPE(testing_state, "Fixed Memory: Test add array of items"); + int memory[4] = {}; + int DATA[] = {1, 2, 3}; + Dqn_Array array = Dqn_Array_InitMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/); + 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[1] == 2, "array.data %d", array.data[1]); + TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data %d", array.data[2]); + TEST_EXPECT_MSG(testing_state, array.len == 3, "array.len: %d", array.len); + TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + } + + { + TEST_START_SCOPE(testing_state, "Fixed Memory: Test clear and clear with memory zeroed"); + int memory[4] = {}; + int DATA[] = {1, 2, 3}; + Dqn_Array array = Dqn_Array_InitMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/); + Dqn_Array_Add(&array, DATA, Dqn_ArrayCount(DATA)); + 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.max == 4, "array.max: %d", array.max); + TEST_EXPECT_MSG(testing_state, array.data[0] == 1, "array.data %d. Clear but don't zero memory so old values should still remain", array.data[0]); + + Dqn_Array_Clear(&array, true /*zero_mem*/); + TEST_EXPECT_MSG(testing_state, array.data[0] == 0, "array.data %d. Clear but zero memory old values should not remain", array.data[0]); + } + + { + TEST_START_SCOPE(testing_state, "Fixed Memory: Test erase stable and erase unstable"); + int memory[4] = {}; + int DATA[] = {1, 2, 3, 4}; + Dqn_Array array = Dqn_Array_InitMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/); + Dqn_Array_Add(&array, DATA, Dqn_ArrayCount(DATA)); + 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[1] == 4, "array.data %d", array.data[1]); + TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data %d", array.data[2]); + TEST_EXPECT_MSG(testing_state, array.len == 3, "array.len: %d", array.len); + + Dqn_Array_EraseStable(&array, 0); + TEST_EXPECT_MSG(testing_state, array.data[0] == 4, "array.data: %d", array.data[0]); + TEST_EXPECT_MSG(testing_state, array.data[1] == 3, "array.data: %d", array.data[1]); + TEST_EXPECT_MSG(testing_state, array.len == 2, "array.len: %d", array.len); + } + + { + TEST_START_SCOPE(testing_state, "Fixed Memory: Test array pop and peek"); + int memory[4] = {}; + int DATA[] = {1, 2, 3}; + Dqn_Array array = Dqn_Array_InitMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/); + Dqn_Array_Add(&array, DATA, Dqn_ArrayCount(DATA)); + 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.len == 1, "array.len: %d", array.len); + TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + + int *peek_item = Dqn_Array_Peek(&array); + TEST_EXPECT_MSG(testing_state, *peek_item == 1, "peek: %d", *peek_item); + TEST_EXPECT_MSG(testing_state, array.len == 1, "array.len: %d", array.len); + TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + } + + { + TEST_START_SCOPE(testing_state, "Fixed Memory: Test free on fixed memory array does nothing"); + int memory[4] = {}; + Dqn_Array array = Dqn_Array_InitMemory(memory, Dqn_ArrayCount(memory), 0 /*len*/); + DQN_DEFER { Dqn_Array_Free(&array); }; + } + } + + // NOTE: Dynamic Memory: Dqn_Array + { + { + TEST_START_SCOPE(testing_state, "Dynamic Memory: Test reserve and over commit reallocates"); + Dqn_Array array = {}; + DQN_DEFER { Dqn_Array_Free(&array); }; + + Dqn_Array_Reserve(&array, 4); + TEST_EXPECT_MSG(testing_state, array.len == 0, "array.len: %d", array.len); + TEST_EXPECT_MSG(testing_state, array.max == 4, "array.max: %d", array.max); + + int DATA[] = {1, 2, 3, 4}; + 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[1] == 2, "array.data: %d", array.data[1]); + TEST_EXPECT_MSG(testing_state, array.data[2] == 3, "array.data: %d", array.data[2]); + TEST_EXPECT_MSG(testing_state, array.data[3] == 4, "array.data: %d", array.data[3]); + TEST_EXPECT_MSG(testing_state, array.len == 4, "array.len: %d", array.len); + + int *added_item = Dqn_Array_Add(&array, 5); + TEST_EXPECT_MSG(testing_state, *added_item == 5, "added_item: %d", *added_item); + TEST_EXPECT_MSG(testing_state, array.data[4] == 5, "array.data: %d", array.data[4]); + TEST_EXPECT_MSG(testing_state, array.len == 5, "array.len: %d", array.len); + TEST_EXPECT_MSG(testing_state, array.max >= 5, "array.max: %d", array.max); + } + } + } + // --------------------------------------------------------------------------------------------- // // NOTE: Dqn_StringBuilder