Start reworking dqnmemstack to have unified push/pop interface

This commit is contained in:
Doyle T 2018-07-18 01:53:58 +10:00
parent 0bfca792ee
commit efe015017a
2 changed files with 138 additions and 227 deletions

View File

@ -2333,10 +2333,10 @@ FILE_SCOPE void DqnMemStack_Test()
i32 const ALIGN64 = 64; i32 const ALIGN64 = 64;
i32 const ALIGN16 = 16; i32 const ALIGN16 = 16;
i32 const ALIGN4 = 4; i32 const ALIGN4 = 4;
DqnMemStack::AllocTo allocTo = DqnMemStack::AllocTo::Head;
if (1) if (1)
{ {
u8 *result1 = (u8 *)stack.Push(2, ALIGN4); u8 *result1 = (u8 *)stack.Push(2, allocTo, ALIGN4);
u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN4); u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN4);
DQN_ASSERT(result1 == result2); DQN_ASSERT(result1 == result2);
stack.Pop(result1); stack.Pop(result1);
@ -2344,7 +2344,7 @@ FILE_SCOPE void DqnMemStack_Test()
if (1) if (1)
{ {
u8 *result1 = (u8 *)stack.Push(120, ALIGN16); u8 *result1 = (u8 *)stack.Push(120, allocTo, ALIGN16);
u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN16); u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN16);
DQN_ASSERT(result1 == result2); DQN_ASSERT(result1 == result2);
stack.Pop(result1); stack.Pop(result1);
@ -2352,7 +2352,7 @@ FILE_SCOPE void DqnMemStack_Test()
if (1) if (1)
{ {
u8 *result1 = (u8 *)stack.Push(12, ALIGN64); u8 *result1 = (u8 *)stack.Push(12, allocTo, ALIGN64);
u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN64); u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN64);
DQN_ASSERT(result1 == result2); DQN_ASSERT(result1 == result2);
stack.Pop(result1); stack.Pop(result1);
@ -2378,7 +2378,7 @@ FILE_SCOPE void DqnMemStack_Test()
if (1) if (1)
{ {
auto stack = DqnMemStack(DQN_MEGABYTE(1), Dqn::ZeroClear::Yes); auto stack = DqnMemStack(DQN_MEGABYTE(1), Dqn::ZeroClear::Yes);
DQN_ASSERT(stack.tracker.GetBoundsGuardSize() == 0); DQN_ASSERT(stack.tracker.boundsGuardSize == 0);
auto *oldBlock = stack.block; auto *oldBlock = stack.block;
DQN_ASSERT(oldBlock); DQN_ASSERT(oldBlock);
@ -2471,7 +2471,7 @@ FILE_SCOPE void DqnMemStack_Test()
auto stack = DqnMemStack(DQN_MEGABYTE(1), Dqn::ZeroClear::Yes, DqnMemStack::Flag::BoundsGuard); auto stack = DqnMemStack(DQN_MEGABYTE(1), Dqn::ZeroClear::Yes, DqnMemStack::Flag::BoundsGuard);
auto *pop1 = stack.Push(222); auto *pop1 = stack.Push(222);
auto *pop2 = stack.PushOnTail(333); auto *pop2 = stack.Push(333, DqnMemStack::AllocTo::Tail);
DqnMemStack::Block *blockToReturnTo = stack.block; DqnMemStack::Block *blockToReturnTo = stack.block;
auto headBefore = blockToReturnTo->head; auto headBefore = blockToReturnTo->head;
@ -2480,9 +2480,9 @@ FILE_SCOPE void DqnMemStack_Test()
{ {
auto memGuard1 = stack.TempRegionGuard(); auto memGuard1 = stack.TempRegionGuard();
auto *result2 = stack.Push(100); auto *result2 = stack.Push(100);
auto *result3 = stack.PushOnTail(100); auto *result3 = stack.Push(100, DqnMemStack::AllocTo::Tail);
auto *result4 = stack.Push(100); auto *result4 = stack.Push(100);
auto *result5 = stack.PushOnTail(100); auto *result5 = stack.Push(100, DqnMemStack::AllocTo::Tail);
DQN_ASSERT(result2 && result3 && result4 && result5); DQN_ASSERT(result2 && result3 && result4 && result5);
DQN_ASSERT(result3 > result5); DQN_ASSERT(result3 > result5);
DQN_ASSERT(result2 < result4); DQN_ASSERT(result2 < result4);
@ -2502,7 +2502,7 @@ FILE_SCOPE void DqnMemStack_Test()
DQN_ASSERT(stack.block->tail == tailBefore); DQN_ASSERT(stack.block->tail == tailBefore);
stack.Pop(pop1); stack.Pop(pop1);
stack.PopOnTail(pop2); stack.Pop(pop2);
DQN_ASSERT(stack.block->head == stack.block->memory); DQN_ASSERT(stack.block->head == stack.block->memory);
DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size); DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size);
@ -2603,8 +2603,8 @@ FILE_SCOPE void DqnMemStack_Test()
char *result = static_cast<char *>(stack.Push(64)); char *result = static_cast<char *>(stack.Push(64));
// TODO(doyle): check head and tail are adjacent to the bounds of the allocation // TODO(doyle): check head and tail are adjacent to the bounds of the allocation
u32 *head = stack.tracker.PtrToHeadBoundsGuard(result); u32 *head = stack.tracker.PtrToHeadGuard(result);
u32 *tail = stack.tracker.PtrToTailBoundsGuard(result); u32 *tail = stack.tracker.PtrToTailGuard(result);
DQN_ASSERT(*head == DqnMemTracker::HEAD_GUARD_VALUE); DQN_ASSERT(*head == DqnMemTracker::HEAD_GUARD_VALUE);
DQN_ASSERT(*tail == DqnMemTracker::TAIL_GUARD_VALUE); DQN_ASSERT(*tail == DqnMemTracker::TAIL_GUARD_VALUE);
@ -2620,13 +2620,13 @@ FILE_SCOPE void DqnMemStack_Test()
DqnMemStack stack = DqnMemStack(DQN_MEGABYTE(1), Dqn::ZeroClear::Yes, DqnMemStack::Flag::BoundsGuard); DqnMemStack stack = DqnMemStack(DQN_MEGABYTE(1), Dqn::ZeroClear::Yes, DqnMemStack::Flag::BoundsGuard);
auto *result1 = stack.Push(100); auto *result1 = stack.Push(100);
auto *result2 = stack.PushOnTail(100); auto *result2 = stack.Push(100, DqnMemStack::AllocTo::Tail);
auto *headBefore = stack.block->head; auto *headBefore = stack.block->head;
auto *tailBefore = stack.block->tail; auto *tailBefore = stack.block->tail;
DQN_ASSERT(result2 && result1); DQN_ASSERT(result2 && result1);
DQN_ASSERT(result2 != result1 && result1 < result2); DQN_ASSERT(result2 != result1 && result1 < result2);
stack.PopOnTail(result2); stack.Pop(result2);
DQN_ASSERT(headBefore == stack.block->head) DQN_ASSERT(headBefore == stack.block->head)
DQN_ASSERT(tailBefore != stack.block->tail) DQN_ASSERT(tailBefore != stack.block->tail)
@ -2652,7 +2652,7 @@ FILE_SCOPE void DqnMemStack_Test()
DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size); DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size);
auto *blockBefore = stack.block; auto *blockBefore = stack.block;
auto *result2 = stack.PushOnTail(DQN_MEGABYTE(1)); auto *result2 = stack.Push(DQN_MEGABYTE(1), DqnMemStack::AllocTo::Tail);
DQN_ASSERT(result2 && result1); DQN_ASSERT(result2 && result1);
DQN_ASSERT(result2 != result1); DQN_ASSERT(result2 != result1);
DQN_ASSERT(stack.block->prevBlock == blockBefore); DQN_ASSERT(stack.block->prevBlock == blockBefore);
@ -2662,7 +2662,7 @@ FILE_SCOPE void DqnMemStack_Test()
DQN_ASSERT(stack.block->tail < stack.block->memory + stack.block->size && DQN_ASSERT(stack.block->tail < stack.block->memory + stack.block->size &&
stack.block->tail >= stack.block->head); stack.block->tail >= stack.block->head);
stack.PopOnTail(result2); stack.Pop(result2);
DQN_ASSERT(blockBefore == stack.block); DQN_ASSERT(blockBefore == stack.block);
stack.Pop(result1); stack.Pop(result1);
@ -2682,14 +2682,14 @@ FILE_SCOPE void DqnMemStack_Test()
DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size); DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size);
auto *blockBefore = stack.block; auto *blockBefore = stack.block;
auto *result2 = stack.PushOnTail(DQN_MEGABYTE(1)); auto *result2 = stack.Push(DQN_MEGABYTE(1), DqnMemStack::AllocTo::Tail);
DQN_ASSERT(result2 == nullptr); DQN_ASSERT(result2 == nullptr);
DQN_ASSERT(stack.block->prevBlock == nullptr); DQN_ASSERT(stack.block->prevBlock == nullptr);
DQN_ASSERT(stack.block == blockBefore); DQN_ASSERT(stack.block == blockBefore);
DQN_ASSERT(stack.block->head > stack.block->memory && stack.block->head < stack.block->tail); DQN_ASSERT(stack.block->head > stack.block->memory && stack.block->head < stack.block->tail);
DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size); DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size);
stack.PopOnTail(result2); stack.Pop(result2);
DQN_ASSERT(blockBefore == stack.block); DQN_ASSERT(blockBefore == stack.block);
stack.Pop(result1); stack.Pop(result1);
@ -2748,7 +2748,7 @@ FILE_SCOPE void DqnMemStack_Test()
auto *tailBefore = stack.block->tail; auto *tailBefore = stack.block->tail;
isize bufSize = 16; isize bufSize = 16;
char *buf = (char *)stack.PushOnTail(bufSize); char *buf = (char *)stack.Push(bufSize, DqnMemStack::AllocTo::Tail);
DqnMem_Set(buf, 'X', bufSize); DqnMem_Set(buf, 'X', bufSize);
for (auto i = 0; i < bufSize; i++) DQN_ASSERT(buf[i] == 'X'); for (auto i = 0; i < bufSize; i++) DQN_ASSERT(buf[i] == 'X');
@ -2760,7 +2760,7 @@ FILE_SCOPE void DqnMemStack_Test()
DQN_ASSERT(blockBefore == stack.block); DQN_ASSERT(blockBefore == stack.block);
DQN_ASSERT(tailBefore > stack.block->tail); DQN_ASSERT(tailBefore > stack.block->tail);
stack.PopOnTail(buf); stack.Pop(buf);
DQN_ASSERT(blockBefore == stack.block); DQN_ASSERT(blockBefore == stack.block);
DQN_ASSERT(tailBefore == stack.block->tail); DQN_ASSERT(tailBefore == stack.block->tail);
@ -2812,7 +2812,7 @@ FILE_SCOPE void DqnMemStack_Test()
auto *tailBefore = stack.block->tail; auto *tailBefore = stack.block->tail;
isize bufSize = 16; isize bufSize = 16;
char *buf = (char *)stack.PushOnTail(bufSize); char *buf = (char *)stack.Push(bufSize, DqnMemStack::AllocTo::Tail);
DqnMem_Set(buf, 'X', bufSize); DqnMem_Set(buf, 'X', bufSize);
for (auto i = 0; i < bufSize; i++) DQN_ASSERT(buf[i] == 'X'); for (auto i = 0; i < bufSize; i++) DQN_ASSERT(buf[i] == 'X');
@ -2825,7 +2825,7 @@ FILE_SCOPE void DqnMemStack_Test()
DQN_ASSERT(blockBefore != stack.block); DQN_ASSERT(blockBefore != stack.block);
DQN_ASSERT(blockBefore == stack.block->prevBlock); DQN_ASSERT(blockBefore == stack.block->prevBlock);
stack.PopOnTail(buf); stack.Pop(buf);
DQN_ASSERT(blockBefore == stack.block); DQN_ASSERT(blockBefore == stack.block);
DQN_ASSERT(tailBefore == stack.block->tail); DQN_ASSERT(tailBefore == stack.block->tail);

