Write basic tests for new revamped memstacks

This commit is contained in:
Doyle Thai 2018-02-03 02:49:54 +11:00
parent 1fc49c4fde
commit 21ff2f43dc
3 changed files with 274 additions and 23 deletions

View File

@ -31,7 +31,7 @@ REM Zi enables debug data, Z7 combines the debug files into one.
REM W4 warning level 4 REM W4 warning level 4
REM WX treat warnings as errors REM WX treat warnings as errors
REM wd4201 ignore: nonstandard extension used: nameless struct/union REM wd4201 ignore: nonstandard extension used: nameless struct/union
set CompileFlags=-EHsc -GR- -Oi -MT -Z7 -W4 -WX -wd4201 -FC -O2 -wd4127 set CompileFlags=-EHsc -GR- -Oi -MT -Z7 -W4 -WX -wd4201 -FC -Od -wd4127
REM Include directories REM Include directories
set IncludeFlags= set IncludeFlags=

27
dqn.h
View File

@ -437,7 +437,7 @@ struct DqnMemStack
// Uses fixed buffer, allocations will be sourced from the buffer and fail after buffer is full. // Uses fixed buffer, allocations will be sourced from the buffer and fail after buffer is full.
// mem: Memory to use for the memory stack // mem: Memory to use for the memory stack
// return: FALSE if args are invalid, or insufficient memSize. // return: FALSE if args are invalid, or insufficient memSize.
bool Init(u8 *const mem, usize size, u32 flags_); bool Init(u8 *const mem, usize size, u32 flags_ = 0);
// Memory Stack capable of expanding when full, but only if NonExpandable flag is not set. // Memory Stack capable of expanding when full, but only if NonExpandable flag is not set.
bool Init(usize size, bool zeroClear, u32 flags_ = 0, DqnMemAPI *const api = &DQN_DEFAULT_HEAP_ALLOCATOR); bool Init(usize size, bool zeroClear, u32 flags_ = 0, DqnMemAPI *const api = &DQN_DEFAULT_HEAP_ALLOCATOR);
@ -3471,9 +3471,19 @@ void *DqnMemStack::Push(usize size, u8 alignment)
// ============================================================================================= // =============================================================================================
DqnAllocatorMetadata *myMetadata = &this->metadata; DqnAllocatorMetadata *myMetadata = &this->metadata;
usize actualSize = myMetadata->GetAllocationSize(size, alignment); usize actualSize = myMetadata->GetAllocationSize(size, alignment);
u8 const *blockUsedUpTo = this->block->memory + this->block->used; bool needNewBlock = false;
u8 const *blockEnd = this->block->memory + this->block->size; if (!block)
if ((blockUsedUpTo + actualSize) > blockEnd) {
needNewBlock = true;
}
else
{
u8 const *blockUsedUpTo = this->block->memory + this->block->used;
u8 const *blockEnd = this->block->memory + this->block->size;
needNewBlock = ((blockUsedUpTo + actualSize) > blockEnd);
}
if (needNewBlock)
{ {
if (Dqn_BitIsSet(this->flags, Flag::NonExpandable)) if (Dqn_BitIsSet(this->flags, Flag::NonExpandable))
{ {
@ -3581,11 +3591,14 @@ void DqnMemStack::Pop(void *const ptr, bool zeroClear)
void DqnMemStack::Free() void DqnMemStack::Free()
{ {
while (this->block)
this->FreeLastBlock();
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard)) if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
this->metadata.allocations.Free(); this->metadata.allocations.Free();
if (this->memAPI)
{
while (this->block)
this->FreeLastBlock();
}
} }
bool DqnMemStack::FreeMemBlock(DqnMemStack::Block *memBlock) bool DqnMemStack::FreeMemBlock(DqnMemStack::Block *memBlock)

View File

@ -1652,11 +1652,6 @@ void DqnArray_Test()
} }
} }
void DqnMemStack_Test()
{
LOG_HEADER();
}
#ifdef DQN_XPLATFORM_LAYER #ifdef DQN_XPLATFORM_LAYER
void DqnFile_Test() void DqnFile_Test()
{ {
@ -2344,19 +2339,263 @@ void DqnMemSet_Test()
#endif #endif
globalIndent--; globalIndent--;
Log(Status::Ok, "Completed succesfully"); Log(Status::Ok, "MemSet");
} }
FILE_SCOPE void Test() FILE_SCOPE void DqnMemStack_Test()
{ {
DqnMemStack stack = {}; LOG_HEADER();
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true, DqnMemStack::Flag::BoundsGuard));
u8 *result = (u8 *)stack.Push(120, 16); // Check Alignment
stack.Pop(result); if (1)
{
DqnMemStack stack = {};
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true, DqnMemStack::Flag::BoundsGuard));
stack.Free();
int break4 = 5; i32 const ALIGN64 = 64;
(void)result; (void)break4; i32 const ALIGN16 = 16;
i32 const ALIGN4 = 4;
if (1)
{
u8 *result1 = (u8 *)stack.Push(2, ALIGN4);
u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN4);
DQN_ASSERT(result1 == result2);
stack.Pop(result1);
}
if (1)
{
u8 *result1 = (u8 *)stack.Push(120, ALIGN16);
u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN16);
DQN_ASSERT(result1 == result2);
stack.Pop(result1);
}
if (1)
{
u8 *result1 = (u8 *)stack.Push(12, ALIGN64);
u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN64);
DQN_ASSERT(result1 == result2);
stack.Pop(result1);
}
Log(Status::Ok, "Check allocated alignment to 4, 16, 64");
}
// Check Non-Expandable
if (1)
{
DqnMemStack stack = {};
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true, DqnMemStack::Flag::NonExpandable));
auto *result1 = stack.Push(DQN_MEGABYTE(2));
DQN_ASSERT(result1 == nullptr);
DQN_ASSERT(stack.block->prevBlock == nullptr);
stack.Free();
Log(Status::Ok, "Check non-expandable flag prevents expansion.");
}
// Check Expansion
if (1)
{
DqnMemStack stack = {};
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true));
DQN_ASSERT(stack.metadata.GetBoundsGuardSize() == 0);
auto *oldBlock = stack.block;
DQN_ASSERT(oldBlock);
DQN_ASSERT(oldBlock->size == DQN_MEGABYTE(1));
DQN_ASSERT(oldBlock->used == 0);
DQN_ASSERT(oldBlock->prevBlock == nullptr);
auto *result1 = stack.Push(DQN_MEGABYTE(2));
DQN_ASSERT(result1);
DQN_ASSERT(stack.block->prevBlock == oldBlock);
DQN_ASSERT(stack.block != oldBlock);
Log(Status::Ok, "Check memory stack allocates additional memory blocks.");
stack.Free();
}
// Temporary Regions
if (1)
{
DqnMemStack stack = {};
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true));
// Check temporary regions
if (1)
{
DqnMemStack::Block *blockToReturnTo = stack.block;
auto usedBefore = blockToReturnTo->used;
if (1)
{
auto memGuard1 = stack.TempRegionGuard();
auto *result2 = stack.Push(100);
auto *result3 = stack.Push(100);
auto *result4 = stack.Push(100);
DQN_ASSERT(result2 && result3 && result4);
DQN_ASSERT(stack.block->used > usedBefore);
DQN_ASSERT(stack.block->memory == blockToReturnTo->memory);
// Force allocation of new block
auto *result5 = stack.Push(DQN_MEGABYTE(5));
DQN_ASSERT(result5);
DQN_ASSERT(stack.block != blockToReturnTo);
DQN_ASSERT(stack.tempRegionCount == 1);
}
DQN_ASSERT(stack.block == blockToReturnTo);
DQN_ASSERT(stack.block->used == usedBefore);
}
// Check temporary regions keep state
if (1)
{
DqnMemStack::Block *blockToReturnTo = stack.block;
auto usedBefore = blockToReturnTo->used;
if (1)
{
auto memGuard1 = stack.TempRegionGuard();
auto *result2 = stack.Push(100);
auto *result3 = stack.Push(100);
auto *result4 = stack.Push(100);
DQN_ASSERT(result2 && result3 && result4);
DQN_ASSERT(stack.block->used > usedBefore);
DQN_ASSERT(stack.block->memory == blockToReturnTo->memory);
// Force allocation of new block
auto *result5 = stack.Push(DQN_MEGABYTE(5));
DQN_ASSERT(result5);
DQN_ASSERT(stack.block != blockToReturnTo);
DQN_ASSERT(stack.tempRegionCount == 1);
memGuard1.keepChanges = true;
}
DQN_ASSERT(stack.block != blockToReturnTo);
DQN_ASSERT(stack.block->prevBlock == blockToReturnTo);
DQN_ASSERT(stack.tempRegionCount == 0);
}
Log(Status::Ok, "Temporary regions return state and/or keep changes if requested.");
stack.Free();
}
// Check Fixed Mem Init
if (1)
{
// Check fail on insufficient size
if (1)
{
u8 memBuf[sizeof(DqnMemStack::Block) - 1] = {};
DqnMemStack stack = {};
auto result = stack.Init(&(memBuf[0]), DQN_ARRAY_COUNT(memBuf));
DQN_ASSERT(result == false);
DQN_ASSERT(stack.block == nullptr);
stack.Free();
}
// Check success
if (1)
{
i32 const bufSize = sizeof(DqnMemStack::Block) * 5;
u8 memBuf[bufSize] = {};
DqnMemStack stack = {};
auto result = stack.Init(memBuf, bufSize);
DQN_ASSERT(result == true);
DQN_ASSERT(stack.block);
DQN_ASSERT(stack.block->prevBlock == false);
DQN_ASSERT(stack.tempRegionCount == 0);
DQN_ASSERT(stack.flags == DqnMemStack::Flag::NonExpandable);
auto *result1 = stack.Push(32);
DQN_ASSERT(result1);
stack.Pop(result1);
auto *result2 = stack.Push(bufSize * 2);
DQN_ASSERT(result2 == nullptr);
DQN_ASSERT(stack.block);
DQN_ASSERT(stack.block->prevBlock == false);
DQN_ASSERT(stack.tempRegionCount == 0);
DQN_ASSERT(stack.flags == DqnMemStack::Flag::NonExpandable);
stack.Free();
}
Log(Status::Ok, "Checked fixed mem initialisation");
}
// Check Freeing Blocks
if (1)
{
DqnMemStack stack = {};
usize size = 32;
usize additionalSize = DqnMemStack::MINIMUM_BLOCK_SIZE;
DqnMemAPI heap = DqnMemAPI::HeapAllocator();
DQN_ASSERT(stack.Init(size, true, 0, &heap));
auto *block1 = stack.block;
size += additionalSize;
auto *result1 = stack.Push(size);
auto *block2 = stack.block;
size += additionalSize;
auto *result2 = stack.Push(size);
auto *block3 = stack.block;
size += additionalSize;
auto *result3 = stack.Push(size);
auto *block4 = stack.block;
size += additionalSize;
auto *result4 = stack.Push(size);
auto *block5 = stack.block;
DQN_ASSERT(result1 && result2 && result3 && result4);
DQN_ASSERT(block1 && block2 && block3 && block4 && block5);
DQN_ASSERT(block5->prevBlock == block4);
DQN_ASSERT(block4->prevBlock == block3);
DQN_ASSERT(block3->prevBlock == block2);
DQN_ASSERT(block2->prevBlock == block1);
DQN_ASSERT(block1->prevBlock == nullptr);
DQN_ASSERT(stack.FreeMemBlock(block4));
DQN_ASSERT(stack.block == block5);
DQN_ASSERT(block5->prevBlock == block3);
DQN_ASSERT(block3->prevBlock == block2);
DQN_ASSERT(block2->prevBlock == block1);
DQN_ASSERT(block1->prevBlock == nullptr);
DQN_ASSERT(stack.FreeMemBlock(block5));
DQN_ASSERT(stack.block == block3);
DQN_ASSERT(block3->prevBlock == block2);
DQN_ASSERT(block2->prevBlock == block1);
DQN_ASSERT(block1->prevBlock == nullptr);
stack.Free();
DQN_ASSERT(stack.memAPI->bytesAllocated == 0);
DQN_ASSERT(stack.block == nullptr);
Log(Status::Ok, "Check freeing arbitrary blocks and freeing");
}
// Check bounds guard places magic values
if (1)
{
DqnMemStack stack = {};
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true, DqnMemStack::Flag::BoundsGuard));
auto *result = stack.Push(64);
// TODO(doyle): check head and tail are adjacent to the bounds of the allocation
u32 *head = stack.metadata.PtrToHeadBoundsGuard((u8 *)result);
u32 *tail = stack.metadata.PtrToTailBoundsGuard((u8 *)result);
DQN_ASSERT(*head == DqnAllocatorMetadata::GUARD_VALUE);
DQN_ASSERT(*tail == DqnAllocatorMetadata::GUARD_VALUE);
Log(Status::Ok, "Bounds guards are placed adjacent and have magic values.");
}
} }
int main(void) int main(void)
@ -2364,8 +2603,7 @@ int main(void)
globalIndent = 1; globalIndent = 1;
globalNewLine = true; globalNewLine = true;
Test(); DqnMemStack_Test();
DqnString_Test(); DqnString_Test();
DqnChar_Test(); DqnChar_Test();
DqnRnd_Test(); DqnRnd_Test();