Write more tests for push pop to tail
This commit is contained in:
parent
b8157a3c9f
commit
9be5194b17
64
dqn.h
64
dqn.h
@ -457,15 +457,16 @@ struct DqnMemStack
|
|||||||
|
|
||||||
// Allocation API
|
// Allocation API
|
||||||
// =============================================================================================
|
// =============================================================================================
|
||||||
void *PushOnTail (isize size, u8 alignment = 4);
|
|
||||||
|
|
||||||
// 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, 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 *const ptr, bool zeroClear = false);
|
void Pop (void *const ptr, bool zeroClear = false);
|
||||||
|
void PopOnTail (void *const ptr, bool zeroClear = false);
|
||||||
|
|
||||||
// Frees all blocks belonging to this stack.
|
// Frees all blocks belonging to this stack.
|
||||||
void Free ();
|
void Free ();
|
||||||
@ -3194,7 +3195,6 @@ FILE_SCOPE u8 *DqnMemAPIInternal_StackAllocatorCallback(DqnMemAPI *api, DqnMemAP
|
|||||||
if (enoughSpace)
|
if (enoughSpace)
|
||||||
{
|
{
|
||||||
stack->Pop(ptr, false);
|
stack->Pop(ptr, false);
|
||||||
|
|
||||||
result = (u8 *)stack->Push(request->newSize, alignment);
|
result = (u8 *)stack->Push(request->newSize, alignment);
|
||||||
DQN_ASSERT(stack->block == block && result == request->oldMemPtr);
|
DQN_ASSERT(stack->block == block && result == request->oldMemPtr);
|
||||||
success = true;
|
success = true;
|
||||||
@ -3208,7 +3208,7 @@ FILE_SCOPE u8 *DqnMemAPIInternal_StackAllocatorCallback(DqnMemAPI *api, DqnMemAP
|
|||||||
enoughSpace = (block->tail - extraBytesReq) > block->head;
|
enoughSpace = (block->tail - extraBytesReq) > block->head;
|
||||||
if (enoughSpace)
|
if (enoughSpace)
|
||||||
{
|
{
|
||||||
stack->Pop(ptr, false);
|
stack->PopOnTail(ptr, false);
|
||||||
result = (u8 *)stack->Push(request->newSize, alignment);
|
result = (u8 *)stack->Push(request->newSize, alignment);
|
||||||
DqnMem_Copy(result, ptr, oldMemSize);
|
DqnMem_Copy(result, ptr, oldMemSize);
|
||||||
result[oldMemSize] = 0;
|
result[oldMemSize] = 0;
|
||||||
@ -3243,7 +3243,9 @@ FILE_SCOPE u8 *DqnMemAPIInternal_StackAllocatorCallback(DqnMemAPI *api, DqnMemAP
|
|||||||
// Switch to old block, pop the ptr and return the new block on top.
|
// Switch to old block, pop the ptr and return the new block on top.
|
||||||
auto *newBlock = stack->block;
|
auto *newBlock = stack->block;
|
||||||
stack->block = oldBlock;
|
stack->block = oldBlock;
|
||||||
stack->Pop(ptr, false);
|
|
||||||
|
if (type == PtrType::Head) stack->Pop(ptr, false);
|
||||||
|
else stack->PopOnTail(ptr, false);
|
||||||
stack->block = newBlock;
|
stack->block = newBlock;
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
@ -3292,7 +3294,9 @@ FILE_SCOPE u8 *DqnMemAPIInternal_StackAllocatorCallback(DqnMemAPI *api, DqnMemAP
|
|||||||
|
|
||||||
if (PtrIsLastAllocationInBlock(&stack->metadata, block, ptr))
|
if (PtrIsLastAllocationInBlock(&stack->metadata, block, ptr))
|
||||||
{
|
{
|
||||||
stack->Pop(ptr, false);
|
PtrType type = ClassifyPtr(block, ptr);
|
||||||
|
if (type == PtrType::Head) stack->Pop(ptr, false);
|
||||||
|
else stack->PopOnTail(ptr, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -3709,16 +3713,17 @@ FILE_SCOPE void DqnMemStackInternal_KillMetadataPtrsExistingInBlock(DqnAllocator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DqnMemStack::Pop(void *const ptr, bool zeroClear)
|
|
||||||
|
FILE_SCOPE void DqnMemStackInternal_Pop(DqnMemStack *stack, void *const ptr, bool zeroClear, bool popHead)
|
||||||
{
|
{
|
||||||
if (!ptr) return;
|
if (!ptr) return;
|
||||||
DQN_ASSERT(this->block);
|
DQN_ASSERT(stack->block);
|
||||||
|
|
||||||
u8 *const bytePtr = (u8 *)ptr;
|
u8 *const bytePtr = (u8 *)ptr;
|
||||||
DqnAllocatorMetadata *myMetadata = &this->metadata;
|
DqnAllocatorMetadata *myMetadata = &stack->metadata;
|
||||||
|
|
||||||
// Check instrumented data
|
// Check instrumented data
|
||||||
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
|
if (Dqn_BitIsSet(stack->flags, DqnMemStack::Flag::BoundsGuard))
|
||||||
{
|
{
|
||||||
myMetadata->CheckAllocations();
|
myMetadata->CheckAllocations();
|
||||||
myMetadata->RemoveAllocation(bytePtr);
|
myMetadata->RemoveAllocation(bytePtr);
|
||||||
@ -3731,30 +3736,41 @@ void DqnMemStack::Pop(void *const ptr, bool zeroClear)
|
|||||||
isize actualSize = myMetadata->GetAllocationSize(size, alignment);
|
isize actualSize = myMetadata->GetAllocationSize(size, alignment);
|
||||||
u8 *const start = bytePtr - offsetToSrc;
|
u8 *const start = bytePtr - offsetToSrc;
|
||||||
u8 *const end = start + actualSize;
|
u8 *const end = start + actualSize;
|
||||||
u8 const *const blockEnd = this->block->memory + this->block->size;
|
u8 const *const blockEnd = stack->block->memory + stack->block->size;
|
||||||
|
|
||||||
if (bytePtr >= this->block->memory && bytePtr < this->block->head)
|
if (popHead)
|
||||||
{
|
{
|
||||||
DQN_ASSERTM(end == this->block->head, "Pointer to pop was not the last allocation! %p != %p", end, this->block->head);
|
DQN_ASSERTM(end == stack->block->head, "Pointer to pop was not the last allocation! %p != %p", end, stack->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 if (bytePtr >= this->block->tail && bytePtr < blockEnd)
|
|
||||||
{
|
|
||||||
DQN_ASSERTM(start == this->block->tail, "Pointer to pop was not the last allocation! %p != %p", start,
|
|
||||||
this->block->tail);
|
|
||||||
|
|
||||||
this->block->tail += actualSize;
|
|
||||||
DQN_ASSERT(this->block->tail <= blockEnd);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DQN_ASSERTM(DQN_INVALID_CODE_PATH, "Pointer to free does not belong to current block!");
|
DQN_ASSERTM(start == stack->block->tail, "Pointer to pop was not the last allocation! %p != %p", start, stack->block->tail);
|
||||||
|
stack->block->tail += actualSize;
|
||||||
|
DQN_ASSERT(stack->block->tail <= blockEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zeroClear)
|
if (zeroClear)
|
||||||
DqnMem_Set(start, 0, end - start);
|
DqnMem_Set(start, 0, end - start);
|
||||||
|
|
||||||
|
if (stack->block->tail == blockEnd && stack->block->head == stack->block->memory)
|
||||||
|
{
|
||||||
|
if (stack->block->prevBlock)
|
||||||
|
{
|
||||||
|
stack->FreeLastBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DqnMemStack::Pop(void *const ptr, bool zeroClear)
|
||||||
|
{
|
||||||
|
DqnMemStackInternal_Pop(this, ptr, zeroClear, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DqnMemStack::PopOnTail(void *const ptr, bool zeroClear)
|
||||||
|
{
|
||||||
|
DqnMemStackInternal_Pop(this, ptr, zeroClear, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DqnMemStack::Free()
|
void DqnMemStack::Free()
|
||||||
|
@ -2426,12 +2426,12 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
// Temporary Regions
|
// Temporary Regions
|
||||||
if (1)
|
if (1)
|
||||||
{
|
{
|
||||||
DqnMemStack stack = {};
|
|
||||||
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true));
|
|
||||||
|
|
||||||
// Check temporary regions
|
// Check temporary regions
|
||||||
if (1)
|
if (1)
|
||||||
{
|
{
|
||||||
|
DqnMemStack stack = {};
|
||||||
|
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true, DqnMemStack::Flag::BoundsGuard));
|
||||||
|
|
||||||
DqnMemStack::Block *blockToReturnTo = stack.block;
|
DqnMemStack::Block *blockToReturnTo = stack.block;
|
||||||
auto headBefore = blockToReturnTo->head;
|
auto headBefore = blockToReturnTo->head;
|
||||||
auto tailBefore = blockToReturnTo->tail;
|
auto tailBefore = blockToReturnTo->tail;
|
||||||
@ -2456,11 +2456,16 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
DQN_ASSERT(stack.block == blockToReturnTo);
|
DQN_ASSERT(stack.block == blockToReturnTo);
|
||||||
DQN_ASSERT(stack.block->head == headBefore);
|
DQN_ASSERT(stack.block->head == headBefore);
|
||||||
DQN_ASSERT(stack.block->tail == tailBefore);
|
DQN_ASSERT(stack.block->tail == tailBefore);
|
||||||
|
|
||||||
|
stack.Free();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check temporary regions keep state
|
// Check temporary regions keep state
|
||||||
if (1)
|
if (1)
|
||||||
{
|
{
|
||||||
|
DqnMemStack stack = {};
|
||||||
|
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true, DqnMemStack::Flag::BoundsGuard));
|
||||||
|
|
||||||
DqnMemStack::Block *blockToReturnTo = stack.block;
|
DqnMemStack::Block *blockToReturnTo = stack.block;
|
||||||
auto headBefore = blockToReturnTo->head;
|
auto headBefore = blockToReturnTo->head;
|
||||||
auto tailBefore = blockToReturnTo->tail;
|
auto tailBefore = blockToReturnTo->tail;
|
||||||
@ -2486,9 +2491,55 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
DQN_ASSERT(stack.block != blockToReturnTo);
|
DQN_ASSERT(stack.block != blockToReturnTo);
|
||||||
DQN_ASSERT(stack.block->prevBlock == blockToReturnTo);
|
DQN_ASSERT(stack.block->prevBlock == blockToReturnTo);
|
||||||
DQN_ASSERT(stack.tempRegionCount == 0);
|
DQN_ASSERT(stack.tempRegionCount == 0);
|
||||||
|
|
||||||
|
stack.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check temporary regions with tail and head pushes
|
||||||
|
if (1)
|
||||||
|
{
|
||||||
|
DqnMemStack stack = {};
|
||||||
|
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true, DqnMemStack::Flag::BoundsGuard));
|
||||||
|
|
||||||
|
auto *pop1 = stack.Push(222);
|
||||||
|
auto *pop2 = stack.PushOnTail(333);
|
||||||
|
|
||||||
|
DqnMemStack::Block *blockToReturnTo = stack.block;
|
||||||
|
auto headBefore = blockToReturnTo->head;
|
||||||
|
auto tailBefore = blockToReturnTo->tail;
|
||||||
|
if (1)
|
||||||
|
{
|
||||||
|
auto memGuard1 = stack.TempRegionGuard();
|
||||||
|
auto *result2 = stack.Push(100);
|
||||||
|
auto *result3 = stack.PushOnTail(100);
|
||||||
|
auto *result4 = stack.Push(100);
|
||||||
|
auto *result5 = stack.PushOnTail(100);
|
||||||
|
DQN_ASSERT(result2 && result3 && result4 && result5);
|
||||||
|
DQN_ASSERT(result3 > result5);
|
||||||
|
DQN_ASSERT(result2 < result4);
|
||||||
|
DQN_ASSERT(stack.block->head > headBefore && stack.block->head < stack.block->tail);
|
||||||
|
DQN_ASSERT(stack.block->tail >= stack.block->head && stack.block->tail < (stack.block->memory + stack.block->size));
|
||||||
|
DQN_ASSERT(stack.block->memory == blockToReturnTo->memory);
|
||||||
|
|
||||||
|
// Force allocation of new block
|
||||||
|
auto *result6 = stack.Push(DQN_MEGABYTE(5));
|
||||||
|
DQN_ASSERT(result6);
|
||||||
|
DQN_ASSERT(stack.block != blockToReturnTo);
|
||||||
|
DQN_ASSERT(stack.tempRegionCount == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_ASSERT(stack.block == blockToReturnTo);
|
||||||
|
DQN_ASSERT(stack.block->head == headBefore);
|
||||||
|
DQN_ASSERT(stack.block->tail == tailBefore);
|
||||||
|
|
||||||
|
stack.Pop(pop1);
|
||||||
|
stack.PopOnTail(pop2);
|
||||||
|
DQN_ASSERT(stack.block->head == stack.block->memory);
|
||||||
|
DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size);
|
||||||
|
|
||||||
|
stack.Free();
|
||||||
}
|
}
|
||||||
Log(Status::Ok, "Temporary regions return state and/or keep changes if requested.");
|
Log(Status::Ok, "Temporary regions return state and/or keep changes if requested.");
|
||||||
stack.Free();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Fixed Mem Init
|
// Check Fixed Mem Init
|
||||||
@ -2607,6 +2658,8 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
Log(Status::Ok, "Bounds guards are placed adjacent and have magic values.");
|
Log(Status::Ok, "Bounds guards are placed adjacent and have magic values.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (1)
|
||||||
|
{
|
||||||
// Push to tail and head
|
// Push to tail and head
|
||||||
if (1)
|
if (1)
|
||||||
{
|
{
|
||||||
@ -2615,7 +2668,87 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
|
|
||||||
auto *result1 = stack.Push(100);
|
auto *result1 = stack.Push(100);
|
||||||
auto *result2 = stack.PushOnTail(100);
|
auto *result2 = stack.PushOnTail(100);
|
||||||
|
auto *headBefore = stack.block->head;
|
||||||
|
auto *tailBefore = stack.block->tail;
|
||||||
|
DQN_ASSERT(result2 && result1);
|
||||||
|
DQN_ASSERT(result2 != result1 && result1 < result2);
|
||||||
|
|
||||||
|
stack.PopOnTail(result2);
|
||||||
|
DQN_ASSERT(headBefore == stack.block->head)
|
||||||
|
DQN_ASSERT(tailBefore != stack.block->tail)
|
||||||
|
|
||||||
|
stack.Pop(result1);
|
||||||
|
DQN_ASSERT(stack.block->prevBlock == false);
|
||||||
|
DQN_ASSERT(stack.block->head == stack.block->memory);
|
||||||
|
DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size);
|
||||||
stack.Free();
|
stack.Free();
|
||||||
|
Log(Status::Ok, "Push, pop to tail and head.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expansion with tail
|
||||||
|
if (1)
|
||||||
|
{
|
||||||
|
// Push too much to tail causes expansion
|
||||||
|
if (1)
|
||||||
|
{
|
||||||
|
DqnMemStack stack = {};
|
||||||
|
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true, DqnMemStack::Flag::BoundsGuard));
|
||||||
|
|
||||||
|
auto *result1 = stack.Push(100);
|
||||||
|
DQN_ASSERT(stack.block->prevBlock == nullptr);
|
||||||
|
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);
|
||||||
|
auto *blockBefore = stack.block;
|
||||||
|
|
||||||
|
auto *result2 = stack.PushOnTail(DQN_MEGABYTE(1));
|
||||||
|
DQN_ASSERT(result2 && result1);
|
||||||
|
DQN_ASSERT(result2 != result1);
|
||||||
|
DQN_ASSERT(stack.block->prevBlock == blockBefore);
|
||||||
|
DQN_ASSERT(stack.block != blockBefore);
|
||||||
|
|
||||||
|
DQN_ASSERT(stack.block->head == stack.block->memory);
|
||||||
|
DQN_ASSERT(stack.block->tail < stack.block->memory + stack.block->size &&
|
||||||
|
stack.block->tail >= stack.block->head);
|
||||||
|
|
||||||
|
stack.PopOnTail(result2);
|
||||||
|
DQN_ASSERT(blockBefore == stack.block);
|
||||||
|
|
||||||
|
stack.Pop(result1);
|
||||||
|
DQN_ASSERT(blockBefore == stack.block);
|
||||||
|
|
||||||
|
stack.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push too much to tail fails to expand when non expandable
|
||||||
|
if (1)
|
||||||
|
{
|
||||||
|
DqnMemStack stack = {};
|
||||||
|
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true, DqnMemStack::Flag::NonExpandable));
|
||||||
|
|
||||||
|
auto *result1 = stack.Push(100);
|
||||||
|
DQN_ASSERT(stack.block->prevBlock == nullptr);
|
||||||
|
DQN_ASSERT(stack.block->head != stack.block->memory);
|
||||||
|
DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size);
|
||||||
|
auto *blockBefore = stack.block;
|
||||||
|
|
||||||
|
auto *result2 = stack.PushOnTail(DQN_MEGABYTE(1));
|
||||||
|
DQN_ASSERT(result2 == nullptr);
|
||||||
|
DQN_ASSERT(stack.block->prevBlock == nullptr);
|
||||||
|
DQN_ASSERT(stack.block == blockBefore);
|
||||||
|
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);
|
||||||
|
|
||||||
|
stack.PopOnTail(result2);
|
||||||
|
DQN_ASSERT(blockBefore == stack.block);
|
||||||
|
|
||||||
|
stack.Pop(result1);
|
||||||
|
DQN_ASSERT(blockBefore == stack.block);
|
||||||
|
|
||||||
|
stack.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(Status::Ok, "Non-Expanding and expanding stack with tail push.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user