From a0b64be71bd6fe501e010889aa0c94780daafd23 Mon Sep 17 00:00:00 2001 From: Doyle Date: Thu, 30 Aug 2018 01:04:56 +1000 Subject: [PATCH] Simplify memory api to dqnallocator --- DqnMemStack.cpp | 4 +- DqnUnitTest.cpp | 383 ---------------------------- dqn.h | 666 +++++++++--------------------------------------- 3 files changed, 117 insertions(+), 936 deletions(-) diff --git a/DqnMemStack.cpp b/DqnMemStack.cpp index 06aca87..779c39b 100644 --- a/DqnMemStack.cpp +++ b/DqnMemStack.cpp @@ -228,9 +228,8 @@ FILE_SCOPE void DqnMemStack_Test() { usize size = 32; usize additional_size = DqnMemStack::MINIMUM_BLOCK_SIZE; - DqnMemAPI heap = DqnMemAPI::HeapAllocator(); - auto stack = DqnMemStack(size, Dqn::ZeroClear::Yes, 0, &heap); + auto stack = DqnMemStack(size, Dqn::ZeroClear::Yes, 0); auto *block1 = stack.block; size += additional_size; @@ -271,7 +270,6 @@ FILE_SCOPE void DqnMemStack_Test() DQN_ASSERT(block1->prev_block == nullptr); stack.Free(); - DQN_ASSERT(stack.mem_api->bytes_allocated == 0); DQN_ASSERT(stack.block == nullptr); Log(Status::Ok, "Check freeing arbitrary blocks and freeing"); } diff --git a/DqnUnitTest.cpp b/DqnUnitTest.cpp index 335cf9a..82b3359 100644 --- a/DqnUnitTest.cpp +++ b/DqnUnitTest.cpp @@ -1244,383 +1244,6 @@ void DqnRect_Test() } } -void DqnArray_TestInternal(DqnMemAPI *const mem_api) -{ - if (1) - { - DqnArray array(mem_api); - if (1) - { - array.Reserve(1); - DQN_ASSERT(array.max >= 1); - DQN_ASSERT(array.len == 0); - - // Test basic push - if (1) - { - DqnV2 va = DqnV2(5, 10); - DQN_ASSERT(array.Push(va)); - - DqnV2 vb = array.data[0]; - DQN_ASSERT(DqnV2_Equals(va, vb)); - - DQN_ASSERT(array.max >= 1); - DQN_ASSERT(array.len == 1); - Log(Status::Ok, "Test basic push"); - } - - // Test array resizing and freeing - if (1) - { - DqnV2 va = DqnV2(10, 15); - DQN_ASSERT(array.Push(va)); - - DqnV2 vb = array.data[0]; - DQN_ASSERT(DqnV2_Equals(va, vb) == false); - - vb = array.data[1]; - DQN_ASSERT(DqnV2_Equals(va, vb) == true); - - DQN_ASSERT(array.max >= 2); - DQN_ASSERT(array.len == 2); - - DQN_ASSERT(array.Push(va)); - DQN_ASSERT(array.max >= 3); - DQN_ASSERT(array.len == 3); - - DQN_ASSERT(array.Push(va)); - DQN_ASSERT(array.max >= 4); - DQN_ASSERT(array.len == 4); - - DQN_ASSERT(array.Push(va)); - DQN_ASSERT(array.max >= 5); - DQN_ASSERT(array.len == 5); - - DQN_ASSERT(array.Push(va)); - DQN_ASSERT(array.max >= 6); - DQN_ASSERT(array.len == 6); - - DQN_ASSERT(array.Push(va)); - DQN_ASSERT(array.max >= 7); - DQN_ASSERT(array.len == 7); - - DQN_ASSERT(array.Push(va)); - DQN_ASSERT(array.max >= 8); - DQN_ASSERT(array.len == 8); - - DQN_ASSERT(array.Push(va)); - DQN_ASSERT(array.max >= 9); - DQN_ASSERT(array.len == 9); - - DQN_ASSERT(array.Push(va)); - DQN_ASSERT(array.max >= 10); - DQN_ASSERT(array.len == 10); - - DQN_ASSERT(array.Push(va)); - DQN_ASSERT(array.max >= 11); - DQN_ASSERT(array.len == 11); - - DqnV2 vc = DqnV2(90, 100); - DQN_ASSERT(array.Push(vc)); - DQN_ASSERT(array.max >= 12); - DQN_ASSERT(array.len == 12); - DQN_ASSERT(DqnV2_Equals(vc, array.data[11])); - - Log(Status::Ok, "Test resizing and free"); - } - array.Free(); - - // Test insert - if (1) - { - DqnV2 va = DqnV2(5, 10); - array.Push(va); - array.Push(va); - array.Push(va); - - DqnV2 vb = DqnV2(1, 2); - array.Insert(-1, vb); - DQN_ASSERT(DqnV2_Equals(array.data[0], vb)); - - DqnV2 vc = DqnV2(2, 1); - array.Insert(array.len, vc); - DQN_ASSERT(DqnV2_Equals(array.data[array.len-1], vc)); - - DqnV2 vd = DqnV2(8, 9); - array.Insert(1, vd); - - DQN_ASSERT(DqnV2_Equals(array.data[0], vb)); - DQN_ASSERT(DqnV2_Equals(array.data[1], vd)); - DQN_ASSERT(DqnV2_Equals(array.data[2], va)); - DQN_ASSERT(DqnV2_Equals(array.data[3], va)); - DQN_ASSERT(DqnV2_Equals(array.data[4], va)); - DQN_ASSERT(DqnV2_Equals(array.data[5], vc)); - - Log(Status::Ok, "Test insert"); - } - array.Free(); - - // Test multi-insert - if (1) - { - DqnV2 va[] = {DqnV2(5, 10), DqnV2(6, 10), DqnV2(7, 10)}; - DqnV2 tmp = DqnV2(1, 1); - array.Push(tmp); - array.Push(tmp); - array.Push(tmp); - - array.Insert(1, va, DQN_ARRAY_COUNT(va)); - DQN_ASSERT(DqnV2_Equals(array.data[0], tmp)); - DQN_ASSERT(DqnV2_Equals(array.data[1], va[0])); - DQN_ASSERT(DqnV2_Equals(array.data[2], va[1])); - DQN_ASSERT(DqnV2_Equals(array.data[3], va[2])); - DQN_ASSERT(DqnV2_Equals(array.data[4], tmp)); - DQN_ASSERT(DqnV2_Equals(array.data[5], tmp)); - - Log(Status::Ok, "Test insert"); - } - array.Free(); - } - - if (1) - { - array.Reserve(1); - DQN_ASSERT(array.max >= 1); - DQN_ASSERT(array.len == 0); - Log(Status::Ok, "Empty array"); - } - array.Free(); - - if (1) - { - DqnV2 a = DqnV2(1, 2); - DqnV2 b = DqnV2(3, 4); - DqnV2 c = DqnV2(5, 6); - DqnV2 d = DqnV2(7, 8); - - array.Reserve(16); - DQN_ASSERT(array.max >= 16); - DQN_ASSERT(array.len == 0); - - array.Clear(); - DQN_ASSERT(array.max >= 16); - DQN_ASSERT(array.len == 0); - - DQN_ASSERT(array.Push(a)); - DQN_ASSERT(array.Push(b)); - DQN_ASSERT(array.Push(c)); - DQN_ASSERT(array.Push(d)); - DQN_ASSERT(array.max >= 16); - DQN_ASSERT(array.len == 4); - - array.Erase(0); - DQN_ASSERT(DqnV2_Equals(array.data[0], d)); - DQN_ASSERT(DqnV2_Equals(array.data[1], b)); - DQN_ASSERT(DqnV2_Equals(array.data[2], c)); - DQN_ASSERT(array.max >= 16); - DQN_ASSERT(array.len == 3); - - array.Erase(2); - DQN_ASSERT(DqnV2_Equals(array.data[0], d)); - DQN_ASSERT(DqnV2_Equals(array.data[1], b)); - DQN_ASSERT(array.max >= 16); - DQN_ASSERT(array.len == 2); - - // array.Erase(100); - DQN_ASSERT(DqnV2_Equals(array.data[0], d)); - DQN_ASSERT(DqnV2_Equals(array.data[1], b)); - DQN_ASSERT(array.max >= 16); - DQN_ASSERT(array.len == 2); - - array.Clear(); - DQN_ASSERT(array.max >= 16); - DQN_ASSERT(array.len == 0); - Log(Status::Ok, "Test removal"); - } - array.Free(); - array.mem_api = mem_api; - - if (1) - { - DqnV2 a = DqnV2(1, 2); - DqnV2 b = DqnV2(3, 4); - DqnV2 c = DqnV2(5, 6); - DqnV2 d = DqnV2(7, 8); - - array.Reserve(16); - - DQN_ASSERT(array.Push(a)); - DQN_ASSERT(array.Push(b)); - DQN_ASSERT(array.Push(c)); - DQN_ASSERT(array.Push(d)); - DQN_ASSERT(array.max >= 16); - DQN_ASSERT(array.len == 4); - - array.EraseStable(0); - DQN_ASSERT(DqnV2_Equals(array.data[0], b)); - DQN_ASSERT(DqnV2_Equals(array.data[1], c)); - DQN_ASSERT(DqnV2_Equals(array.data[2], d)); - DQN_ASSERT(array.max >= 16); - DQN_ASSERT(array.len == 3); - - array.EraseStable(1); - DQN_ASSERT(DqnV2_Equals(array.data[0], b)); - DQN_ASSERT(DqnV2_Equals(array.data[1], d)); - DQN_ASSERT(array.max >= 16); - DQN_ASSERT(array.len == 2); - - array.EraseStable(1); - DQN_ASSERT(DqnV2_Equals(array.data[0], b)); - DQN_ASSERT(array.max >= 16); - DQN_ASSERT(array.len == 1); - - Log(Status::Ok, "Test stable removal"); - } - array.Free(); - } - - // TODO(doyle): Stable erase list API -#if 0 - if (1) - { - // Test normal remove list scenario - if (1) - { - i64 indexes_to_free[] = {3, 2, 1, 0}; - i32 int_list[] = {128, 32, 29, 31}; - - DqnArray array(mem_api); - array.Reserve(DQN_ARRAY_COUNT(int_list)); - - array.Push(int_list, DQN_ARRAY_COUNT(int_list)); - array.EraseStable(indexes_to_free, DQN_ARRAY_COUNT(indexes_to_free)); - DQN_ASSERT(array.len == 0); - array.Free(); - } - - // Test all indexes invalid - if (1) - { - i64 indexes_to_free[] = {100, 200, 300, 400}; - i32 int_list[] = {128, 32, 29, 31}; - - DqnArray array(mem_api); - array.Reserve(DQN_ARRAY_COUNT(int_list)); - array.Push(int_list, DQN_ARRAY_COUNT(int_list)); - array.EraseStable(indexes_to_free, DQN_ARRAY_COUNT(indexes_to_free)); - DQN_ASSERT(array.len == 4); - DQN_ASSERT(array.data[0] == 128); - DQN_ASSERT(array.data[1] == 32); - DQN_ASSERT(array.data[2] == 29); - DQN_ASSERT(array.data[3] == 31); - array.Free(); - } - - // Test remove singular index - if (1) - { - i64 indexes_to_free[] = {1}; - i32 int_list[] = {128, 32, 29, 31}; - - DqnArray array(mem_api); - array.Reserve(DQN_ARRAY_COUNT(int_list)); - array.Push(int_list, DQN_ARRAY_COUNT(int_list)); - array.EraseStable(indexes_to_free, DQN_ARRAY_COUNT(indexes_to_free)); - DQN_ASSERT(array.len == 3); - DQN_ASSERT(array.data[0] == 128); - DQN_ASSERT(array.data[1] == 29); - DQN_ASSERT(array.data[2] == 31); - array.Free(); - } - - // Test remove singular invalid index - if (1) - { - i64 indexes_to_free[] = {100}; - i32 int_list[] = {128, 32, 29, 31}; - - DqnArray array(mem_api); - array.Reserve(DQN_ARRAY_COUNT(int_list)); - array.Push(int_list, DQN_ARRAY_COUNT(int_list)); - array.EraseStable(indexes_to_free, DQN_ARRAY_COUNT(indexes_to_free)); - DQN_ASSERT(array.len == 4); - DQN_ASSERT(array.data[0] == 128); - DQN_ASSERT(array.data[1] == 32); - DQN_ASSERT(array.data[2] == 29); - DQN_ASSERT(array.data[3] == 31); - array.Free(); - } - - // Test remove second last index - if (1) - { - i64 indexes_to_free[] = {2}; - i32 int_list[] = {128, 32, 29, 31}; - - DqnArray array(mem_api); - array.Reserve(DQN_ARRAY_COUNT(int_list)); - array.Push(int_list, DQN_ARRAY_COUNT(int_list)); - array.EraseStable(indexes_to_free, DQN_ARRAY_COUNT(indexes_to_free)); - DQN_ASSERT(array.len == 3); - DQN_ASSERT(array.data[0] == 128); - DQN_ASSERT(array.data[1] == 32); - DQN_ASSERT(array.data[2] == 31); - array.Free(); - } - - // Test remove last 2 indexes - if (1) - { - i64 indexes_to_free[] = {2, 3}; - i32 int_list[] = {128, 32, 29, 31}; - - DqnArray array(mem_api); - array.Reserve(DQN_ARRAY_COUNT(int_list)); - array.Push(int_list, DQN_ARRAY_COUNT(int_list)); - array.EraseStable(indexes_to_free, DQN_ARRAY_COUNT(indexes_to_free)); - DQN_ASSERT(array.len == 2); - DQN_ASSERT(array.data[0] == 128); - DQN_ASSERT(array.data[1] == 32); - array.Free(); - } - - // Test invalid free index doesn't delete out of bounds - if (1) - { - i64 indexes_to_free[] = {30, 1, 3}; - i32 int_list[] = {128, 32, 29, 31}; - - DqnArray array(mem_api); - array.Reserve(DQN_ARRAY_COUNT(int_list)); - array.Push(int_list, DQN_ARRAY_COUNT(int_list)); - array.EraseStable(indexes_to_free, DQN_ARRAY_COUNT(indexes_to_free)); - - DQN_ASSERT(array.len == 2); - DQN_ASSERT(array.data[0] == 128); - DQN_ASSERT(array.data[1] == 29); - array.Free(); - } - - // Test a free list including the first index - if (1) - { - i64 indexes_to_free[] = {0, 1, 2}; - i32 int_list[] = {128, 32, 29, 31}; - - DqnArray array(mem_api); - array.Reserve(DQN_ARRAY_COUNT(int_list)); - array.Push(int_list, DQN_ARRAY_COUNT(int_list)); - array.EraseStable(indexes_to_free, DQN_ARRAY_COUNT(indexes_to_free)); - - DQN_ASSERT(array.len == 1); - DQN_ASSERT(array.data[0] == 31); - array.Free(); - } - Log(Status::Ok, "Test stable removal with list of indexes"); - } -#endif -} - void DqnArray_TestRealDataInternal(DqnArray *array) { (void)array; @@ -1646,12 +1269,6 @@ void DqnArray_TestRealDataInternal(DqnArray *array) void DqnArray_Test() { LOG_HEADER(); - if (1) - { - auto allocator = DqnMemAPI::HeapAllocator(); - DqnArray_TestInternal(&allocator); - } - if (1) { if (1) diff --git a/dqn.h b/dqn.h index eb3a7e5..eaf509d 100644 --- a/dqn.h +++ b/dqn.h @@ -32,7 +32,7 @@ // #DqnRnd Random Number Generator (ints and floats) // #Dqn_* Utility code, (qsort, quick file reading) // #DqnMem Memory Allocation -// #DqnMemAPI Custom memory API for Dqn Data Structures +// #DqnAllocator Custom memory API for Dqn Data Structures // #DqnPool Pool objects // #DqnArray Dynamic Array using Templates // #DqnMemStack Memory Allocator, Push, Pop Style @@ -1153,85 +1153,24 @@ DQN_FILE_SCOPE void DqnMem_Copy (void *dest, void const *src, usize num_bytes DQN_FILE_SCOPE void *DqnMem_Set (void *dest, u8 value, usize num_bytes_to_set); DQN_FILE_SCOPE int DqnMem_Cmp(void const *src, void const *dest, usize num_bytes); -// #DqnMemAPI API +// #DqnAllocator API // ================================================================================================= -// You only need to care about this API if you want to use custom mem-alloc routines in the data -// structures! Otherwise it already has a default one to use. - -// How To Use: -// 1. Implement the allocator function, where DqnMemAPI::Request will tell you the request. -// - (NOTE) The callback should return the resulting data into DqnMemAPI::Result -// 2. Create a DqnMemAPI struct with a function ptr to your allocator -// - (OPTIONAL) Set the user context to your book-keeping/mem allocating service -// 3. Initialise any data structure that supports a DqnMemAPI with your struct. - -// That's it! Done :) Of course, changing mem_api's after initialisation is invalid since the -// pointers belonging to your old routine may not be tracked in your new mem_api. So you're at your -// own discretion there. - -class DqnMemAPI +// A generic structure for managing memory allocations for different Dqn data structures. +using DqnMallocProc = void *(size_t size); +using DqnFreeProc = void (void *ptr); +using DqnCallocProc = void *(size_t num_items, size_t size); +using DqnReallocProc = void *(void *ptr, size_t size); +struct DqnAllocator { -public: - enum struct Type - { - Uninitialised, - Alloc, - Calloc, - Realloc, - Free - }; - - struct Request - { - void *user_context; - Type type; - union - { - struct Alloc_ - { - bool zero_clear; - isize request_size; - } alloc; - - struct Free_ - { - void *ptr_to_free; - isize size_to_free; - } free; - - struct Realloc_ - { - isize new_size; - isize old_size; - void *old_mem_ptr; - } realloc; - } e; - }; - - typedef void *Allocator(DqnMemAPI *, DqnMemAPI::Request); - - Allocator *allocator; - void *user_context; - - isize bytes_allocated; - isize lifetime_bytes_allocated; - isize lifetime_bytes_freed; - - static DqnMemAPI HeapAllocator (); - - enum struct StackPushType - { - Head, - Tail, - }; - static DqnMemAPI StackAllocator(struct DqnMemStack *stack, StackPushType type = StackPushType::Head); - - void *Realloc(void *old_ptr, isize old_size, isize new_size); - void *Alloc (isize size, Dqn::ZeroClear clear = Dqn::ZeroClear::Yes); - void Free (void *ptr_to_free, isize size_to_free = 0); // TODO(doyle): size_to_free opt is iffy - bool IsValid() const { return (this->allocator != nullptr); } + DqnMallocProc *Malloc; + DqnCallocProc *Calloc; + DqnReallocProc *Realloc; + DqnFreeProc *Free; }; +DqnAllocator DQN_DEFAULT_ALLOCATOR_ = {malloc, calloc, realloc, free}; +DqnAllocator *DQN_DEFAULT_ALLOCATOR = &DQN_DEFAULT_ALLOCATOR_; + // #DqnPool API // ================================================================================================= template @@ -1325,34 +1264,31 @@ struct DqnPool } }; -FILE_SCOPE DqnMemAPI DQN_DEFAULT_HEAP_ALLOCATOR_ = DqnMemAPI::HeapAllocator(); -FILE_SCOPE DqnMemAPI *DQN_DEFAULT_HEAP_ALLOCATOR = &DQN_DEFAULT_HEAP_ALLOCATOR_; - // #DqnArray API // ================================================================================================= template struct DqnArray { - DqnMemAPI *mem_api = DQN_DEFAULT_HEAP_ALLOCATOR; + DqnAllocator *allocator = DQN_DEFAULT_ALLOCATOR; isize len; isize max; T *data; DqnArray () = default; - DqnArray (DqnMemAPI *mem_api_) { *this = {}; this->mem_api = mem_api_; } - // ~DqnArray () { if (this->data && this->mem_api) this->mem_api->Free(data); } + DqnArray (DqnAllocator *allocator) { *this = {}; this->allocator = allocator; } + // ~DqnArray () { if (this->data && this->mem_api) this->mem_api->Free(data); } - void UseMemory (T *data_, isize max_, isize len_ = 0) { this->mem_api = nullptr; this->data = data_; this->max = max_; this->len = len_; } - void Clear (Dqn::ZeroClear clear = Dqn::ZeroClear::No) { if (!data) return; len = 0; if (clear == Dqn::ZeroClear::Yes) DqnMem_Clear(data, 0, sizeof(T) * max); } - void Free () { if (data) { mem_api->Free(data); } *this = {}; } - T *Front () { DQN_ASSERT(len > 0); return data + 0; } - T *Back () { DQN_ASSERT(len > 0); return data + (len - 1); } + void UseMemory (T *data_, isize max_, isize len_ = 0) { this->mem_api = nullptr; this->data = data_; this->max = max_; this->len = len_; } + void Clear (Dqn::ZeroClear clear = Dqn::ZeroClear::No) { if (!data) return; len = 0; if (clear == Dqn::ZeroClear::Yes) DqnMem_Clear(data, 0, sizeof(T) * max); } + void Free () { if (data) { allocator->Free(data); } *this = {}; } + T *Front () { DQN_ASSERT(len > 0); return data + 0; } + T *Back () { DQN_ASSERT(len > 0); return data + (len - 1); } void Resize (isize new_len) { if (new_len > max) Reserve(GrowCapacity_(new_len)); len = new_len; } void Resize (isize new_len, T const *v) { if (new_len > max) Reserve(GrowCapacity_(new_len)); if (new_len > len) for (isize n = len; n < new_len; n++) data[n] = *v; len = new_len; } void Reserve (isize new_max); T *Make (isize len = 1) { len += len; if (len > max) Reserve(GrowCapacity_(len)); return &data[len - len]; } - T *Push (T const &v) { return Insert(len, &v, 1); } - T *Push (T const *v, isize len_items = 1) { return Insert(len, v, len_items); } + T *Push (T const &v) { if (len + 1 > max) Reserve(GrowCapacity_(len + 1)); data[len++] = v; return data + (len-1); } + T *Push (T const *v, isize v_len = 1) { isize new_len = len + v_len; if (new_len > max) Reserve(GrowCapacity_(new_len)); T *result = data + len; for (isize i = 0; i < new_len; ++i) data[len++] = v[i]; return result; } void Pop () { if (len > 0) len--; } void Erase (isize index) { DQN_ASSERT(index >= 0 && index < len); data[index] = data[--len]; } void EraseStable(isize index); @@ -1406,13 +1342,13 @@ template void DqnArray::Reserve(isize new_max) if (data) { - T *newData = (T *)mem_api->Realloc(data, max * sizeof(T), new_max * sizeof(T)); + T *newData = (T *)allocator->Realloc(data, new_max * sizeof(T)); data = newData; max = new_max; } else { - data = (T *)mem_api->Alloc(new_max * sizeof(T)); + data = (T *)allocator->Malloc(new_max * sizeof(T)); max = new_max; } @@ -1448,8 +1384,8 @@ struct DqnMemTracker static u32 const HEAD_GUARD_VALUE = 0xCAFEBABE; static u32 const TAIL_GUARD_VALUE = 0xDEADBEEF; - DqnArray allocations; // Read: When BoundsGuard is enabled, tracks all allocations. - u32 bounds_guard_size; // Read: sizeof(GUARD_VALUE) OR 0 if BoundsGuard is disabled. + DqnArray allocations; // Read: When BoundsGuard is enabled, tracks all allocations. + u32 bounds_guard_size; // Read: sizeof(GUARD_VALUE) OR 0 if BoundsGuard is disabled. void Init (bool bounds_guard); void Free () { allocations.Free(); } @@ -1518,7 +1454,7 @@ struct DqnMemStack }; DqnMemTracker tracker; // Read: Metadata for managing ptr allocation - DqnMemAPI *mem_api; // Read: API used to add additional memory blocks to this stack. + DqnAllocator *block_allocator; // Read: Allocator used to allocator blocks for this memory stack Block *block; // Read: Memory block allocated for the stack u32 flags; // Read i32 tmp_region_count;// Read: The number of temp memory regions in use @@ -1529,14 +1465,12 @@ struct DqnMemStack DqnMemStack(void *mem, isize size, Dqn::ZeroClear clear, u32 flags_ = 0); // Memory Stack capable of expanding when full, but only if NonExpandable flag is not set. - DqnMemStack(isize size, Dqn::ZeroClear clear, u32 flags_ = 0, DqnMemAPI *api = DQN_DEFAULT_HEAP_ALLOCATOR); + DqnMemStack (isize size, Dqn::ZeroClear clear, u32 flags_ = 0, DqnAllocator *block_allocator_ = DQN_DEFAULT_ALLOCATOR) { LazyInit(size, clear, flags_, block_allocator_); } + void LazyInit(isize size, Dqn::ZeroClear clear, u32 flags_ = 0, DqnAllocator *block_allocator_ = DQN_DEFAULT_ALLOCATOR); // Allocation API // ============================================================================================= - enum struct AllocTo - { - Head, Tail - }; + enum struct AllocTo { Head, Tail }; // Allocate memory from the MemStack. // alignment: Ptr returned from allocator is aligned to this value and MUST be power of 2. @@ -1592,6 +1526,16 @@ struct DqnMemStack // Keep allocations that have occurred since Begin(). End() does not need to be called anymore. void TempRegionKeepChanges(TempRegion region); + + void *MallocHead (size_t size) { return Push(size); } + void *CallocHead (size_t size) { void *result = Push(size); DqnMem_Clear(result, 0, size); } + void FreeHead (void *ptr) { (void)ptr; return; } + void *ReallocHead(void *ptr, size_t size) { DqnPtrHeader *header = tracker.PtrToHeader((char *)ptr); void *result = Push(size); DqnMem_Copy(result, ptr, header->alloc_amount); return result; } + + void *MallocTail (size_t size) { return Push(size, AllocTo::Tail); } + void *CallocTail (size_t size) { void *result = Push(size, AllocTo::Tail); DqnMem_Clear(result, 0, size); } + void FreeTail (void *ptr) { (void)ptr; return; } + void *ReallocTail(void *ptr, size_t size) { DqnPtrHeader *header = tracker.PtrToHeader((char *)ptr); void *result = Push(size, AllocTo::Tail); DqnMem_Copy(result, ptr, header->alloc_amount); return result; } }; // #DqnHash API @@ -1851,13 +1795,13 @@ struct DqnRect // ================================================================================================= struct DqnString { - DqnMemAPI *mem_api = DQN_DEFAULT_HEAP_ALLOCATOR; - int len = 0; - int max = 0; - char *str = nullptr; + DqnAllocator *allocator = DQN_DEFAULT_ALLOCATOR; + int len = 0; + int max = 0; + char *str = nullptr; DqnString() = default; - DqnString(char *buf, int max_) : mem_api(nullptr), len(0), str(buf) { max = max_; NullTerminate(); } + DqnString(char *buf, int max_) : len(0), str(buf) { max = max_; NullTerminate(); } DqnString(char const *str_) { Append(str_); } DqnString(char const *str_, int len_) { Append(str_, len_); } DqnString(DqnSlice const &other) { Append(other.data, other.len); } @@ -1884,14 +1828,13 @@ struct DqnString void NullTerminate () { str[len] = 0; } // NOTE: If you modify the storage directly, this can be handy. void Clear (Dqn::ZeroClear clear = Dqn::ZeroClear::No) { if (clear == Dqn::ZeroClear::Yes) DqnMem_Set(str, 0, max); len = max = 0; NullTerminate(); } - void Free () { if (str) mem_api->Free(str); str = nullptr; } + void Free () { if (str) allocator->Free(str); str = nullptr; } void Resize (int new_max) { if (new_max > max) Reserve(new_max); len = DQN_MIN(new_max, len); NullTerminate(); } void Reserve (int new_max); void Append (char const *src, int len_ = -1); - int VSprintfAtOffset(char const *fmt, va_list va, int offset) { Reserve(len + VAListLen(fmt, va) + 1); int result = Dqn_vsnprintf(str + offset, max - len, fmt, va); len = (offset + result); return result; } + int VSprintfAtOffset(char const *fmt, va_list va, int offset) { Reserve(len + Dqn_vsnprintf(nullptr, 0, fmt, va) + 1); int result = Dqn_vsnprintf(str + offset, max - len, fmt, va); len = (offset + result); return result; } - static int VAListLen (char const *fmt, va_list va); static bool Cmp (DqnString const *a, DqnString const *b, Dqn::IgnoreCase ignore = Dqn::IgnoreCase::No) { return (a->len == b->len) && (DqnStr_Cmp(a->str, b->str, a->len, ignore) == 0); } static bool Cmp (DqnString const *a, DqnSlice const b, Dqn::IgnoreCase ignore = Dqn::IgnoreCase::No) { return (a->len == b.len) && (DqnStr_Cmp(a->str, b.data, b.len, ignore) == 0); } static bool Cmp (DqnString const *a, DqnSlice const b, Dqn::IgnoreCase ignore = Dqn::IgnoreCase::No) { return (a->len == b.len) && (DqnStr_Cmp(a->str, b.data, b.len, ignore) == 0); } @@ -1899,7 +1842,7 @@ struct DqnString // return: -1 if invalid, or if buf_size is 0 the required buffer length in wchar_t characters i32 ToWChar(wchar_t *const buf, i32 const buf_size) const; // return: String allocated using api. - wchar_t *ToWChar(DqnMemAPI *const api = DQN_DEFAULT_HEAP_ALLOCATOR) const; + wchar_t *ToWChar(DqnAllocator *allocator = DQN_DEFAULT_ALLOCATOR) const; }; @@ -1942,7 +1885,15 @@ struct DqnFixedString void NullTerminate () { str[len] = 0; } // NOTE: If you modify the storage directly, this can be handy. void Clear (Dqn::ZeroClear clear = Dqn::ZeroClear::No) { if (clear == Dqn::ZeroClear::Yes) DqnMem_Set(str, 0, MAX); *this = {}; } - int VSprintfAtOffset(char const *fmt, va_list va, int offset) { char *start = str + offset; int result = Dqn_vsnprintf(start, static_cast((str + MAX) - start), fmt, va); len = (offset + result); return result; } + int VSprintfAtOffset(char const *fmt, va_list va, int offset) + { + char *start = str + offset; + int result = Dqn_vsnprintf(start, static_cast((str + MAX) - start), fmt, va); + len = (offset + result); + if (Dqn::IS_DEBUG) DQN_ASSERT(Dqn_vsnprintf(nullptr, 0, fmt, va) < MAX); + + return result; + } }; using DqnFixedString16 = DqnFixedString<16>; @@ -2077,16 +2028,16 @@ struct DqnVArray DqnVArray () = default; // Zero is initialisation DqnVArray (isize size) { LazyInit(size); } - void LazyInit (isize size) { if (data) return; len = 0; max = size; data = (T *)DqnOS_VAlloc(max * sizeof(T)); DQN_ALWAYS_ASSERT(data); } - // ~DqnVArray () { if (data) DqnOS_VFree(data, sizeof(T) * max); } + void LazyInit (isize size) { *this = {}; if (data) return; len = 0; max = size; data = (T *)DqnOS_VAlloc(max * sizeof(T)); DQN_ALWAYS_ASSERT(data); } + // ~DqnVArray () { if (data) DqnOS_VFree(data, sizeof(T) * max); } void Clear (Dqn::ZeroClear clear = Dqn::ZeroClear::No) { if (data) { len = 0; if (clear == Dqn::ZeroClear::Yes) DqnMem_Clear(data, 0, sizeof(T) * max); } } void Free () { if (data) { DqnOS_VFree(data, sizeof(T) * max); } *this = {}; } T *Front () { return (len > 0) ? (data + 0) : nullptr; } T *Back () { return (len > 0) ? (data + (len - 1)) : nullptr; } - T *Make (isize num = 1) { LazyInit(1024); len += num; DQN_ASSERT(len <= max); return &data[len - num]; } - T *Push (T const &v) { return Insert(len, &v, 1); } - T *Push (T const *v, isize num_items = 1) { return Insert(len, v, num_items); } + T *Make (isize num = 1) { if (!data) LazyInit(1024); len += num; DQN_ASSERT(len <= max); return &data[len - num]; } + T *Push (T const &v) { data[len++] = v; return data + (len - 1); } + T *Push (T const *v, isize v_len = 1) { T *result = data + len; for (isize i = 0; i < new_len; ++i) data[len++] = v[i]; return result; } void Pop () { if (len > 0) len--; } void Erase (isize index) { if (!data) return; DQN_ASSERT(index >= 0 && index < len); data[index] = data[--len]; } void EraseStable(isize index); @@ -2102,7 +2053,7 @@ struct DqnVArray template T *DqnVArray::Insert(isize index, T const *v, isize num_items) { - LazyInit(1024); + if (!data) LazyInit(1024); index = DQN_CLAMP(index, 0, len); isize const new_len = len + num_items; @@ -2454,8 +2405,8 @@ DQN_FILE_SCOPE bool DqnFile_ReadAll(wchar_t const *path, u8 *buf, usize buf_si // Buffer is null-terminated and should be freed when done with. // return: False if file access failure OR nullptr arguments. -DQN_FILE_SCOPE u8 *DqnFile_ReadAll(char const *path, usize *buf_size, DqnMemAPI *api = DQN_DEFAULT_HEAP_ALLOCATOR); -DQN_FILE_SCOPE u8 *DqnFile_ReadAll(wchar_t const *path, usize *buf_size, DqnMemAPI *api = DQN_DEFAULT_HEAP_ALLOCATOR); +DQN_FILE_SCOPE u8 *DqnFile_ReadAll(char const *path, usize *buf_size, DqnAllocator *allocator = DQN_DEFAULT_ALLOCATOR); +DQN_FILE_SCOPE u8 *DqnFile_ReadAll(wchar_t const *path, usize *buf_size, DqnAllocator *allocator = DQN_DEFAULT_ALLOCATOR); DQN_FILE_SCOPE u8 *DqnFile_ReadAll(wchar_t const *path, usize *buf_size, DqnMemStack *stack, DqnMemStack::AllocTo allocTo = DqnMemStack::AllocTo::Head); DQN_FILE_SCOPE u8 *DqnFile_ReadAll(char const *path, usize *buf_size, DqnMemStack *stack, DqnMemStack::AllocTo allocTo = DqnMemStack::AllocTo::Head); @@ -2484,8 +2435,8 @@ DQN_FILE_SCOPE bool DqnFile_Copy (wchar_t const *src, wchar_t const *dest); // num_files: Pass in a ref to a i32. The function fills it out with the number of entries. // return: An array of strings of the files in the directory in UTF-8. The directory lisiting is // allocated with malloc and must be freed using free() or the helper function ListDirFree() -DQN_FILE_SCOPE char **DqnFile_ListDir (char const *dir, i32 *num_files, DqnMemAPI *api = DQN_DEFAULT_HEAP_ALLOCATOR); -DQN_FILE_SCOPE void DqnFile_ListDirFree (char **file_list, i32 num_files, DqnMemAPI *api = DQN_DEFAULT_HEAP_ALLOCATOR); +DQN_FILE_SCOPE char **DqnFile_ListDir (char const *dir, i32 *num_files, DqnAllocator *allocator = DQN_DEFAULT_ALLOCATOR); +DQN_FILE_SCOPE void DqnFile_ListDirFree (char **file_list, i32 num_files, DqnAllocator *allocator = DQN_DEFAULT_ALLOCATOR); // XPlatform > #DqnCatalog API // ================================================================================================= @@ -2938,369 +2889,6 @@ DQN_FILE_SCOPE int DqnMem_Cmp(void const *src, void const *dest, usize num_bytes return (src_ptr[i] - dest_ptr[i]); } -// #DqnMemAPI -// ================================================================================================= -FILE_SCOPE void DqnMemAPI__ValidateRequest(DqnMemAPI::Request request_) -{ - DQN_ASSERT(request_.type != DqnMemAPI::Type::Uninitialised); - - if (request_.type == DqnMemAPI::Type::Alloc) - { - auto *request = &request_.e.alloc; - DQN_ASSERT(request->request_size > 0); - return; - } - - if (request_.type == DqnMemAPI::Type::Realloc) - { - auto *request = &request_.e.realloc; - DQN_ASSERT(request->old_size > 0); - DQN_ASSERT(request->new_size > 0); - DQN_ASSERT((request->new_size - request->old_size) != 0); - DQN_ASSERT(request->old_mem_ptr); - return; - } -} - -FILE_SCOPE void DqnMemAPI__UpdateAPIStatistics(DqnMemAPI *api, DqnMemAPI::Request *request_) -{ - if (request_->type == DqnMemAPI::Type::Alloc) - { - auto *request = &request_->e.alloc; - api->bytes_allocated += request->request_size; - api->lifetime_bytes_allocated += request->request_size; - return; - } - - if (request_->type == DqnMemAPI::Type::Realloc) - { - auto *request = &request_->e.realloc; - api->lifetime_bytes_allocated += request->new_size; - api->lifetime_bytes_freed += request->old_size; - - api->bytes_allocated += request->new_size; - api->bytes_allocated -= request->old_size; - return; - } - - if (request_->type == DqnMemAPI::Type::Free) - { - auto *request = &request_->e.free; - api->bytes_allocated -= request->size_to_free; - api->lifetime_bytes_freed += request->size_to_free; - return; - } -} - -FILE_SCOPE void *DqnMemAPI__HeapAllocatorCallback(DqnMemAPI *api, DqnMemAPI::Request request_) -{ - DqnMemAPI__ValidateRequest(request_); - DQN_ASSERT(!request_.user_context); - - u8 *result = nullptr; - bool success = false; - - if (request_.type == DqnMemAPI::Type::Alloc) - { - auto const *request = &request_.e.alloc; - - if (request->zero_clear) result = (u8 *)DqnMem_Calloc(request->request_size); - else result = (u8 *)DqnMem_Alloc(request->request_size); - - success = (result != nullptr); - } - else if (request_.type == DqnMemAPI::Type::Realloc) - { - auto const *request = &request_.e.realloc; - if (request->new_size == request->old_size) - { - result = (u8 *)request->old_mem_ptr; - } - else - { - result = (u8 *)DqnMem_Realloc(request->old_mem_ptr, request->new_size); - success = (result != nullptr); - } - } - else if (request_.type == DqnMemAPI::Type::Free) - { - auto *request = &request_.e.free; - DqnMem_Free(request->ptr_to_free); - success = (request->ptr_to_free != nullptr); - } - else - { - DQN_ASSERT(DQN_INVALID_CODE_PATH); - } - - if (success) - { - DqnMemAPI__UpdateAPIStatistics(api, &request_); - } - - return result; -} - -struct DqnMemAPI__DqnMemStackContext -{ - enum Mode - { - PushToHead, - PushToTail, - }; - - DqnMemStack *stack; - Mode mode; -}; - -FILE_SCOPE void * -DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api, DqnMemAPI::Request request_, bool push_to_head) -{ - DqnMemAPI__ValidateRequest(request_); - DQN_ASSERT(request_.user_context); - - DqnMemStack::AllocTo const allocTo = (push_to_head) ? DqnMemStack::AllocTo::Head : DqnMemStack::AllocTo::Tail; - auto *const stack = (DqnMemStack *)(request_.user_context); - void *result = nullptr; - bool success = false; - - auto PtrIsLastAllocationInBlock = - [](DqnMemTracker const *tracker, DqnMemStack::Block const *block, char *ptr) -> bool { - - DqnPtrHeader *header = tracker->PtrToHeader(ptr); - bool result = false; - if (header->alloc_type == 0) - { - char const *ptrEnd = ptr - header->offset_to_src_ptr + tracker->GetAllocationSize(header->alloc_amount, header->alignment); - result = ptrEnd == block->head; - } - else - { - auto *actualPtr = ptr - header->offset_to_src_ptr; - result = actualPtr == block->tail; - } - - return result; - }; - - if (request_.type == DqnMemAPI::Type::Alloc) - { - auto *request = &request_.e.alloc; - result = static_cast(stack->Push(request->request_size, allocTo)); - if (result) - { - success = true; - if (request->zero_clear) DqnMem_Clear(result, 0, request->request_size); - } - } - else if (request_.type == DqnMemAPI::Type::Realloc) - { - // IMPORTANT: This is a _naive_ realloc scheme for stack allocation. - auto *request = &request_.e.realloc; - char *ptr = static_cast(request->old_mem_ptr); - DqnPtrHeader *header = stack->tracker.PtrToHeader(static_cast(request->old_mem_ptr)); - - for (DqnMemStack::Block *block = stack->block; block; block = block->prev_block) - { - DQN_ASSERT(ptr >= block->memory && ptr <= (block->memory + block->size)); - } - - DqnMemStack::Block *const block = stack->block; - isize const oldMemSize = header->alloc_amount; - isize const extraBytesReq = request->new_size - oldMemSize; - u8 alignment = header->alignment; - DQN_ASSERT(extraBytesReq > 0); - - if (PtrIsLastAllocationInBlock(&stack->tracker, block, ptr)) - { - bool enoughSpace = false; - if (header->alloc_type == 0) - { - DQN_ASSERT((block->head + extraBytesReq) >= block->memory); - - enoughSpace = (block->head + extraBytesReq) < block->tail; - if (enoughSpace) - { - stack->Pop(ptr, Dqn::ZeroClear::No); - result = static_cast(stack->Push(request->new_size, DqnMemStack::AllocTo::Head, alignment)); - DQN_ASSERT(stack->block == block && result == request->old_mem_ptr); - success = true; - } - } - else - { - DQN_ASSERT((block->tail - extraBytesReq) < (block->memory + block->size)); - enoughSpace = (block->tail - extraBytesReq) > block->head; - if (enoughSpace) - { - stack->Pop(ptr, Dqn::ZeroClear::No); - result = static_cast(stack->Push(request->new_size, DqnMemStack::AllocTo::Tail, alignment)); - DqnMem_Copy(result, ptr, oldMemSize); - (static_cast(result))[oldMemSize] = 0; - - success = true; - DQN_ASSERT(stack->block == block); - } - } - - if (!enoughSpace) - { - // TODO(doyle): Does realloc need clear to zero flag as well? - // Else, last allocation but not enough space in block. Create a new block and - // copy - DqnMemStack::Block *oldBlock = block; - if (header->alloc_type == 0) - { - result = static_cast(stack->Push(request->new_size, DqnMemStack::AllocTo::Head, alignment)); - } - else - { - result = static_cast(stack->Push(request->new_size, DqnMemStack::AllocTo::Tail, alignment)); - } - - if (result) - { - DQN_ASSERT(stack->block->prev_block == oldBlock); - DQN_ASSERT(stack->block != oldBlock); - DqnMem_Copy(result, ptr, oldMemSize); - - // Switch to old block, pop the ptr and return the new block on top. - auto *new_block = stack->block; - stack->block = oldBlock; - - stack->Pop(ptr, Dqn::ZeroClear::No); - stack->block = new_block; - success = true; - } - } - } - else - { - if (request->new_size < request->old_size) - { - // NOTE: This is questionable behaviour. We don't reclaim data since it's not - // well-defined in a stack allocator. This would cause gaps in memory. - success = false; // Deny updating statistics. - result = ptr; - } - else - { - DQN_LOGE("Lost %$_d, the ptr to realloc is sandwiched between other allocations (LIFO)", oldMemSize); - - if (header->alloc_type == 0) - { - result = (u8 *)stack->Push(request->new_size, DqnMemStack::AllocTo::Head, alignment); - } - else - { - result = (u8 *)stack->Push(request->new_size, DqnMemStack::AllocTo::Tail, alignment); - } - - if (result) - { - success = true; - DqnMem_Copy(result, ptr, oldMemSize); - } - } - } - } - else - { - auto *request = &request_.e.free; - DQN_ASSERT(request_.type == DqnMemAPI::Type::Free); - - DqnMemStack::Block *block = stack->block; - char *ptr = static_cast(request->ptr_to_free); - DqnPtrHeader *header = stack->tracker.PtrToHeader(ptr); - - if (PtrIsLastAllocationInBlock(&stack->tracker, block, ptr)) - { - stack->Pop(ptr, Dqn::ZeroClear::No); - } - else - { - DQN_LOGE("Lost %$_d, the ptr to free is sandwiched between other allocations (LIFO)", header->alloc_amount); - } - } - - // TODO(doyle): Stats. Irrelevant now? - (void)api; - if (success) - { -#if 0 - DqnMemAPI__UpdateAPIStatistics(api, &request_); -#endif - } - - return result; -} - -FILE_SCOPE void *DqnMemAPI__StackAllocatorCallbackPushToHead(DqnMemAPI *api, DqnMemAPI::Request request_) -{ - void *result = DqnMemAPI__StackAllocatorCallback(api, request_, true); - return result; -} - -FILE_SCOPE void *DqnMemAPI__StackAllocatorCallbackPushToTail(DqnMemAPI *api, DqnMemAPI::Request request_) -{ - void *result = DqnMemAPI__StackAllocatorCallback(api, request_, false); - return result; -} - -void *DqnMemAPI::Realloc(void *const old_ptr, isize old_size, isize new_size) -{ - Request request = {}; - request.type = Type::Realloc; - request.user_context = this->user_context; - request.e.realloc.new_size = new_size; - request.e.realloc.old_mem_ptr = old_ptr; - request.e.realloc.old_size = old_size; - - void *result = (void *)this->allocator(this, request); - return result; -} - -void *DqnMemAPI::Alloc(isize size, Dqn::ZeroClear clear) -{ - Request request = {}; - request.type = Type::Alloc; - request.user_context = this->user_context; - request.e.alloc.zero_clear = (clear == Dqn::ZeroClear::Yes) ? true : false; - request.e.alloc.request_size = size; - - void *result = (void *)this->allocator(this, request); - return result; -} - -void DqnMemAPI::Free(void *const ptr_to_free, isize size_to_free) -{ - Request request = {}; - request.type = Type::Free; - request.user_context = this->user_context; - request.e.free.ptr_to_free = ptr_to_free; - request.e.free.size_to_free = size_to_free; - this->allocator(this, request); -} - -DqnMemAPI DqnMemAPI::HeapAllocator() -{ - DqnMemAPI result = {0}; - result.allocator = DqnMemAPI__HeapAllocatorCallback; - result.user_context = nullptr; - return result; -} - -DqnMemAPI DqnMemAPI::StackAllocator(struct DqnMemStack *stack, StackPushType type) -{ - DQN_ASSERT(stack); - DqnMemAPI result = {0}; - result.allocator = (type == StackPushType::Head) - ? DqnMemAPI__StackAllocatorCallbackPushToHead - : DqnMemAPI__StackAllocatorCallbackPushToTail; - result.user_context = stack; - return result; -} - // #DqnMemTracker // ================================================================================================= void DqnMemTracker::Init(bool bounds_guard) @@ -3308,10 +2896,9 @@ void DqnMemTracker::Init(bool bounds_guard) // TODO(doyle): How to handle memory here. if (bounds_guard) { - this->bounds_guard_size = sizeof(HEAD_GUARD_VALUE); - LOCAL_PERSIST DqnMemAPI heap = DqnMemAPI::HeapAllocator(); - this->allocations.mem_api = &heap; - this->allocations.Reserve(128); + this->bounds_guard_size = sizeof(HEAD_GUARD_VALUE); + this->allocations.allocator = DQN_DEFAULT_ALLOCATOR; + this->allocations.Reserve(1024); } else { @@ -3358,10 +2945,12 @@ void DqnMemTracker::CheckAllocations() const // #DqnMemStack // ================================================================================================= DQN_FILE_SCOPE DqnMemStack::Block * -DqnMemStack__AllocateBlock(isize size, Dqn::ZeroClear clear, DqnMemAPI *api) +DqnMemStack__AllocateBlock(isize size, Dqn::ZeroClear clear, DqnAllocator *allocator) { + bool zero = clear == Dqn::ZeroClear::Yes; isize total_size = sizeof(DqnMemStack::Block) + size; - auto *result = static_cast(api->Alloc(total_size, clear)); + auto *result = zero ? static_cast(allocator->Calloc(1, total_size)) + : static_cast(allocator->Malloc(total_size)); DQN_ALWAYS_ASSERTM(result, "Allocated memory block was null"); char *block_offset = reinterpret_cast(result) + sizeof(*result); @@ -3381,23 +2970,22 @@ DqnMemStack::DqnMemStack(void *mem, isize size, Dqn::ZeroClear clear, u32 flags_ char *block_offset = static_cast(mem) + sizeof(*this->block); isize const block_size = size - sizeof(*this->block); - this->block = static_cast(mem); - *this->block = Block(block_offset, block_size); - this->flags = (flags_ | Flag::NonExpandable); + this->block = static_cast(mem); + *this->block = Block(block_offset, block_size); + this->flags = (flags_ | Flag::NonExpandable); bool bounds_guard = Dqn_BitIsSet(this->flags, Flag::BoundsGuard); this->tracker.Init(bounds_guard); } -DqnMemStack::DqnMemStack(isize size, Dqn::ZeroClear clear, u32 flags_, DqnMemAPI *api) +void DqnMemStack::LazyInit(isize size, Dqn::ZeroClear clear, u32 flags_, DqnAllocator *block_allocator_) { DQN_ALWAYS_ASSERTM(size > 0, "%zu <= 0", size); - *this = {}; - - this->block = DqnMemStack__AllocateBlock(size, clear, api); - this->flags = flags_; - this->mem_api = api; - bool bounds_guard = Dqn_BitIsSet(this->flags, Flag::BoundsGuard); + *this = {}; + this->block = DqnMemStack__AllocateBlock(size, clear, block_allocator_); + this->flags = flags_; + this->block_allocator = block_allocator_; + bool bounds_guard = Dqn_BitIsSet(this->flags, Flag::BoundsGuard); this->tracker.Init(bounds_guard); } @@ -3409,17 +2997,17 @@ void *DqnMemStack::Push(isize size, AllocTo allocTo, u8 alignment) if (size == 0) return nullptr; + if (!this->block) + LazyInit(MINIMUM_BLOCK_SIZE, Dqn::ZeroClear::Yes, Flag::All, DQN_DEFAULT_ALLOCATOR); + bool const push_to_head = (allocTo == AllocTo::Head); - isize size_to_alloc = this->tracker.GetAllocationSize(size, alignment); + isize size_to_alloc = this->tracker.GetAllocationSize(size, alignment); // Allocate New Block If Full // ============================================================================================= bool need_new_block = true; - if (this->block) - { - if (push_to_head) need_new_block = ((this->block->head + size_to_alloc) > this->block->tail); - else need_new_block = ((this->block->tail - size_to_alloc) < this->block->head); - } + if (push_to_head) need_new_block = ((this->block->head + size_to_alloc) > this->block->tail); + else need_new_block = ((this->block->tail - size_to_alloc) < this->block->head); if (need_new_block) { @@ -3431,14 +3019,8 @@ void *DqnMemStack::Push(isize size, AllocTo allocTo, u8 alignment) return nullptr; } - if (!this->block && !this->mem_api) // we /ssume this is a zero initialised mem stack - { - this->mem_api = DQN_DEFAULT_HEAP_ALLOCATOR; - this->tracker.Init(Dqn_BitIsSet(this->flags, DqnMemStack::Flag::BoundsGuard)); - } - isize new_block_size = DQN_MAX(size_to_alloc, MINIMUM_BLOCK_SIZE); - Block *new_block = DqnMemStack__AllocateBlock(new_block_size, Dqn::ZeroClear::No, this->mem_api); + Block *new_block = DqnMemStack__AllocateBlock(new_block_size, Dqn::ZeroClear::No, this->block_allocator); new_block->prev_block = this->block; this->block = new_block; } @@ -3570,11 +3152,8 @@ void DqnMemStack::Free() if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard)) this->tracker.allocations.Free(); - if (this->mem_api) - { - while (this->block) - this->FreeLastBlock(); - } + while (this->block_allocator && this->block) + this->FreeLastBlock(); } bool DqnMemStack::FreeMemBlock(DqnMemStack::Block *mem_block) @@ -3582,9 +3161,6 @@ bool DqnMemStack::FreeMemBlock(DqnMemStack::Block *mem_block) if (!mem_block || !this->block) return false; - if (!this->mem_api) - return false; - DqnMemStack::Block **block_ptr = &this->block; while (*block_ptr && (*block_ptr) != mem_block) @@ -3601,7 +3177,8 @@ bool DqnMemStack::FreeMemBlock(DqnMemStack::Block *mem_block) } isize real_size = block_to_free->size + sizeof(DqnMemStack::Block); - this->mem_api->Free(block_to_free, real_size); + (void)real_size; + this->block_allocator->Free(block_to_free); // No more blocks, then last block has been freed if (!this->block) DQN_ASSERT(this->tmp_region_count == 0); @@ -5683,22 +5260,12 @@ DQN_FILE_SCOPE inline u32 Dqn_BitToggle(u32 bits, u32 flag) // #DqnString Impleemntation // ================================================================================================= -int DqnString::VAListLen(char const *fmt, va_list va) -{ - LOCAL_PERSIST char tmp[STB_SPRINTF_MIN]; - auto PrintCallback = [](char *buf, void * /*user*/, int /*len*/) -> char * { return buf; }; - int result = Dqn_vsprintfcb(PrintCallback, nullptr, tmp, fmt, va); - return result; -} - void DqnString::Reserve(int new_max) { if (new_max >= this->max) { - char *new_ptr = - (this->str) - ? static_cast(mem_api->Realloc(this->str, sizeof(this->str[0]) * this->len, new_max * sizeof(this->str[0]))) - : static_cast(mem_api->Alloc(new_max * sizeof(this->str[0]))); + char *new_ptr = (this->str) ? static_cast(allocator->Realloc(this->str, new_max * sizeof(this->str[0]))) + : static_cast(allocator->Malloc(new_max * sizeof(this->str[0]))); DQN_ALWAYS_ASSERT(new_ptr); this->str = new_ptr; @@ -7747,7 +7314,7 @@ static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, c #include // unlink() #endif -#define DQN_FILE__LIST_DIR(name) DQN_FILE_SCOPE char **name(char const *dir, i32 *num_files, DqnMemAPI *api) +#define DQN_FILE__LIST_DIR(name) DQN_FILE_SCOPE char **name(char const *dir, i32 *num_files, DqnAllocator *allocator) // XPlatform > #DqnFile // ================================================================================================= @@ -7856,7 +7423,6 @@ DQN_FILE__LIST_DIR(DqnFile__PlatformListDir) curr_num_files++; } } - FindClose(find_handle); } if (curr_num_files == 0) @@ -7876,7 +7442,7 @@ DQN_FILE__LIST_DIR(DqnFile__PlatformListDir) } DQN_DEFER(FindClose(find_handle)); - char **list = (char **)api->Alloc(sizeof(*list) * (curr_num_files), Dqn::ZeroClear::Yes); + char **list = (char **)allocator->Calloc(1, sizeof(*list) * (curr_num_files)); if (!list) { @@ -7888,11 +7454,11 @@ DQN_FILE__LIST_DIR(DqnFile__PlatformListDir) for (auto i = 0; i < curr_num_files; i++) { // TODO(doyle): Max path is bad. - list[i] = (char *)api->Alloc(sizeof(**list) * MAX_PATH, Dqn::ZeroClear::Yes); + list[i] = (char *)allocator->Calloc(1, sizeof(**list) * MAX_PATH); if (!list[i]) { for (auto j = 0; j < i; j++) - api->Free(list[j]); + allocator->Free(list[j]); DQN_LOGE("Memory allocation failed, required: %$_d", sizeof(**list) * MAX_PATH); *num_files = 0; @@ -8030,7 +7596,7 @@ DQN_FILE__LIST_DIR(DqnFile__PlatformListDir) if (!dir_handle) return nullptr; DQN_DEFER(closedir(dir_handle)); - char **list = (char **)api->Alloc(sizeof(*list) * curr_num_files, Dqn::ZeroClear::Yes); + char **list = (char **)allocator->Calloc(1, sizeof(*list) * curr_num_files); if (!list) { DQN_LOGE("Memory allocation failed, required: %$_d", sizeof(*list) * curr_num_files); @@ -8041,7 +7607,7 @@ DQN_FILE__LIST_DIR(DqnFile__PlatformListDir) struct dirent *dir_file = readdir(dir_handle); for (auto i = 0; i < curr_num_files; i++) { - list[i] = (char *)api->Alloc(sizeof(**list) * DQN_ARRAY_COUNT(dir_file->d_name), Dqn::ZeroClear::Yes); + list[i] = (char *)allocator->Calloc(1, sizeof(**list) * DQN_ARRAY_COUNT(dir_file->d_name)); if (!list[i]) { for (auto j = 0; j < i; j++) api->Free(list[j]); @@ -8158,39 +7724,39 @@ usize DqnFile::Read(u8 *buf, usize num_bytes_to_read) return num_bytes_read; } -u8 *DqnFile_ReadAll(wchar_t const *path, usize *buf_size, DqnMemAPI *api) +u8 *DqnFile_ReadAll(wchar_t const *path, usize *buf_size, DqnAllocator *allocator) { // TODO(doyle): Logging usize required_size = 0; if (!DqnFile_Size(path, &required_size) || required_size == 0) return nullptr; - auto *buf = (u8 *)api->Alloc(required_size, Dqn::ZeroClear::No); + auto *buf = (u8 *)allocator->Malloc(required_size); if (DqnFile_ReadAll(path, buf, required_size)) { *buf_size = required_size; return buf; } - api->Free(buf, required_size); + allocator->Free(buf); return nullptr; } -DQN_FILE_SCOPE u8 *DqnFile_ReadAll(char const *path, usize *buf_size, DqnMemAPI *api) +DQN_FILE_SCOPE u8 *DqnFile_ReadAll(char const *path, usize *buf_size, DqnAllocator *allocator) { // TODO(doyle): Logging usize required_size = 0; if (!DqnFile_Size(path, &required_size) || required_size == 0) return nullptr; - auto *buf = (u8 *)api->Alloc(required_size, Dqn::ZeroClear::No); + auto *buf = (u8 *)allocator->Malloc(required_size); if (DqnFile_ReadAll(path, buf, required_size)) { *buf_size = required_size; return buf; } - api->Free(buf, required_size); + allocator->Free(buf); return nullptr; } @@ -8499,23 +8065,23 @@ bool DqnFile_Copy(wchar_t const *src, wchar_t const *dest) #endif } -char **DqnFile_ListDir(char const *dir, i32 *num_files, DqnMemAPI *api) +char **DqnFile_ListDir(char const *dir, i32 *num_files, DqnAllocator *allocator) { - char **result = DqnFile__PlatformListDir(dir, num_files, api); + char **result = DqnFile__PlatformListDir(dir, num_files, allocator); return result; } -void DqnFile_ListDirFree(char **file_list, i32 num_files, DqnMemAPI *api) +void DqnFile_ListDirFree(char **file_list, i32 num_files, DqnAllocator *allocator) { if (file_list) { for (isize i = 0; i < num_files; i++) { - if (file_list[i]) api->Free(file_list[i]); + if (file_list[i]) allocator->Free(file_list[i]); file_list[i] = nullptr; } - api->Free(file_list); + allocator->Free(file_list); } }