305
dqn.h
View File

@ -1413,13 +1413,14 @@ template <typename T> void DqnArray<T>::Reserve(isize newMax)
// #DqnMemTracker // #DqnMemTracker
// ================================================================================================= // =================================================================================================
// Allocation Layout // Allocation Layout
// +------------------------------------------------------------+ +-----------------+ // +-------------------------------------------------------------------------+ +-----------------+
// | Allocation Head | | Allocation Tail | // | Allocation Head | | Allocation Tail |
// +--------------------+------------------------------------------------------------+------------------------+-----------------+ // +--------------------+-------------------------------------------------------------------------------------+-----------------------------------+
// | Ptr From Allocator | Offset To Src | Alignment | Alloc Amount | B. Guard (Opt.) | Aligned Ptr For Client | B. Guard (Opt.) | // | Ptr From Allocator | Offset To Src | Alignment | Alloc Type | Alloc Amount | B. Guard (Opt.) | Aligned Ptr For Client | B. Guard (Opt.) |
// +----------------------------------------------------------------------------------------------------------------------------+ // +----------------------------------------------------------------------------------------------------------------------------------------------+
// Ptr From Allocator: The pointer returned by the allocator, not aligned // Ptr From Allocator: The pointer returned by the allocator, not aligned
// Alignment: The pointer given to the client is aligned to this power of two boundary // Alignment: The pointer given to the client is aligned to this power of two boundary
// Alloc Type: If the allocation was allocated from the head or tail of the memory block (mainly for memstacks).
// Alloc Amount: The requested allocation amount by the client (so does not include metadata) // Alloc Amount: The requested allocation amount by the client (so does not include metadata)
// B. Guard: Bounds Guard value. // B. Guard: Bounds Guard value.
// Aligned Ptr For Client: The allocated memory for the client. // Aligned Ptr For Client: The allocated memory for the client.
@ -1430,32 +1431,33 @@ struct DqnMemTracker
static u32 const TAIL_GUARD_VALUE = 0xDEADBEEF; static u32 const TAIL_GUARD_VALUE = 0xDEADBEEF;
static u32 const OFFSET_TO_SRC_SIZE = sizeof(u8); static u32 const OFFSET_TO_SRC_SIZE = sizeof(u8);
static u32 const ALIGNMENT_SIZE = sizeof(u8); static u32 const ALIGNMENT_SIZE = sizeof(u8);
static u32 const ALLOC_TYPE_SIZE = sizeof(u8);
static u32 const ALLOC_AMOUNT_SIZE = sizeof(usize); static u32 const ALLOC_AMOUNT_SIZE = sizeof(usize);
DqnArray<void *> allocations; // When BoundsGuard is enabled, tracks all allocations. DqnArray<void *> allocations; // Read: When BoundsGuard is enabled, tracks all allocations.
u32 boundsGuardSize; // Read: sizeof(GUARD_VALUE) OR 0 if BoundsGuard is disabled.
u32 allocHeadSize; // Read: The size of all the metadata at the head of the allocated ptr i.e. (offset, alignment ... bounds guard)
u32 allocTailSize; // Read: The size of all the metadata at the end of the allocated ptr i.e. (bounds guard)
void Init (bool boundsGuard); void Init (bool boundsGuard);
void Free (); void Free () { allocations.Free(); }
void AddAllocation (char *ptr) { DQN_ASSERT(allocations.Push(ptr) != nullptr); } void AddAllocation (char *ptr) { DQN_ASSERT(allocations.Push(ptr) != nullptr); }
void RemoveAllocation (char *ptr); void RemoveAllocation (char *ptr);
void CheckAllocations () const; void CheckAllocations () const;
isize GetAllocationSize (isize size, u8 alignment) const { return allocHeadSize + size + allocTailSize + (alignment - 1); }
auto GetBoundsGuardSize () const { return boundsGuardSize; } // ptr: The ptr given to the client when allocating.
auto GetAllocHeadSize () const { return allocHeadSize; } u32 *PtrToHeadGuard (char *ptr) const { union { char *charPtr; u32 *u32Ptr; }; charPtr = ptr - allocHeadSize + OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE + ALLOC_TYPE_SIZE + ALLOC_AMOUNT_SIZE; return u32Ptr; }
auto GetAllocTailSize () const { return allocTailSize; }
isize GetAllocationSize (isize size, u8 alignment) const { return GetAllocHeadSize() + size + GetAllocTailSize() + (alignment - 1); } // IMPORTANT: Getting the tail uses "Alloc Amount" metadata
u32 *PtrToTailGuard (char *ptr) const { union { char *charPtr; u32 *u32Ptr; }; charPtr = ptr + *PtrToAllocAmount(ptr); return u32Ptr; }
u8 *PtrToAlignment (char *ptr) const { union { char *charPtr; u8 *u8Ptr; }; charPtr = ptr - allocHeadSize + OFFSET_TO_SRC_SIZE; return u8Ptr; }
u8 *PtrToOffsetToSrc(char *ptr) const { union { char *charPtr; u8 *u8Ptr; }; charPtr = ptr - allocHeadSize; return u8Ptr; }
u32 *PtrToHeadBoundsGuard(char *ptr) const; // ptr: The ptr given to the client when allocating. // 0 if Pushed to Head on memstack, 1 if Pushed to Tail on memstack
u32 *PtrToTailBoundsGuard(char *ptr) const; // IMPORTANT: Uses "Alloc Amount" metadata to find the tail! u8 *PtrToAllocType (char *ptr) const { union { char *charPtr; u8 *u8Ptr; }; charPtr = ptr - allocHeadSize + OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE; return u8Ptr; }
u8 *PtrToAlignment (char *ptr) const; isize *PtrToAllocAmount(char *ptr) const { union { char *charPtr; isize *isizePtr; }; charPtr = ptr - allocHeadSize + OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE + ALLOC_TYPE_SIZE; return isizePtr; }
u8 *PtrToOffsetToSrc (char *ptr) const;
isize *PtrToAllocAmount (char *ptr) const;
private:
u32 boundsGuardSize; // sizeof(GUARD_VALUE) OR 0 if BoundsGuard is disabled.
u32 allocHeadSize; // Bounds Guard Size + Offset To Src Size + Alloc Amount Size
u32 allocTailSize; // Bounds Guard Size
}; };
// #DqnMemStack API // #DqnMemStack API
@ -1481,7 +1483,7 @@ struct DqnMemStack
enum Flag enum Flag
{ {
NonExpandable = (1 << 0), // Disallow additional memory blocks when full. NonExpandable = (1 << 0), // Disallow additional memory blocks when full.
NonExpandableAssert = (1 << 1), // Assert when NonExpandable is set with allocation on a full stack. NonExpandableAssert = (1 << 1), // Assert when non-expandable is set and we run out of space
BoundsGuard = (1 << 2), // Track, check and add guards on all allocations BoundsGuard = (1 << 2), // Track, check and add guards on all allocations
PushAssertsOnFail = (1 << 3), // Assert when push*() fails. PushAssertsOnFail = (1 << 3), // Assert when push*() fails.
All = (NonExpandable | NonExpandableAssert | BoundsGuard | PushAssertsOnFail), All = (NonExpandable | NonExpandableAssert | BoundsGuard | PushAssertsOnFail),
@ -1526,16 +1528,18 @@ struct DqnMemStack
// Allocation API // Allocation API
// ============================================================================================= // =============================================================================================
enum struct AllocTo
{
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.
// return: nullptr if out of space OR stack is using fixed memory/size OR stack full and platform malloc fails. // return: nullptr if out of space OR stack is using fixed memory/size OR stack full and platform malloc fails.
void *Push (isize size, u8 alignment = 4); void *Push (isize size, AllocTo allocTo = AllocTo::Head, u8 alignment = 4);
void *PushOnTail (isize size, u8 alignment = 4);
// Frees the given ptr. It MUST be the last allocated item in the stack, asserts otherwise. // Frees the given ptr. It MUST be the last allocated item in the stack, asserts otherwise.
void Pop (void *ptr, Dqn::ZeroClear clear = Dqn::ZeroClear::No); void Pop (void *ptr, Dqn::ZeroClear clear = Dqn::ZeroClear::No);
void PopOnTail (void *ptr, Dqn::ZeroClear zeroClear = Dqn::ZeroClear::No);
// Frees all blocks belonging to this stack. // Frees all blocks belonging to this stack.
void Free (); void Free ();
@ -3313,13 +3317,13 @@ struct DqnMemAPI__DqnMemStackContext
Mode mode; Mode mode;
}; };
FILE_SCOPE void *DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api, FILE_SCOPE void *
DqnMemAPI::Request request_, DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api, DqnMemAPI::Request request_, bool pushToHead)
bool pushToHead)
{ {
DqnMemAPI__ValidateRequest(request_); DqnMemAPI__ValidateRequest(request_);
DQN_ASSERT(request_.userContext); DQN_ASSERT(request_.userContext);
DqnMemStack::AllocTo const allocTo = (pushToHead) ? DqnMemStack::AllocTo::Head : DqnMemStack::AllocTo::Tail;
auto *const stack = (DqnMemStack *)(request_.userContext); auto *const stack = (DqnMemStack *)(request_.userContext);
void *result = nullptr; void *result = nullptr;
bool success = false; bool success = false;
@ -3331,6 +3335,7 @@ FILE_SCOPE void *DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api,
Tail, Tail,
}; };
// TODO(doyle): Should use the metadata in the ptr head
auto ClassifyPtr = [](DqnMemStack::Block const *block, char const *ptr) -> PtrType { auto ClassifyPtr = [](DqnMemStack::Block const *block, char const *ptr) -> PtrType {
PtrType result = PtrType::NotInCurrentBlock; PtrType result = PtrType::NotInCurrentBlock;
@ -3355,7 +3360,7 @@ FILE_SCOPE void *DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api,
if (type == PtrType::Head) if (type == PtrType::Head)
{ {
isize const oldMemSize = *(tracker->PtrToAllocAmount(ptr)); isize const oldMemSize = *(tracker->PtrToAllocAmount(ptr));
char const *ptrEnd = ptr + oldMemSize + tracker->GetAllocTailSize(); char const *ptrEnd = ptr + oldMemSize + tracker->allocTailSize;
result = (ptrEnd == (block->head - 1)); result = (ptrEnd == (block->head - 1));
} }
else if (type == PtrType::Tail) else if (type == PtrType::Tail)
@ -3371,9 +3376,7 @@ FILE_SCOPE void *DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api,
if (request_.type == DqnMemAPI::Type::Alloc) if (request_.type == DqnMemAPI::Type::Alloc)
{ {
auto *request = &request_.e.alloc; auto *request = &request_.e.alloc;
if (pushToHead) result = static_cast<char *>(stack->Push(request->requestSize)); result = static_cast<char *>(stack->Push(request->requestSize, allocTo));
else result = static_cast<char *>(stack->PushOnTail(request->requestSize));
if (result) if (result)
{ {
success = true; success = true;
@ -3408,7 +3411,7 @@ FILE_SCOPE void *DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api,
if (enoughSpace) if (enoughSpace)
{ {
stack->Pop(ptr, Dqn::ZeroClear::No); stack->Pop(ptr, Dqn::ZeroClear::No);
result = static_cast<char *>(stack->Push(request->newSize, alignment)); result = static_cast<char *>(stack->Push(request->newSize, DqnMemStack::AllocTo::Head, alignment));
DQN_ASSERT(stack->block == block && result == request->oldMemPtr); DQN_ASSERT(stack->block == block && result == request->oldMemPtr);
success = true; success = true;
} }
@ -3421,8 +3424,8 @@ FILE_SCOPE void *DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api,
enoughSpace = (block->tail - extraBytesReq) > block->head; enoughSpace = (block->tail - extraBytesReq) > block->head;
if (enoughSpace) if (enoughSpace)
{ {
stack->PopOnTail(ptr, Dqn::ZeroClear::No); stack->Pop(ptr, Dqn::ZeroClear::No);
result = static_cast<char *>(stack->PushOnTail(request->newSize, alignment)); result = static_cast<char *>(stack->Push(request->newSize, DqnMemStack::AllocTo::Tail, alignment));
DqnMem_Copy(result, ptr, oldMemSize); DqnMem_Copy(result, ptr, oldMemSize);
(static_cast<char *>(result))[oldMemSize] = 0; (static_cast<char *>(result))[oldMemSize] = 0;
@ -3439,12 +3442,12 @@ FILE_SCOPE void *DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api,
DqnMemStack::Block *oldBlock = block; DqnMemStack::Block *oldBlock = block;
if (type == PtrType::Head) if (type == PtrType::Head)
{ {
result = static_cast<char *>(stack->Push(request->newSize, alignment)); result = static_cast<char *>(stack->Push(request->newSize, DqnMemStack::AllocTo::Head, alignment));
} }
else else
{ {
DQN_ASSERT(type == PtrType::Tail); DQN_ASSERT(type == PtrType::Tail);
result = (u8 *)stack->PushOnTail(request->newSize, alignment); result = static_cast<char *>(stack->Push(request->newSize, DqnMemStack::AllocTo::Tail, alignment));
} }
if (result) if (result)
@ -3457,8 +3460,7 @@ FILE_SCOPE void *DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api,
auto *newBlock = stack->block; auto *newBlock = stack->block;
stack->block = oldBlock; stack->block = oldBlock;
if (type == PtrType::Head) stack->Pop(ptr, Dqn::ZeroClear::No); stack->Pop(ptr, Dqn::ZeroClear::No);
else stack->PopOnTail(ptr, Dqn::ZeroClear::No);
stack->block = newBlock; stack->block = newBlock;
success = true; success = true;
} }
@ -3475,18 +3477,16 @@ FILE_SCOPE void *DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api,
} }
else else
{ {
DQN_LOGE( DQN_LOGE("Lost %$_d, the ptr to realloc is sandwiched between other allocations (LIFO)", oldMemSize);
"Lost %$_d, the ptr to realloc is sandwiched between other allocations (LIFO)",
oldMemSize);
if (type == PtrType::Head) if (type == PtrType::Head)
{ {
result = (u8 *)stack->Push(request->newSize, alignment); result = (u8 *)stack->Push(request->newSize, DqnMemStack::AllocTo::Head, alignment);
} }
else else
{ {
DQN_ASSERT(type == PtrType::Tail); DQN_ASSERT(type == PtrType::Tail);
result = (u8 *)stack->PushOnTail(request->newSize, alignment); result = (u8 *)stack->Push(request->newSize, DqnMemStack::AllocTo::Tail, alignment);
} }
if (result) if (result)
@ -3507,9 +3507,7 @@ FILE_SCOPE void *DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api,
if (PtrIsLastAllocationInBlock(&stack->tracker, block, ptr)) if (PtrIsLastAllocationInBlock(&stack->tracker, block, ptr))
{ {
PtrType type = ClassifyPtr(block, ptr); stack->Pop(ptr, Dqn::ZeroClear::No);
if (type == PtrType::Head) stack->Pop(ptr, Dqn::ZeroClear::No);
else stack->PopOnTail(ptr, Dqn::ZeroClear::No);
} }
else else
{ {
@ -3613,15 +3611,10 @@ void DqnMemTracker::Init(bool boundsGuard)
this->boundsGuardSize = 0; this->boundsGuardSize = 0;
} }
this->allocHeadSize = OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE + ALLOC_AMOUNT_SIZE + this->boundsGuardSize; this->allocHeadSize = OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE + ALLOC_TYPE_SIZE + ALLOC_AMOUNT_SIZE + this->boundsGuardSize;
this->allocTailSize = this->boundsGuardSize; this->allocTailSize = this->boundsGuardSize;
} }
void DqnMemTracker::Free()
{
this->allocations.Free();
}
void DqnMemTracker::RemoveAllocation(char *ptr) void DqnMemTracker::RemoveAllocation(char *ptr)
{ {
isize deleteIndex = -1; isize deleteIndex = -1;
@ -3643,8 +3636,8 @@ void DqnMemTracker::CheckAllocations() const
for (auto index = 0; index < this->allocations.count; index++) for (auto index = 0; index < this->allocations.count; index++)
{ {
char *ptr = static_cast<char *>(this->allocations.data[index]); char *ptr = static_cast<char *>(this->allocations.data[index]);
u32 const *headGuard = this->PtrToHeadBoundsGuard(ptr); u32 const *headGuard = this->PtrToHeadGuard(ptr);
u32 const *tailGuard = this->PtrToTailBoundsGuard(ptr); u32 const *tailGuard = this->PtrToTailGuard(ptr);
DQN_ASSERTM(*headGuard == HEAD_GUARD_VALUE, DQN_ASSERTM(*headGuard == HEAD_GUARD_VALUE,
"Bounds guard has been destroyed at the head end of the allocation! Expected: " "Bounds guard has been destroyed at the head end of the allocation! Expected: "
@ -3658,59 +3651,6 @@ void DqnMemTracker::CheckAllocations() const
} }
} }
u32 *DqnMemTracker::PtrToHeadBoundsGuard(char *ptr) const
{
union {
char *charPtr;
u32 *u32Ptr;
};
charPtr = ptr - this->allocHeadSize + OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE + ALLOC_AMOUNT_SIZE;
return u32Ptr;
}
u8 *DqnMemTracker::PtrToAlignment(char *ptr) const
{
union {
char *charPtr;
u8 *u8Ptr;
};
charPtr = ptr - this->allocHeadSize + OFFSET_TO_SRC_SIZE;
return u8Ptr;
}
u8 *DqnMemTracker::PtrToOffsetToSrc(char *ptr) const
{
union {
char *charPtr;
u8 *u8Ptr;
};
charPtr = ptr - this->allocHeadSize;
return u8Ptr;
}
isize *DqnMemTracker::PtrToAllocAmount(char *ptr) const
{
union {
char *charPtr;
isize *isizePtr;
};
charPtr = ptr - this->allocHeadSize + OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE;
return isizePtr;
}
u32 *DqnMemTracker::PtrToTailBoundsGuard(char *ptr) const
{
isize size = *PtrToAllocAmount(ptr);
union {
char *charPtr;
u32 *u32Ptr;
};
charPtr = ptr + size;
return u32Ptr;
}
// #DqnMemStack // #DqnMemStack
// ================================================================================================= // =================================================================================================
DQN_FILE_SCOPE DqnMemStack::Block * DQN_FILE_SCOPE DqnMemStack::Block *
@ -3762,7 +3702,7 @@ DqnMemStack::DqnMemStack(isize size, Dqn::ZeroClear clear, u32 flags_, DqnMemAPI
this->tracker.Init(boundsGuard); this->tracker.Init(boundsGuard);
} }
FILE_SCOPE void *DqnMemStack__Push(DqnMemStack *stack, isize size, u8 alignment, bool pushToHead) void *DqnMemStack::Push(isize size, AllocTo allocTo, u8 alignment)
{ {
DQN_ASSERT(size >= 0 && (alignment % 2 == 0)); DQN_ASSERT(size >= 0 && (alignment % 2 == 0));
DQN_ALWAYS_ASSERTM(alignment <= 128, "Alignment supported. Update metadata to use u16 for storing the offset!"); DQN_ALWAYS_ASSERTM(alignment <= 128, "Alignment supported. Update metadata to use u16 for storing the offset!");
@ -3770,78 +3710,72 @@ FILE_SCOPE void *DqnMemStack__Push(DqnMemStack *stack, isize size, u8 alignment,
if (size == 0) if (size == 0)
return nullptr; return nullptr;
bool const pushToHead = (allocTo == AllocTo::Head);
isize sizeToAllocate = this->tracker.GetAllocationSize(size, alignment);
// Allocate New Block If Full // Allocate New Block If Full
// ============================================================================================= // =============================================================================================
DqnMemTracker *tracker = &stack->tracker;
isize actualSize = tracker->GetAllocationSize(size, alignment);
bool needNewBlock = false; bool needNewBlock = false;
if (stack->block) if (this->block)
{ {
if (pushToHead) needNewBlock = ((stack->block->head + actualSize) > stack->block->tail); if (pushToHead) needNewBlock = ((this->block->head + sizeToAllocate) > this->block->tail);
else needNewBlock = ((stack->block->tail - actualSize) < stack->block->head); else needNewBlock = ((this->block->tail - sizeToAllocate) < this->block->head);
} }
if (needNewBlock) if (needNewBlock)
{ {
if (Dqn_BitIsSet(stack->flags, DqnMemStack::Flag::NonExpandable)) if (Dqn_BitIsSet(this->flags, Flag::NonExpandable))
{ {
DQN_LOGE("Allocator is non-expandable and has run out of memory."); if (Dqn_BitIsSet(this->flags, Flag::NonExpandableAssert))
if (Dqn_BitIsSet(stack->flags, DqnMemStack::Flag::NonExpandableAssert)) DQN_ASSERTM(Dqn_BitIsSet(this->flags, Flag::NonExpandable), "Allocator is non-expandable and has run out of memory");
DQN_ASSERT(DQN_INVALID_CODE_PATH);
return nullptr; return nullptr;
} }
isize newBlockSize = DQN_MAX(actualSize, DqnMemStack::MINIMUM_BLOCK_SIZE); isize newBlockSize = DQN_MAX(sizeToAllocate, MINIMUM_BLOCK_SIZE);
DqnMemStack::Block *newBlock = DqnMemStack__AllocateBlock(newBlockSize, Dqn::ZeroClear::No, stack->memAPI); Block *newBlock = DqnMemStack__AllocateBlock(newBlockSize, Dqn::ZeroClear::No, this->memAPI);
if (!newBlock) newBlock->prevBlock = this->block;
{ this->block = newBlock;
DQN_LOGE(
"Allocator is full and could not allocate additional memory blocks, requested: %$d",
newBlockSize);
return nullptr;
}
newBlock->prevBlock = stack->block;
stack->block = newBlock;
} }
// Calculate Ptr To Give Client // Calculate Ptr To Give Client
// ============================================================================================= // =============================================================================================
char *currPtr = (pushToHead) ? (stack->block->head) : (stack->block->tail - actualSize); char *currPtr = (pushToHead) ? (this->block->head) : (this->block->tail - sizeToAllocate);
char *result = reinterpret_cast<char *>(DQN_ALIGN_POW_N((currPtr + tracker->GetAllocHeadSize()), alignment)); char *result = reinterpret_cast<char *>(DQN_ALIGN_POW_N(currPtr + this->tracker.allocHeadSize, alignment));
isize const offsetToSrc = result - currPtr; isize const offsetToSrc = result - currPtr;
DQN_ASSERT(offsetToSrc > 0 && offsetToSrc < (u8)-1); DQN_ASSERT(offsetToSrc > 0 && offsetToSrc < (u8)-1);
if (pushToHead) if (pushToHead)
{ {
stack->block->head += actualSize; this->block->head += sizeToAllocate;
DQN_ASSERT(stack->block->head <= stack->block->tail); DQN_ASSERT(this->block->head <= this->block->tail);
} }
else else
{ {
stack->block->tail -= actualSize; this->block->tail -= sizeToAllocate;
DQN_ASSERT(stack->block->tail >= stack->block->head); DQN_ASSERT(this->block->tail >= this->block->head);
} }
// Instrument allocation with guards and tracker // Instrument allocation with guards and tracker
// ============================================================================================= // =============================================================================================
{ {
auto *myOffsetToSrc = tracker->PtrToOffsetToSrc(result); auto *myOffsetToSrc = this->tracker.PtrToOffsetToSrc(result);
*myOffsetToSrc = (u8)offsetToSrc; *myOffsetToSrc = (u8)offsetToSrc;
auto *myAlignment = tracker->PtrToAlignment(result); auto *myAlignment = this->tracker.PtrToAlignment(result);
*myAlignment = alignment; *myAlignment = alignment;
auto *allocAmount = tracker->PtrToAllocAmount(result); auto *allocAmount = this->tracker.PtrToAllocAmount(result);
*allocAmount = size; *allocAmount = size;
if (Dqn_BitIsSet(stack->flags, DqnMemStack::Flag::BoundsGuard)) auto *allocType = this->tracker.PtrToAllocType(result);
*allocType = (pushToHead) ? 0 : 1;
if (Dqn_BitIsSet(this->flags, DqnMemStack::Flag::BoundsGuard))
{ {
auto *headGuard = tracker->PtrToHeadBoundsGuard(result); auto *headGuard = this->tracker.PtrToHeadGuard(result);
auto *tailGuard = tracker->PtrToTailBoundsGuard(result); auto *tailGuard = this->tracker.PtrToTailGuard(result);
*headGuard = DqnMemTracker::HEAD_GUARD_VALUE; *headGuard = DqnMemTracker::HEAD_GUARD_VALUE;
*tailGuard = DqnMemTracker::TAIL_GUARD_VALUE; *tailGuard = DqnMemTracker::TAIL_GUARD_VALUE;
} }
@ -3853,33 +3787,21 @@ FILE_SCOPE void *DqnMemStack__Push(DqnMemStack *stack, isize size, u8 alignment,
char *checkAlignment = reinterpret_cast<char *>(DQN_ALIGN_POW_N(result, alignment)); char *checkAlignment = reinterpret_cast<char *>(DQN_ALIGN_POW_N(result, alignment));
DQN_ASSERTM(checkAlignment == result, "Adding bounds guard should not destroy alignment! %p != %p", result, checkAlignment); DQN_ASSERTM(checkAlignment == result, "Adding bounds guard should not destroy alignment! %p != %p", result, checkAlignment);
if (Dqn_BitIsSet(stack->flags, DqnMemStack::Flag::BoundsGuard)) if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
{ {
tracker->AddAllocation(result); this->tracker.AddAllocation(result);
tracker->CheckAllocations(); this->tracker.CheckAllocations();
} }
} }
return result; return result;
} }
void *DqnMemStack::PushOnTail(isize size, u8 alignment) FILE_SCOPE void DqnMemStack__KillTrackedPtrsInRange(DqnMemTracker *tracker, char const *start, char const *end)
{
void *result = DqnMemStack__Push(this, size, alignment, false);
return result;
}
void *DqnMemStack::Push(isize size, u8 alignment)
{
void *result = DqnMemStack__Push(this, size, alignment, true);
return result;
}
FILE_SCOPE void DqnMemStack__KillTrackerPtrsExistingIn(DqnMemTracker *tracker, char const *start, char const *end)
{ {
if (start >= end) return; if (start >= end) return;
for (auto index = 0; index < tracker->allocations.count; index++) for (isize index = 0; index < tracker->allocations.count; index++)
{ {
char *ptr = static_cast<char *>(tracker->allocations.data[index]); char *ptr = static_cast<char *>(tracker->allocations.data[index]);
if (ptr >= start && ptr < end) if (ptr >= start && ptr < end)
@ -3890,72 +3812,61 @@ FILE_SCOPE void DqnMemStack__KillTrackerPtrsExistingIn(DqnMemTracker *tracker, c
} }
} }
FILE_SCOPE void DqnMemStack__KillTrackerPtrsExistingInBlock(DqnMemTracker *tracker, DqnMemStack::Block const *block) FILE_SCOPE void DqnMemStack__KillTrackedPtrsInBlock(DqnMemTracker *tracker, DqnMemStack::Block const *block)
{ {
char const *blockStart = block->memory; char const *blockStart = block->memory;
char const *blockEnd = block->memory + block->size; char const *blockEnd = block->memory + block->size;
DqnMemStack__KillTrackerPtrsExistingIn(tracker, blockStart, blockEnd); DqnMemStack__KillTrackedPtrsInRange(tracker, blockStart, blockEnd);
} }
FILE_SCOPE void DqnMemStack__Pop(DqnMemStack *stack, void *ptr, Dqn::ZeroClear clear, bool popHead) void DqnMemStack::Pop(void *ptr, Dqn::ZeroClear clear)
{ {
if (!ptr) return; if (!ptr) return;
DQN_ASSERT(stack->block);
char *const bytePtr = static_cast<char *>(ptr); char *bytePtr = static_cast<char *>(ptr);
DqnMemTracker *tracker = &stack->tracker;
// Check instrumented data // Check instrumented data
if (Dqn_BitIsSet(stack->flags, DqnMemStack::Flag::BoundsGuard)) if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
{ {
tracker->CheckAllocations(); this->tracker.CheckAllocations();
tracker->RemoveAllocation(bytePtr); this->tracker.RemoveAllocation(bytePtr);
} }
isize const size = *(tracker->PtrToAllocAmount(bytePtr)); bool const popHead = (*(this->tracker.PtrToAllocType(bytePtr)) == 0);
u8 const alignment = *(tracker->PtrToAlignment(bytePtr)); isize const size = *(this->tracker.PtrToAllocAmount(bytePtr));
u8 const offsetToSrc = *(tracker->PtrToOffsetToSrc(bytePtr)); u8 const alignment = *(this->tracker.PtrToAlignment(bytePtr));
u8 const offsetToSrc = *(this->tracker.PtrToOffsetToSrc(bytePtr));
isize actualSize = tracker->GetAllocationSize(size, alignment); isize actualSize = this->tracker.GetAllocationSize(size, alignment);
char *start = bytePtr - offsetToSrc; char *start = bytePtr - offsetToSrc;
char *end = start + actualSize; char *end = start + actualSize;
char const *blockEnd = stack->block->memory + stack->block->size; char const *blockEnd = this->block->memory + this->block->size;
if (popHead) if (popHead)
{ {
DQN_ASSERTM(end == stack->block->head, "Pointer to pop was not the last allocation! %p != %p", end, stack->block->head); DQN_ASSERTM(end == this->block->head, "Pointer to pop was not the last allocation! %p != %p", end, this->block->head);
stack->block->head -= actualSize; this->block->head -= actualSize;
DQN_ASSERT(stack->block->head >= stack->block->memory); DQN_ASSERT(this->block->head >= this->block->memory);
} }
else else
{ {
DQN_ASSERTM(start == stack->block->tail, "Pointer to pop was not the last allocation! %p != %p", start, stack->block->tail); DQN_ASSERTM(start == this->block->tail, "Pointer to pop was not the last allocation! %p != %p", start, this->block->tail);
stack->block->tail += actualSize; this->block->tail += actualSize;
DQN_ASSERT(stack->block->tail <= blockEnd); DQN_ASSERT(this->block->tail <= blockEnd);
} }
if (clear == Dqn::ZeroClear::Yes) if (clear == Dqn::ZeroClear::Yes)
DqnMem_Set(start, 0, end - start); DqnMem_Set(start, 0, end - start);
if (stack->block->tail == blockEnd && stack->block->head == stack->block->memory) if (this->block->tail == blockEnd && this->block->head == this->block->memory)
{ {
if (stack->block->prevBlock) if (this->block->prevBlock)
{ {
stack->FreeLastBlock(); this->FreeLastBlock();
} }
} }
} }
void DqnMemStack::Pop(void *ptr, Dqn::ZeroClear clear)
{
DqnMemStack__Pop(this, ptr, clear, true);
}
void DqnMemStack::PopOnTail(void *ptr, Dqn::ZeroClear clear)
{
DqnMemStack__Pop(this, ptr, clear, false);
}
void DqnMemStack::Free() void DqnMemStack::Free()
{ {
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard)) if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
@ -3988,7 +3899,7 @@ bool DqnMemStack::FreeMemBlock(DqnMemStack::Block *memBlock)
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard)) if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
{ {
DqnMemStack__KillTrackerPtrsExistingInBlock(&this->tracker, blockToFree); DqnMemStack__KillTrackedPtrsInBlock(&this->tracker, blockToFree);
} }
isize realSize = blockToFree->size + sizeof(DqnMemStack::Block); isize realSize = blockToFree->size + sizeof(DqnMemStack::Block);
@ -4008,7 +3919,7 @@ void DqnMemStack::ResetTail()
char *end = this->block->memory + this->block->size; char *end = this->block->memory + this->block->size;
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard)) if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
{ {
DqnMemStack__KillTrackerPtrsExistingIn(&this->tracker, start, end); DqnMemStack__KillTrackedPtrsInRange(&this->tracker, start, end);
} }
this->block->tail = end; this->block->tail = end;
@ -4036,7 +3947,7 @@ void DqnMemStack::ClearCurrBlock(Dqn::ZeroClear clear)
{ {
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard)) if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
{ {
DqnMemStack__KillTrackerPtrsExistingInBlock(&this->tracker, this->block); DqnMemStack__KillTrackedPtrsInBlock(&this->tracker, this->block);
} }
this->block->head = this->block->memory; this->block->head = this->block->memory;
@ -4136,7 +4047,7 @@ void DqnMemStack::TempRegionEnd(TempRegion region)
{ {
char *blockStart = this->block->head; char *blockStart = this->block->head;
char *blockEnd = this->block->tail; char *blockEnd = this->block->tail;
DqnMemStack__KillTrackerPtrsExistingIn(&this->tracker, blockStart, blockEnd); DqnMemStack__KillTrackedPtrsInRange(&this->tracker, blockStart, blockEnd);
} }
break; break;
} }
@ -4161,7 +4072,7 @@ void DqnMemStack::TempRegionEnd(TempRegion region)
if (Dqn_BitIsSet(this->flags, DqnMemStack::Flag::BoundsGuard)) if (Dqn_BitIsSet(this->flags, DqnMemStack::Flag::BoundsGuard))
{ {
DqnMemStack__KillTrackerPtrsExistingIn(&this->tracker, blockStart, blockEnd); DqnMemStack__KillTrackedPtrsInRange(&this->tracker, blockStart, blockEnd);
} }
} }
} }