Simplify memory api to dqnallocator

This commit is contained in:
Doyle 2018-08-30 01:04:56 +10:00
parent 3501dd41c1
commit a0b64be71b
3 changed files with 117 additions and 936 deletions

View File

@ -228,9 +228,8 @@ FILE_SCOPE void DqnMemStack_Test()
{ {
usize size = 32; usize size = 32;
usize additional_size = DqnMemStack::MINIMUM_BLOCK_SIZE; 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; auto *block1 = stack.block;
size += additional_size; size += additional_size;
@ -271,7 +270,6 @@ FILE_SCOPE void DqnMemStack_Test()
DQN_ASSERT(block1->prev_block == nullptr); DQN_ASSERT(block1->prev_block == nullptr);
stack.Free(); stack.Free();
DQN_ASSERT(stack.mem_api->bytes_allocated == 0);
DQN_ASSERT(stack.block == nullptr); DQN_ASSERT(stack.block == nullptr);
Log(Status::Ok, "Check freeing arbitrary blocks and freeing"); Log(Status::Ok, "Check freeing arbitrary blocks and freeing");
} }

View File

@ -1244,383 +1244,6 @@ void DqnRect_Test()
} }
} }
void DqnArray_TestInternal(DqnMemAPI *const mem_api)
{
if (1)
{
DqnArray<DqnV2> 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<i32> 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<i32> 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<i32> 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<i32> 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<i32> 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<i32> 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<i32> 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<i32> 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<char> *array) void DqnArray_TestRealDataInternal(DqnArray<char> *array)
{ {
(void)array; (void)array;
@ -1646,12 +1269,6 @@ void DqnArray_TestRealDataInternal(DqnArray<char> *array)
void DqnArray_Test() void DqnArray_Test()
{ {
LOG_HEADER(); LOG_HEADER();
if (1)
{
auto allocator = DqnMemAPI::HeapAllocator();
DqnArray_TestInternal(&allocator);
}
if (1) if (1)
{ {
if (1) if (1)

622
dqn.h
View File

@ -32,7 +32,7 @@
// #DqnRnd Random Number Generator (ints and floats) // #DqnRnd Random Number Generator (ints and floats)
// #Dqn_* Utility code, (qsort, quick file reading) // #Dqn_* Utility code, (qsort, quick file reading)
// #DqnMem Memory Allocation // #DqnMem Memory Allocation
// #DqnMemAPI Custom memory API for Dqn Data Structures // #DqnAllocator Custom memory API for Dqn Data Structures
// #DqnPool Pool objects // #DqnPool Pool objects
// #DqnArray Dynamic Array using Templates // #DqnArray Dynamic Array using Templates
// #DqnMemStack Memory Allocator, Push, Pop Style // #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 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); 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 // A generic structure for managing memory allocations for different Dqn data structures.
// structures! Otherwise it already has a default one to use. using DqnMallocProc = void *(size_t size);
using DqnFreeProc = void (void *ptr);
// How To Use: using DqnCallocProc = void *(size_t num_items, size_t size);
// 1. Implement the allocator function, where DqnMemAPI::Request will tell you the request. using DqnReallocProc = void *(void *ptr, size_t size);
// - (NOTE) The callback should return the resulting data into DqnMemAPI::Result struct DqnAllocator
// 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
{ {
public: DqnMallocProc *Malloc;
enum struct Type DqnCallocProc *Calloc;
{ DqnReallocProc *Realloc;
Uninitialised, DqnFreeProc *Free;
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); }
}; };
DqnAllocator DQN_DEFAULT_ALLOCATOR_ = {malloc, calloc, realloc, free};
DqnAllocator *DQN_DEFAULT_ALLOCATOR = &DQN_DEFAULT_ALLOCATOR_;
// #DqnPool API // #DqnPool API
// ================================================================================================= // =================================================================================================
template <typename T, i16 SIZE> template <typename T, i16 SIZE>
@ -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 // #DqnArray API
// ================================================================================================= // =================================================================================================
template<typename T> template<typename T>
struct DqnArray struct DqnArray
{ {
DqnMemAPI *mem_api = DQN_DEFAULT_HEAP_ALLOCATOR; DqnAllocator *allocator = DQN_DEFAULT_ALLOCATOR;
isize len; isize len;
isize max; isize max;
T *data; T *data;
DqnArray () = default; DqnArray () = default;
DqnArray (DqnMemAPI *mem_api_) { *this = {}; this->mem_api = mem_api_; } DqnArray (DqnAllocator *allocator) { *this = {}; this->allocator = allocator; }
// ~DqnArray () { if (this->data && this->mem_api) this->mem_api->Free(data); } // ~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 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 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 = {}; } void Free () { if (data) { allocator->Free(data); } *this = {}; }
T *Front () { DQN_ASSERT(len > 0); return data + 0; } T *Front () { DQN_ASSERT(len > 0); return data + 0; }
T *Back () { DQN_ASSERT(len > 0); return data + (len - 1); } 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) { 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 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); void Reserve (isize new_max);
T *Make (isize len = 1) { len += len; if (len > max) Reserve(GrowCapacity_(len)); return &data[len - len]; } 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) { if (len + 1 > max) Reserve(GrowCapacity_(len + 1)); data[len++] = v; return data + (len-1); }
T *Push (T const *v, isize len_items = 1) { return Insert(len, v, len_items); } 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 Pop () { if (len > 0) len--; }
void Erase (isize index) { DQN_ASSERT(index >= 0 && index < len); data[index] = data[--len]; } void Erase (isize index) { DQN_ASSERT(index >= 0 && index < len); data[index] = data[--len]; }
void EraseStable(isize index); void EraseStable(isize index);
@ -1406,13 +1342,13 @@ template <typename T> void DqnArray<T>::Reserve(isize new_max)
if (data) 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; data = newData;
max = new_max; max = new_max;
} }
else else
{ {
data = (T *)mem_api->Alloc(new_max * sizeof(T)); data = (T *)allocator->Malloc(new_max * sizeof(T));
max = new_max; max = new_max;
} }
@ -1518,7 +1454,7 @@ struct DqnMemStack
}; };
DqnMemTracker tracker; // Read: Metadata for managing ptr allocation 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 Block *block; // Read: Memory block allocated for the stack
u32 flags; // Read u32 flags; // Read
i32 tmp_region_count;// Read: The number of temp memory regions in use 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); 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. // 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 // Allocation API
// ============================================================================================= // =============================================================================================
enum struct AllocTo enum struct AllocTo { Head, Tail };
{
Head, Tail
};
// Allocate memory from the MemStack. // Allocate memory from the MemStack.
// alignment: Ptr returned from allocator is aligned to this value and MUST be power of 2. // 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. // Keep allocations that have occurred since Begin(). End() does not need to be called anymore.
void TempRegionKeepChanges(TempRegion region); 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 // #DqnHash API
@ -1851,13 +1795,13 @@ struct DqnRect
// ================================================================================================= // =================================================================================================
struct DqnString struct DqnString
{ {
DqnMemAPI *mem_api = DQN_DEFAULT_HEAP_ALLOCATOR; DqnAllocator *allocator = DQN_DEFAULT_ALLOCATOR;
int len = 0; int len = 0;
int max = 0; int max = 0;
char *str = nullptr; char *str = nullptr;
DqnString() = default; 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_) { Append(str_); }
DqnString(char const *str_, int len_) { Append(str_, len_); } DqnString(char const *str_, int len_) { Append(str_, len_); }
DqnString(DqnSlice<char const> const &other) { Append(other.data, other.len); } DqnString(DqnSlice<char const> 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 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 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 Resize (int new_max) { if (new_max > max) Reserve(new_max); len = DQN_MIN(new_max, len); NullTerminate(); }
void Reserve (int new_max); void Reserve (int new_max);
void Append (char const *src, int len_ = -1); 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, 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<char const> 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<char const> 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<char> 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<char> 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 // 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; i32 ToWChar(wchar_t *const buf, i32 const buf_size) const;
// return: String allocated using api. // 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 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 = {}; } 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<int>((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<int>((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>; using DqnFixedString16 = DqnFixedString<16>;
@ -2077,16 +2028,16 @@ struct DqnVArray
DqnVArray () = default; // Zero is initialisation DqnVArray () = default; // Zero is initialisation
DqnVArray (isize size) { LazyInit(size); } 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); } 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); } // ~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 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 = {}; } void Free () { if (data) { DqnOS_VFree(data, sizeof(T) * max); } *this = {}; }
T *Front () { return (len > 0) ? (data + 0) : nullptr; } T *Front () { return (len > 0) ? (data + 0) : nullptr; }
T *Back () { return (len > 0) ? (data + (len - 1)) : 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 *Make (isize num = 1) { if (!data) 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) { data[len++] = v; return data + (len - 1); }
T *Push (T const *v, isize num_items = 1) { return Insert(len, v, num_items); } 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 Pop () { if (len > 0) len--; }
void Erase (isize index) { if (!data) return; DQN_ASSERT(index >= 0 && index < len); data[index] = data[--len]; } void Erase (isize index) { if (!data) return; DQN_ASSERT(index >= 0 && index < len); data[index] = data[--len]; }
void EraseStable(isize index); void EraseStable(isize index);
@ -2102,7 +2053,7 @@ struct DqnVArray
template<typename T> T *DqnVArray<T>::Insert(isize index, T const *v, isize num_items) template<typename T> T *DqnVArray<T>::Insert(isize index, T const *v, isize num_items)
{ {
LazyInit(1024); if (!data) LazyInit(1024);
index = DQN_CLAMP(index, 0, len); index = DQN_CLAMP(index, 0, len);
isize const new_len = len + num_items; 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. // Buffer is null-terminated and should be freed when done with.
// return: False if file access failure OR nullptr arguments. // 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(char const *path, usize *buf_size, DqnAllocator *allocator = DQN_DEFAULT_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(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(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); 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. // 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 // 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() // 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 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, DqnMemAPI *api = DQN_DEFAULT_HEAP_ALLOCATOR); DQN_FILE_SCOPE void DqnFile_ListDirFree (char **file_list, i32 num_files, DqnAllocator *allocator = DQN_DEFAULT_ALLOCATOR);
// XPlatform > #DqnCatalog API // 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]); 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<char *>(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<char *>(request->old_mem_ptr);
DqnPtrHeader *header = stack->tracker.PtrToHeader(static_cast<char *>(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<char *>(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<char *>(stack->Push(request->new_size, DqnMemStack::AllocTo::Tail, alignment));
DqnMem_Copy(result, ptr, oldMemSize);
(static_cast<char *>(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<char *>(stack->Push(request->new_size, DqnMemStack::AllocTo::Head, alignment));
}
else
{
result = static_cast<char *>(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<char *>(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 // #DqnMemTracker
// ================================================================================================= // =================================================================================================
void DqnMemTracker::Init(bool bounds_guard) void DqnMemTracker::Init(bool bounds_guard)
@ -3309,9 +2897,8 @@ void DqnMemTracker::Init(bool bounds_guard)
if (bounds_guard) if (bounds_guard)
{ {
this->bounds_guard_size = sizeof(HEAD_GUARD_VALUE); this->bounds_guard_size = sizeof(HEAD_GUARD_VALUE);
LOCAL_PERSIST DqnMemAPI heap = DqnMemAPI::HeapAllocator(); this->allocations.allocator = DQN_DEFAULT_ALLOCATOR;
this->allocations.mem_api = &heap; this->allocations.Reserve(1024);
this->allocations.Reserve(128);
} }
else else
{ {
@ -3358,10 +2945,12 @@ void DqnMemTracker::CheckAllocations() const
// #DqnMemStack // #DqnMemStack
// ================================================================================================= // =================================================================================================
DQN_FILE_SCOPE DqnMemStack::Block * 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; isize total_size = sizeof(DqnMemStack::Block) + size;
auto *result = static_cast<DqnMemStack::Block *>(api->Alloc(total_size, clear)); auto *result = zero ? static_cast<DqnMemStack::Block *>(allocator->Calloc(1, total_size))
: static_cast<DqnMemStack::Block *>(allocator->Malloc(total_size));
DQN_ALWAYS_ASSERTM(result, "Allocated memory block was null"); DQN_ALWAYS_ASSERTM(result, "Allocated memory block was null");
char *block_offset = reinterpret_cast<char *>(result) + sizeof(*result); char *block_offset = reinterpret_cast<char *>(result) + sizeof(*result);
@ -3389,14 +2978,13 @@ DqnMemStack::DqnMemStack(void *mem, isize size, Dqn::ZeroClear clear, u32 flags_
this->tracker.Init(bounds_guard); 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); DQN_ALWAYS_ASSERTM(size > 0, "%zu <= 0", size);
*this = {}; *this = {};
this->block = DqnMemStack__AllocateBlock(size, clear, block_allocator_);
this->block = DqnMemStack__AllocateBlock(size, clear, api);
this->flags = flags_; this->flags = flags_;
this->mem_api = api; this->block_allocator = block_allocator_;
bool bounds_guard = Dqn_BitIsSet(this->flags, Flag::BoundsGuard); bool bounds_guard = Dqn_BitIsSet(this->flags, Flag::BoundsGuard);
this->tracker.Init(bounds_guard); this->tracker.Init(bounds_guard);
} }
@ -3409,17 +2997,17 @@ void *DqnMemStack::Push(isize size, AllocTo allocTo, u8 alignment)
if (size == 0) if (size == 0)
return nullptr; 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); 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 // Allocate New Block If Full
// ============================================================================================= // =============================================================================================
bool need_new_block = true; bool need_new_block = true;
if (this->block)
{
if (push_to_head) need_new_block = ((this->block->head + size_to_alloc) > this->block->tail); 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); else need_new_block = ((this->block->tail - size_to_alloc) < this->block->head);
}
if (need_new_block) if (need_new_block)
{ {
@ -3431,14 +3019,8 @@ void *DqnMemStack::Push(isize size, AllocTo allocTo, u8 alignment)
return nullptr; 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); 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; new_block->prev_block = this->block;
this->block = new_block; this->block = new_block;
} }
@ -3570,11 +3152,8 @@ void DqnMemStack::Free()
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard)) if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
this->tracker.allocations.Free(); this->tracker.allocations.Free();
if (this->mem_api) while (this->block_allocator && this->block)
{
while (this->block)
this->FreeLastBlock(); this->FreeLastBlock();
}
} }
bool DqnMemStack::FreeMemBlock(DqnMemStack::Block *mem_block) bool DqnMemStack::FreeMemBlock(DqnMemStack::Block *mem_block)
@ -3582,9 +3161,6 @@ bool DqnMemStack::FreeMemBlock(DqnMemStack::Block *mem_block)
if (!mem_block || !this->block) if (!mem_block || !this->block)
return false; return false;
if (!this->mem_api)
return false;
DqnMemStack::Block **block_ptr = &this->block; DqnMemStack::Block **block_ptr = &this->block;
while (*block_ptr && (*block_ptr) != mem_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); 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 // No more blocks, then last block has been freed
if (!this->block) DQN_ASSERT(this->tmp_region_count == 0); 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 // #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) void DqnString::Reserve(int new_max)
{ {
if (new_max >= this->max) if (new_max >= this->max)
{ {
char *new_ptr = char *new_ptr = (this->str) ? static_cast<char *>(allocator->Realloc(this->str, new_max * sizeof(this->str[0])))
(this->str) : static_cast<char *>(allocator->Malloc(new_max * sizeof(this->str[0])));
? static_cast<char *>(mem_api->Realloc(this->str, sizeof(this->str[0]) * this->len, new_max * sizeof(this->str[0])))
: static_cast<char *>(mem_api->Alloc(new_max * sizeof(this->str[0])));
DQN_ALWAYS_ASSERT(new_ptr); DQN_ALWAYS_ASSERT(new_ptr);
this->str = 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 <unistd.h> // unlink() #include <unistd.h> // unlink()
#endif #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 // XPlatform > #DqnFile
// ================================================================================================= // =================================================================================================
@ -7856,7 +7423,6 @@ DQN_FILE__LIST_DIR(DqnFile__PlatformListDir)
curr_num_files++; curr_num_files++;
} }
} }
FindClose(find_handle);
} }
if (curr_num_files == 0) if (curr_num_files == 0)
@ -7876,7 +7442,7 @@ DQN_FILE__LIST_DIR(DqnFile__PlatformListDir)
} }
DQN_DEFER(FindClose(find_handle)); 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) if (!list)
{ {
@ -7888,11 +7454,11 @@ DQN_FILE__LIST_DIR(DqnFile__PlatformListDir)
for (auto i = 0; i < curr_num_files; i++) for (auto i = 0; i < curr_num_files; i++)
{ {
// TODO(doyle): Max path is bad. // 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]) if (!list[i])
{ {
for (auto j = 0; j < i; j++) 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); DQN_LOGE("Memory allocation failed, required: %$_d", sizeof(**list) * MAX_PATH);
*num_files = 0; *num_files = 0;
@ -8030,7 +7596,7 @@ DQN_FILE__LIST_DIR(DqnFile__PlatformListDir)
if (!dir_handle) return nullptr; if (!dir_handle) return nullptr;
DQN_DEFER(closedir(dir_handle)); 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) if (!list)
{ {
DQN_LOGE("Memory allocation failed, required: %$_d", sizeof(*list) * curr_num_files); 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); struct dirent *dir_file = readdir(dir_handle);
for (auto i = 0; i < curr_num_files; i++) 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]) if (!list[i])
{ {
for (auto j = 0; j < i; j++) api->Free(list[j]); 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; 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 // TODO(doyle): Logging
usize required_size = 0; usize required_size = 0;
if (!DqnFile_Size(path, &required_size) || required_size == 0) if (!DqnFile_Size(path, &required_size) || required_size == 0)
return nullptr; 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)) if (DqnFile_ReadAll(path, buf, required_size))
{ {
*buf_size = required_size; *buf_size = required_size;
return buf; return buf;
} }
api->Free(buf, required_size); allocator->Free(buf);
return nullptr; 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 // TODO(doyle): Logging
usize required_size = 0; usize required_size = 0;
if (!DqnFile_Size(path, &required_size) || required_size == 0) if (!DqnFile_Size(path, &required_size) || required_size == 0)
return nullptr; 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)) if (DqnFile_ReadAll(path, buf, required_size))
{ {
*buf_size = required_size; *buf_size = required_size;
return buf; return buf;
} }
api->Free(buf, required_size); allocator->Free(buf);
return nullptr; return nullptr;
} }
@ -8499,23 +8065,23 @@ bool DqnFile_Copy(wchar_t const *src, wchar_t const *dest)
#endif #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; 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) if (file_list)
{ {
for (isize i = 0; i < num_files; i++) 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; file_list[i] = nullptr;
} }
api->Free(file_list); allocator->Free(file_list);
} }
} }