Add CPP member functions to DqnMemStack

This commit is contained in:
Doyle Thai 2017-06-23 15:30:21 +10:00
parent 187fc14d02
commit 9bb8efcf9d
3 changed files with 120 additions and 35 deletions

105
dqn.h
View File

@ -53,7 +53,7 @@
// #DqnSprintf Cross-platform Sprintf Implementation (Public Domain lib stb_sprintf)
////////////////////////////////////////////////////////////////////////////////
// Platform Checks
// Global Preprocessor Checks
////////////////////////////////////////////////////////////////////////////////
// This needs to be above the portable layer so that, if the user requests
// a platform implementation, platform specific implementations in the portable
@ -66,6 +66,10 @@
#define DQN_UNIX_PLATFORM
#endif
#ifdef __cplusplus
#define DQN_CPP_MODE
#endif
////////////////////////////////////////////////////////////////////////////////
// #Portable Code
////////////////////////////////////////////////////////////////////////////////
@ -77,10 +81,6 @@
#define DQN_FILE_SCOPE
#endif
#ifdef __cplusplus
#define DQN_CPP_MODE
#endif
#include <stdint.h> // For standard types
#include <stddef.h> // For standard types
#include <string.h> // memmove
@ -210,10 +210,37 @@ enum DqnMemStackFlag
typedef struct DqnMemStack
{
struct DqnMemStackBlock *block;
u32 flags;
i32 tempRegionCount;
u32 byteAlign;
#if defined(DQN_CPP_MODE)
// Initialisation API
bool InitWithFixedMem (u8 *const mem, const size_t memSize, const u32 byteAlignment = 4);
bool InitWithFixedSize(const size_t size, const bool zeroClear, const u32 byteAlignment = 4);
bool Init (const size_t size, const bool zeroClear, const u32 byteAlignment = 4);
// Memory API
void *Push(size_t size);
void Pop (void *const ptr, size_t size);
void Free();
bool FreeMemBlock (DqnMemStackBlock *memBlock);
bool FreeLastBlock ();
void ClearCurrBlock(const bool zeroClear);
// Temporary Regions API
struct DqnMemStackTempRegion TempRegionBegin();
void TempRegionEnd (DqnMemStackTempRegion region);
// Scoped Temporary Regions API
struct DqnMemStackTempRegionScoped TempRegionScoped(bool *const succeeded);
// Advanced API
DqnMemStackBlock *AllocateCompatibleBlock(size_t size);
bool AttachBlock (DqnMemStackBlock *const newBlock);
bool DetachBlock (DqnMemStackBlock *const detachBlock);
void FreeDetachedBlock (DqnMemStackBlock *memBlock);
#endif
} DqnMemStack;
////////////////////////////////////////////////////////////////////////////////
@ -241,6 +268,7 @@ DQN_FILE_SCOPE bool DqnMemStack_Init (DqnMemStack *const stack, size_t size, con
// DqnMemStack Memory Operations
////////////////////////////////////////////////////////////////////////////////
// Allocate memory from the MemStack.
// size: "size" gets aligned to the byte alignment of the stack.
// return: NULL if out of space OR stack is using fixed memory/size OR stack full and platform malloc fails.
DQN_FILE_SCOPE void *DqnMemStack_Push (DqnMemStack *const stack, size_t size);
@ -252,7 +280,7 @@ DQN_FILE_SCOPE void DqnMemStack_Free (DqnMemStack *const stack);
// Frees the specified block belonging to the stack.
// return: FALSE if block doesn't belong this into calls DqnMem_Free() or invalid args.
DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemStackBlock *block);
DQN_FILE_SCOPE bool DqnMemStack_FreeMemBlock(DqnMemStack *const stack, DqnMemStackBlock *memBlock);
// Frees the last-most memory block. If last block, free that block making the MemStack blockless.
// Next allocate will attach a block.
@ -287,8 +315,7 @@ DQN_FILE_SCOPE void DqnMemStackTempRegion_End (DqnMemStackTempRegion region);
#ifdef DQN_CPP_MODE
struct DqnMemStackTempRegionScoped
{
bool isInit;
DqnMemStackTempRegionScoped(DqnMemStack *const stack);
DqnMemStackTempRegionScoped(DqnMemStack *const stack, bool *const succeeded);
~DqnMemStackTempRegionScoped();
private:
@ -317,8 +344,8 @@ DQN_FILE_SCOPE bool DqnMemStack_AttachBlock (DqnMemStack
DQN_FILE_SCOPE bool DqnMemStack_DetachBlock (DqnMemStack *const stack, DqnMemStackBlock *const detachBlock);
// (IMPORTANT) Should only be used to free blocks that haven't been attached! Attached blocks should
// be freed using DqnMemStack_FreeStackBlock().
DQN_FILE_SCOPE void DqnMemStack_FreeBlock(DqnMemStackBlock *block);
// be freed using DqnMemStack_FreeMemBlock().
DQN_FILE_SCOPE void DqnMemStack_FreeDetachedBlock(DqnMemStackBlock *memBlock);
////////////////////////////////////////////////////////////////////////////////
// #DqnMemAPI Public API - Custom memory API for Dqn Data Structures
@ -927,9 +954,10 @@ enum DqnFileAction
typedef struct DqnFile
{
u32 permissionFlags;
void *handle;
size_t size;
u32 permissionFlags;
} DqnFile;
// NOTE: W(ide) versions of functions only work on Win32, since Unix is UTF-8 compatible.
@ -1682,12 +1710,45 @@ DqnMemStackInternal_AllocateBlock(u32 byteAlign, size_t size)
return result;
}
////////////////////////////////////////////////////////////////////////////////
// #DqnMemStack CPP Implementation
////////////////////////////////////////////////////////////////////////////////
#if defined(DQN_CPP_MODE)
bool DqnMemStack::InitWithFixedMem (u8 *const mem, const size_t memSize, const u32 byteAlignment) { return DqnMemStack_InitWithFixedMem (this, mem, memSize, byteAlign); }
bool DqnMemStack::InitWithFixedSize(const size_t size, const bool zeroClear, const u32 byteAlignment) { return DqnMemStack_InitWithFixedSize(this, size, zeroClear, byteAlign); }
bool DqnMemStack::Init (const size_t size, const bool zeroClear, const u32 byteAlignment) { return DqnMemStack_Init (this, size, zeroClear, byteAlign); }
void *DqnMemStack::Push(size_t size) { return DqnMemStack_Push(this, size); }
void DqnMemStack::Pop (void *const ptr, size_t size) { DqnMemStack_Pop (this, ptr, size); }
void DqnMemStack::Free() { DqnMemStack_Free(this); }
bool DqnMemStack::FreeMemBlock(DqnMemStackBlock *memBlock) { return DqnMemStack_FreeMemBlock(this, block); }
bool DqnMemStack::FreeLastBlock() { return DqnMemStack_FreeLastBlock(this); }
void DqnMemStack::ClearCurrBlock(const bool zeroClear) { DqnMemStack_ClearCurrBlock(this, zeroClear); }
DqnMemStackTempRegion DqnMemStack::TempRegionBegin()
{
// NOTE: Should always succeed since the stack is guaranteed to exist.
DqnMemStackTempRegion result = {};
bool succeeded = DqnMemStackTempRegion_Begin(&result, this);
DQN_ASSERT_HARD(succeeded);
return result;
}
void DqnMemStack::TempRegionEnd(DqnMemStackTempRegion region) { DqnMemStackTempRegion_End(region); }
DqnMemStackTempRegionScoped DqnMemStack::TempRegionScoped(bool *const succeeded) { return DqnMemStackTempRegionScoped(this, succeeded); }
DqnMemStackBlock *DqnMemStack::AllocateCompatibleBlock(size_t size) { return DqnMemStack_AllocateCompatibleBlock(this, size); }
bool DqnMemStack::AttachBlock (DqnMemStackBlock *const newBlock) { return DqnMemStack_AttachBlock (this, newBlock); }
bool DqnMemStack::DetachBlock (DqnMemStackBlock *const detachBlock) { return DqnMemStack_DetachBlock (this, detachBlock); }
void DqnMemStack::FreeDetachedBlock (DqnMemStackBlock *memBlock) { DqnMemStack_FreeDetachedBlock (memBlock); }
#endif
////////////////////////////////////////////////////////////////////////////////
// #DqnMemStack Initialisation Implementation
////////////////////////////////////////////////////////////////////////////////
DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem(DqnMemStack *const stack, u8 *const mem,
const size_t memSize, const u32 byteAlign)
{
// TODO(doyle): Logging
if (!stack || !mem) return false;
if (!DQN_ASSERT_MSG(
@ -1837,14 +1898,14 @@ DQN_FILE_SCOPE void DqnMemStack_Free(DqnMemStack *stack)
stack->flags &= ~DqnMemStackFlag_IsNotExpandable;
}
DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemStackBlock *block)
DQN_FILE_SCOPE bool DqnMemStack_FreeMemBlock(DqnMemStack *const stack, DqnMemStackBlock *memBlock)
{
if (!stack || !block || !stack->block) return false;
if (!stack || !memBlock || !stack->block) return false;
if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser) return false;
DqnMemStackBlock **blockPtr = &stack->block;
while (*blockPtr && (*blockPtr) != block)
while (*blockPtr && (*blockPtr) != memBlock)
blockPtr = &((*blockPtr)->prevBlock);
if (*blockPtr)
@ -1863,7 +1924,7 @@ DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemS
DQN_FILE_SCOPE bool DqnMemStack_FreeLastBlock(DqnMemStack *const stack)
{
bool result = DqnMemStack_FreeStackBlock(stack, stack->block);
bool result = DqnMemStack_FreeMemBlock(stack, stack->block);
return result;
}
@ -1913,10 +1974,10 @@ DQN_FILE_SCOPE void DqnMemStackTempRegion_End(DqnMemStackTempRegion region)
}
#ifdef DQN_CPP_MODE
DqnMemStackTempRegionScoped::DqnMemStackTempRegionScoped(DqnMemStack *const stack)
DqnMemStackTempRegionScoped::DqnMemStackTempRegionScoped(DqnMemStack *const stack, bool *const succeeded)
{
this->isInit = DqnMemStackTempRegion_Begin(&this->tempMemStack, stack);
DQN_ASSERT(this->isInit);
bool result = DqnMemStackTempRegion_Begin(&this->tempMemStack, stack);
if (succeeded) *succeeded = result;
}
DqnMemStackTempRegionScoped::~DqnMemStackTempRegionScoped()
@ -1976,10 +2037,10 @@ DQN_FILE_SCOPE bool DqnMemStack_DetachBlock(DqnMemStack *const stack,
return true;
}
DQN_FILE_SCOPE void DqnMemStack_FreeBlock(DqnMemStackBlock *block)
DQN_FILE_SCOPE void DqnMemStack_FreeDetachedBlock(DqnMemStackBlock *memBlock)
{
if (!block) return;
DqnMem_Free(block);
if (!memBlock) return;
DqnMem_Free(memBlock);
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -1399,7 +1399,7 @@ void MemStackTest()
fakeBlock.memory = fakeBlockMem;
fakeBlock.size = DQN_ARRAY_COUNT(fakeBlockMem);
fakeBlock.used = 0;
DQN_ASSERT(!DqnMemStack_FreeStackBlock(&stack, &fakeBlock));
DQN_ASSERT(!DqnMemStack_FreeMemBlock(&stack, &fakeBlock));
// Ensure that the actual blocks are still valid and freeing did nothing
DQN_ASSERT(firstBlock->size == firstBlockSize);
@ -1432,7 +1432,7 @@ void MemStackTest()
DQN_ASSERT(fourth[i] == 'f');
// Free the first block
DqnMemStack_FreeStackBlock(&stack, firstBlock);
DqnMemStack_FreeMemBlock(&stack, firstBlock);
// Revalidate state
DQN_ASSERT(secondBlock->size == secondBlockSize);
@ -1458,7 +1458,7 @@ void MemStackTest()
DQN_ASSERT(fourth[i] == 'f');
// Free the third block
DqnMemStack_FreeStackBlock(&stack, thirdBlock);
DqnMemStack_FreeMemBlock(&stack, thirdBlock);
// Revalidate state
DQN_ASSERT(secondBlock->size == secondBlockSize);
@ -1478,7 +1478,7 @@ void MemStackTest()
DQN_ASSERT(fourth[i] == 'f');
// Free the second block
DqnMemStack_FreeStackBlock(&stack, secondBlock);
DqnMemStack_FreeMemBlock(&stack, secondBlock);
// Revalidate state
DQN_ASSERT(fourthBlock->size == fourthBlockSize);
@ -1495,6 +1495,9 @@ void MemStackTest()
// Test pop
if (1)
{
// Test aligned pop
if (1)
{
DqnMemStack stack = {};
DqnMemStack_Init(&stack, DQN_KILOBYTE(1), true);
@ -1505,6 +1508,24 @@ void MemStackTest()
DQN_ASSERT(DqnMemStack_Pop(&stack, alloc, allocSize));
DQN_ASSERT(stack.block->used == 0);
DqnMemStack_Free(&stack);
}
// Test pop on a non-byte aligned allocation. This checks to see if
// Pop() doesn't naiively forget to re-byte align the passed in size.
if (1)
{
DqnMemStack stack = {};
DqnMemStack_Init(&stack, DQN_KILOBYTE(1), true);
size_t allocSize = 1;
void *alloc = DqnMemStack_Push(&stack, allocSize);
DQN_ASSERT(stack.block->used == DQN_ALIGN_POW_N(allocSize, stack.byteAlign));
DQN_ASSERT(DqnMemStack_Pop(&stack, alloc, allocSize));
DQN_ASSERT(stack.block->used == 0);
DqnMemStack_Free(&stack);
}
}
}
@ -1641,6 +1662,9 @@ void FileTest()
DqnMemStack_Free(&memStack);
}
////////////////////////////////////////////////////////////////////////////
// Test directory listing
////////////////////////////////////////////////////////////////////////////
if (1)
{
u32 numFiles;

View File

@ -21,7 +21,7 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{87785192-6F49-4F85-AA0D-F0AFA5CCCDDA}.Release|x86.ActiveCfg = Release|x64
{87785192-6F49-4F85-AA0D-F0AFA5CCCDDA}.Release|x86.ActiveCfg = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE