Simplify memory api to dqnallocator
This commit is contained in:
parent
3501dd41c1
commit
a0b64be71b
@ -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");
|
||||
}
|
||||
|
383
DqnUnitTest.cpp
383
DqnUnitTest.cpp
@ -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)array;
|
||||
@ -1646,12 +1269,6 @@ void DqnArray_TestRealDataInternal(DqnArray<char> *array)
|
||||
void DqnArray_Test()
|
||||
{
|
||||
LOG_HEADER();
|
||||
if (1)
|
||||
{
|
||||
auto allocator = DqnMemAPI::HeapAllocator();
|
||||
DqnArray_TestInternal(&allocator);
|
||||
}
|
||||
|
||||
if (1)
|
||||
{
|
||||
if (1)
|
||||
|
622
dqn.h
622
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 <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
|
||||
// =================================================================================================
|
||||
template<typename T>
|
||||
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 (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 = {}; }
|
||||
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 <typename T> void DqnArray<T>::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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
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<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 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<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); }
|
||||
@ -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<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>;
|
||||
@ -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); }
|
||||
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<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);
|
||||
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<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
|
||||
// =================================================================================================
|
||||
void DqnMemTracker::Init(bool bounds_guard)
|
||||
@ -3309,9 +2897,8 @@ void DqnMemTracker::Init(bool bounds_guard)
|
||||
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->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<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");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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->block = DqnMemStack__AllocateBlock(size, clear, block_allocator_);
|
||||
this->flags = flags_;
|
||||
this->mem_api = api;
|
||||
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);
|
||||
|
||||
// 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 (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)
|
||||
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<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])));
|
||||
char *new_ptr = (this->str) ? static_cast<char *>(allocator->Realloc(this->str, new_max * sizeof(this->str[0])))
|
||||
: static_cast<char *>(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 <unistd.h> // 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user