Switch membuffer to memstack and add pop
This commit is contained in:
parent
034de12235
commit
25744855ee
321
dqn.h
321
dqn.h
@ -67,92 +67,93 @@ DQN_FILE_SCOPE void *DqnMem_Realloc(void *memory, size_t newSize);
|
||||
DQN_FILE_SCOPE void DqnMem_Free (void *memory);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DqnMemBuffer - Memory Buffer, For push buffer/ptr memory style management
|
||||
// DqnMemStack - Memory Stack, For push stack/ptr memory style management
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DqnMemBuffer is a data structure to dynamically allocate memory in a stack
|
||||
// DqnMemStack is a data structure to dynamically allocate memory in a stack
|
||||
// like style. It pre-allocates a block of memory in init and sub-allocates from
|
||||
// this block to take advantage of memory locality.
|
||||
|
||||
// When an allocation requires a larger amount of memory than available in the
|
||||
// block then the MemBuffer will allocate a new block of sufficient size for
|
||||
// you in DqnMemBuffer_Allocate(..). This _DOES_ mean that there will be wasted
|
||||
// block then the MemStack will allocate a new block of sufficient size for
|
||||
// you in DqnMemStack_Allocate(..). This _DOES_ mean that there will be wasted
|
||||
// space at the end of each block and is a tradeoff for memory locality against
|
||||
// optimal space usage.
|
||||
|
||||
// How To Use:
|
||||
// 1. Create a DqnMemBuffer struct and pass it into an initialisation function
|
||||
// 1. Create a DqnMemStack struct and pass it into an initialisation function
|
||||
// - InitWithFixedMem() allows you to pass in your own memory which is
|
||||
// converted to a memory block. This disables dynamic allocation.
|
||||
// NOTE: Space is reserved in the given memory for MemBufferBlock metadata.
|
||||
// NOTE: Space is reserved in the given memory for MemStackBlock metadata.
|
||||
|
||||
// - InitWithFixedSize() allows you to to disable dynamic allocations and
|
||||
// sub-allocate from the initial MemBuffer allocation size only.
|
||||
// sub-allocate from the initial MemStack allocation size only.
|
||||
|
||||
// 2. Use DqnMemBuffer_Allocate(..) to allocate memory for use.
|
||||
// - "Freeing" memory is dealt by creating temporary MemBuffers or using the
|
||||
// 2. Use DqnMemStack_Allocate(..) to allocate memory for use.
|
||||
// - "Freeing" memory is dealt by creating temporary MemStacks or using the
|
||||
// BeginTempRegion and EndTempRegion functions. Specifically freeing
|
||||
// individual items is typically not generalisable in this scheme.
|
||||
|
||||
typedef struct DqnMemBufferBlock
|
||||
typedef struct DqnMemStackBlock
|
||||
{
|
||||
u8 *memory;
|
||||
size_t used;
|
||||
size_t size;
|
||||
|
||||
DqnMemBufferBlock *prevBlock;
|
||||
} DqnMemBufferBlock;
|
||||
DqnMemStackBlock *prevBlock;
|
||||
} DqnMemStackBlock;
|
||||
|
||||
enum DqnMemBufferFlag
|
||||
enum DqnMemStackFlag
|
||||
{
|
||||
DqnMemBufferFlag_IsNotExpandable = (1 << 0),
|
||||
DqnMemBufferFlag_IsFixedMemoryFromUser = (1 << 1), // NOTE(doyle): Required to indicate we CAN'T free this memory when free is called.
|
||||
DqnMemStackFlag_IsNotExpandable = (1 << 0),
|
||||
DqnMemStackFlag_IsFixedMemoryFromUser = (1 << 1), // NOTE(doyle): Required to indicate we CAN'T free this memory when free is called.
|
||||
};
|
||||
|
||||
typedef struct DqnMemBuffer
|
||||
typedef struct DqnMemStack
|
||||
{
|
||||
DqnMemBufferBlock *block;
|
||||
DqnMemStackBlock *block;
|
||||
|
||||
u32 flags;
|
||||
i32 tempBufferCount;
|
||||
i32 tempStackCount;
|
||||
u32 byteAlign;
|
||||
} DqnMemBuffer;
|
||||
} DqnMemStack;
|
||||
|
||||
typedef struct DqnTempBuffer
|
||||
typedef struct DqnTempMemStack
|
||||
{
|
||||
DqnMemBuffer *buffer;
|
||||
DqnMemBufferBlock *startingBlock;
|
||||
DqnMemStack *stack;
|
||||
DqnMemStackBlock *startingBlock;
|
||||
size_t used;
|
||||
|
||||
} DqnTempBuffer;
|
||||
} DqnTempMemStack;
|
||||
|
||||
DQN_FILE_SCOPE bool DqnMemBuffer_InitWithFixedMem (DqnMemBuffer *const buffer, u8 *const mem, const size_t memSize, const u32 byteAlign = 4); // Use preallocated memory, no further allocations, returns NULL on allocate if out of space
|
||||
DQN_FILE_SCOPE bool DqnMemBuffer_InitWithFixedSize(DqnMemBuffer *const buffer, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Single allocation from platform, no further allocations, returns NULL of allocate if out of space
|
||||
DQN_FILE_SCOPE bool DqnMemBuffer_Init (DqnMemBuffer *const buffer, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Allocates from platform dynamically as space runs out
|
||||
DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem (DqnMemStack *const stack, u8 *const mem, const size_t memSize, const u32 byteAlign = 4); // Use preallocated memory, no further allocations, returns NULL on allocate if out of space
|
||||
DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedSize(DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Single allocation from platform, no further allocations, returns NULL of allocate if out of space
|
||||
DQN_FILE_SCOPE bool DqnMemStack_Init (DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Allocates from platform dynamically as space runs out
|
||||
|
||||
DQN_FILE_SCOPE void *DqnMemBuffer_Allocate (DqnMemBuffer *const buffer, size_t size); // Returns NULL if out of space and buffer is using fixed memory/size, or platform allocation fails
|
||||
DQN_FILE_SCOPE void DqnMemBuffer_Free (DqnMemBuffer *const buffer); // Frees all blocks belonging to this buffer
|
||||
DQN_FILE_SCOPE bool DqnMemBuffer_FreeBufferBlock(DqnMemBuffer *const buffer, DqnMemBufferBlock *block); // Frees the specified block, returns false if block doesn't belong
|
||||
DQN_FILE_SCOPE bool DqnMemBuffer_FreeLastBlock (DqnMemBuffer *const buffer); // Frees the last-most memory block. If last block, free that block, next allocate will attach a block.
|
||||
DQN_FILE_SCOPE void DqnMemBuffer_ClearCurrBlock (DqnMemBuffer *const buffer, const bool zeroClear); // Reset the current memory block usage to 0
|
||||
DQN_FILE_SCOPE void *DqnMemStack_Allocate (DqnMemStack *const stack, size_t size); // Returns NULL if out of space and stack is using fixed memory/size, or platform allocation fails
|
||||
DQN_FILE_SCOPE bool DqnMemStack_Pop (DqnMemStack *const stack, void *ptr, size_t size); // Frees the given ptr. It MUST be the last allocated item in the stack
|
||||
DQN_FILE_SCOPE void DqnMemStack_Free (DqnMemStack *const stack); // Frees all blocks belonging to this stack
|
||||
DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock (DqnMemStack *const stack, DqnMemStackBlock *block); // Frees the specified block, returns false if block doesn't belong
|
||||
DQN_FILE_SCOPE bool DqnMemStack_FreeLastBlock (DqnMemStack *const stack); // Frees the last-most memory block. If last block, free that block, next allocate will attach a block.
|
||||
DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock (DqnMemStack *const stack, const bool zeroClear); // Reset the current memory block usage to 0
|
||||
|
||||
// TempBuffer is only required for the function. Once BeginTempRegion() is called, subsequent allocation calls can be made using the original buffer.
|
||||
// Upon EndTempRegion() the original buffer will free any additional blocks it allocated during the temp region and revert to the original
|
||||
// TempMemStack is only required for the function. Once BeginTempRegion() is called, subsequent allocation calls can be made using the original stack.
|
||||
// Upon EndTempRegion() the original stack will free any additional blocks it allocated during the temp region and revert to the original
|
||||
// state before BeginTempRegion() was called.
|
||||
// WARNING: Any calls to Free/Clear functions in a TempRegion will invalidate and trash the buffer structure.
|
||||
// WARNING: Any calls to Free/Clear functions in a TempRegion will invalidate and trash the stack structure.
|
||||
// TODO(doyle): Look into a way of disallowing calls to free/clear in temp regions
|
||||
DQN_FILE_SCOPE DqnTempBuffer DqnMemBuffer_BeginTempRegion(DqnMemBuffer *const buffer);
|
||||
DQN_FILE_SCOPE void DqnMemBuffer_EndTempRegion (DqnTempBuffer tempBuffer);
|
||||
DQN_FILE_SCOPE DqnTempMemStack DqnMemStack_BeginTempRegion(DqnMemStack *const stack);
|
||||
DQN_FILE_SCOPE void DqnMemStack_EndTempRegion (DqnTempMemStack tempstack);
|
||||
|
||||
// (OPTIONAL) DqnMemBuffer Advanced API
|
||||
// (OPTIONAL) DqnMemStack Advanced API
|
||||
// This is useful for forcing a new block to be used. AllocateCompatibleBlock
|
||||
// will fail if the supplied buffer has flags set such that the buffer is not
|
||||
// will fail if the supplied stack has flags set such that the stack is not
|
||||
// allowed to have new blocks.
|
||||
DQN_FILE_SCOPE DqnMemBufferBlock *DqnMemBuffer_AllocateCompatibleBlock(const DqnMemBuffer *const buffer, size_t size);
|
||||
DQN_FILE_SCOPE bool DqnMemBuffer_AttachBlock (DqnMemBuffer *const buffer, DqnMemBufferBlock *const newBlock);
|
||||
DQN_FILE_SCOPE DqnMemStackBlock *DqnMemStack_AllocateCompatibleBlock(const DqnMemStack *const stack, size_t size);
|
||||
DQN_FILE_SCOPE bool DqnMemStack_AttachBlock (DqnMemStack *const stack, DqnMemStackBlock *const newBlock);
|
||||
|
||||
// (IMPORTANT) Should only be used to free blocks that haven't been attached!
|
||||
// Attached blocks should be freed using FreeBufferBlock().
|
||||
DQN_FILE_SCOPE void DqnMemBuffer_FreeBlock(DqnMemBufferBlock *block);
|
||||
// Attached blocks should be freed using FreeStackBlock().
|
||||
DQN_FILE_SCOPE void DqnMemStack_FreeBlock(DqnMemStackBlock *block);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DqnMemAPI - Memory API, For using custom allocators
|
||||
@ -507,8 +508,15 @@ typedef union DqnV3
|
||||
f32 e[3];
|
||||
} DqnV3;
|
||||
|
||||
// Create a vector using ints and typecast to floats
|
||||
DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z);
|
||||
typedef union DqnV3i
|
||||
{
|
||||
struct { i32 x, y, z; };
|
||||
struct { i32 r, g, b; };
|
||||
i32 e[3];
|
||||
} DqnV3i;
|
||||
|
||||
// DqnV3
|
||||
DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z); // Create a vector using ints and typecast to floats
|
||||
DQN_FILE_SCOPE DqnV3 DqnV3_3f(f32 x, f32 y, f32 z);
|
||||
|
||||
DQN_FILE_SCOPE DqnV3 DqnV3_Add (DqnV3 a, DqnV3 b);
|
||||
@ -532,6 +540,10 @@ DQN_FILE_SCOPE inline DqnV3 &operator-=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_S
|
||||
DQN_FILE_SCOPE inline DqnV3 &operator+=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Add (a, b)); }
|
||||
DQN_FILE_SCOPE inline bool operator==(DqnV3 a, DqnV3 b) { return DqnV3_Equals (a, b); }
|
||||
|
||||
// DqnV3i
|
||||
DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z);
|
||||
DQN_FILE_SCOPE DqnV3i DqnV3i_3f(f32 x, f32 y, f32 z);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Vec4
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -1328,17 +1340,17 @@ DQN_FILE_SCOPE void DqnMem_Free(void *memory)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DqnMemBuffer - Memory API, For using custom allocators
|
||||
// DqnMemStack - Memory API, For using custom allocators
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
DQN_FILE_SCOPE DqnMemBufferBlock *
|
||||
DqnMemBuffer_AllocateBlockInternal(u32 byteAlign, size_t size)
|
||||
DQN_FILE_SCOPE DqnMemStackBlock *
|
||||
DqnMemStack_AllocateBlockInternal(u32 byteAlign, size_t size)
|
||||
{
|
||||
size_t alignedSize = DQN_ALIGN_POW_N(size, byteAlign);
|
||||
size_t totalSize = alignedSize + sizeof(DqnMemBufferBlock) + (byteAlign -1);
|
||||
size_t totalSize = alignedSize + sizeof(DqnMemStackBlock) + (byteAlign -1);
|
||||
// NOTE(doyle): Total size includes another (byteAlign-1) since we also want
|
||||
// to align the base pointer to memory that we receive.
|
||||
|
||||
DqnMemBufferBlock *result = (DqnMemBufferBlock *)DqnMem_Calloc(totalSize);
|
||||
DqnMemStackBlock *result = (DqnMemStackBlock *)DqnMem_Calloc(totalSize);
|
||||
if (!result) return NULL;
|
||||
|
||||
result->memory = (u8 *)DQN_ALIGN_POW_N((u8 *)result + sizeof(*result), byteAlign);
|
||||
@ -1347,110 +1359,110 @@ DqnMemBuffer_AllocateBlockInternal(u32 byteAlign, size_t size)
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE DqnMemBufferBlock *
|
||||
DqnMemBuffer_AllocateCompatibleBlock(const DqnMemBuffer *const buffer, size_t size)
|
||||
DQN_FILE_SCOPE DqnMemStackBlock *
|
||||
DqnMemStack_AllocateCompatibleBlock(const DqnMemStack *const stack, size_t size)
|
||||
{
|
||||
if (!buffer) return NULL;
|
||||
if (buffer->flags & DqnMemBufferFlag_IsFixedMemoryFromUser) return NULL;
|
||||
if (buffer->flags & DqnMemBufferFlag_IsNotExpandable) return NULL;
|
||||
if (!stack) return NULL;
|
||||
if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser) return NULL;
|
||||
if (stack->flags & DqnMemStackFlag_IsNotExpandable) return NULL;
|
||||
|
||||
DqnMemBufferBlock *block =
|
||||
DqnMemBuffer_AllocateBlockInternal(buffer->byteAlign, size);
|
||||
DqnMemStackBlock *block =
|
||||
DqnMemStack_AllocateBlockInternal(stack->byteAlign, size);
|
||||
return block;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE bool DqnMemBuffer_AttachBlock(DqnMemBuffer *const buffer,
|
||||
DqnMemBufferBlock *const newBlock)
|
||||
DQN_FILE_SCOPE bool DqnMemStack_AttachBlock(DqnMemStack *const stack,
|
||||
DqnMemStackBlock *const newBlock)
|
||||
{
|
||||
if (!buffer || !newBlock) return false;
|
||||
if (buffer->flags & DqnMemBufferFlag_IsFixedMemoryFromUser) return false;
|
||||
if (buffer->flags & DqnMemBufferFlag_IsNotExpandable) return false;
|
||||
if (!stack || !newBlock) return false;
|
||||
if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser) return false;
|
||||
if (stack->flags & DqnMemStackFlag_IsNotExpandable) return false;
|
||||
|
||||
newBlock->prevBlock = buffer->block;
|
||||
buffer->block = newBlock;
|
||||
newBlock->prevBlock = stack->block;
|
||||
stack->block = newBlock;
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE void DqnMemBuffer_FreeBlock(DqnMemBufferBlock *block)
|
||||
DQN_FILE_SCOPE void DqnMemStack_FreeBlock(DqnMemStackBlock *block)
|
||||
{
|
||||
if (!block) return;
|
||||
DqnMem_Free(block);
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE bool DqnMemBuffer_InitWithFixedMem(DqnMemBuffer *const buffer,
|
||||
DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem(DqnMemStack *const stack,
|
||||
u8 *const mem,
|
||||
const size_t memSize,
|
||||
const u32 byteAlign)
|
||||
{
|
||||
if (!buffer || !mem) return false;
|
||||
DQN_ASSERT(!buffer->block);
|
||||
if (!stack || !mem) return false;
|
||||
DQN_ASSERT(!stack->block);
|
||||
|
||||
// TODO(doyle): Better logging
|
||||
if (memSize < sizeof(DqnMemBufferBlock))
|
||||
if (memSize < sizeof(DqnMemStackBlock))
|
||||
DQN_ASSERT(DQN_INVALID_CODE_PATH);
|
||||
|
||||
buffer->block = (DqnMemBufferBlock *)mem;
|
||||
buffer->block->memory = mem + sizeof(DqnMemBufferBlock);
|
||||
buffer->block->used = 0;
|
||||
buffer->block->size = memSize - sizeof(DqnMemBufferBlock);
|
||||
buffer->flags = (DqnMemBufferFlag_IsFixedMemoryFromUser | DqnMemBufferFlag_IsNotExpandable);
|
||||
stack->block = (DqnMemStackBlock *)mem;
|
||||
stack->block->memory = mem + sizeof(DqnMemStackBlock);
|
||||
stack->block->used = 0;
|
||||
stack->block->size = memSize - sizeof(DqnMemStackBlock);
|
||||
stack->flags = (DqnMemStackFlag_IsFixedMemoryFromUser | DqnMemStackFlag_IsNotExpandable);
|
||||
|
||||
const u32 DEFAULT_ALIGNMENT = 4;
|
||||
buffer->tempBufferCount = 0;
|
||||
buffer->byteAlign = (byteAlign == 0) ? DEFAULT_ALIGNMENT : byteAlign;
|
||||
stack->tempStackCount = 0;
|
||||
stack->byteAlign = (byteAlign == 0) ? DEFAULT_ALIGNMENT : byteAlign;
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE bool DqnMemBuffer_Init(DqnMemBuffer *const buffer, size_t size,
|
||||
DQN_FILE_SCOPE bool DqnMemStack_Init(DqnMemStack *const stack, size_t size,
|
||||
const bool zeroClear,
|
||||
const u32 byteAlign)
|
||||
{
|
||||
if (!buffer || size <= 0) return false;
|
||||
DQN_ASSERT(!buffer->block);
|
||||
if (!stack || size <= 0) return false;
|
||||
DQN_ASSERT(!stack->block);
|
||||
|
||||
buffer->block = DqnMemBuffer_AllocateBlockInternal(byteAlign, size);
|
||||
if (!buffer->block) return false;
|
||||
stack->block = DqnMemStack_AllocateBlockInternal(byteAlign, size);
|
||||
if (!stack->block) return false;
|
||||
|
||||
buffer->tempBufferCount = 0;
|
||||
buffer->byteAlign = byteAlign;
|
||||
buffer->flags = 0;
|
||||
stack->tempStackCount = 0;
|
||||
stack->byteAlign = byteAlign;
|
||||
stack->flags = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE bool DqnMemBuffer_InitWithFixedSize(DqnMemBuffer *const buffer,
|
||||
DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedSize(DqnMemStack *const stack,
|
||||
size_t size,
|
||||
const bool zeroClear,
|
||||
const u32 byteAlign)
|
||||
{
|
||||
bool result = DqnMemBuffer_Init(buffer, size, byteAlign);
|
||||
bool result = DqnMemStack_Init(stack, size, byteAlign);
|
||||
if (result)
|
||||
{
|
||||
buffer->flags |= DqnMemBufferFlag_IsNotExpandable;
|
||||
stack->flags |= DqnMemStackFlag_IsNotExpandable;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE void *DqnMemBuffer_Allocate(DqnMemBuffer *const buffer, size_t size)
|
||||
DQN_FILE_SCOPE void *DqnMemStack_Allocate(DqnMemStack *const stack, size_t size)
|
||||
{
|
||||
if (!buffer || size == 0) return NULL;
|
||||
if (!stack || size == 0) return NULL;
|
||||
|
||||
size_t alignedSize = DQN_ALIGN_POW_N(size, buffer->byteAlign);
|
||||
if (!buffer->block ||
|
||||
(buffer->block->used + alignedSize) > buffer->block->size)
|
||||
size_t alignedSize = DQN_ALIGN_POW_N(size, stack->byteAlign);
|
||||
if (!stack->block ||
|
||||
(stack->block->used + alignedSize) > stack->block->size)
|
||||
{
|
||||
size_t newBlockSize;
|
||||
// TODO(doyle): Allocate block size based on the aligned size or
|
||||
// a minimum block size? Not allocate based on the current block
|
||||
// size
|
||||
if (buffer->block) newBlockSize = DQN_MAX(alignedSize, buffer->block->size);
|
||||
if (stack->block) newBlockSize = DQN_MAX(alignedSize, stack->block->size);
|
||||
else newBlockSize = alignedSize;
|
||||
|
||||
DqnMemBufferBlock *newBlock = DqnMemBuffer_AllocateCompatibleBlock(buffer, newBlockSize);
|
||||
DqnMemStackBlock *newBlock = DqnMemStack_AllocateCompatibleBlock(stack, newBlockSize);
|
||||
if (newBlock)
|
||||
{
|
||||
if (!DqnMemBuffer_AttachBlock(buffer, newBlock))
|
||||
if (!DqnMemStack_AttachBlock(stack, newBlock))
|
||||
{
|
||||
// IMPORTANT(doyle): This should be impossible, considering that
|
||||
// AllocateCompatibleBlock checks the preconditions that the new
|
||||
@ -1464,48 +1476,62 @@ DQN_FILE_SCOPE void *DqnMemBuffer_Allocate(DqnMemBuffer *const buffer, size_t si
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Better notifying to user, out of space in buffer OR buffer
|
||||
// TODO: Better notifying to user, out of space in stack OR stack
|
||||
// is configured such that new blocks are not allowed.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
u8 *currPointer = buffer->block->memory + buffer->block->used;
|
||||
u8 *alignedResult = (u8 *)DQN_ALIGN_POW_N(currPointer, buffer->byteAlign);
|
||||
u8 *currPointer = stack->block->memory + stack->block->used;
|
||||
u8 *alignedResult = (u8 *)DQN_ALIGN_POW_N(currPointer, stack->byteAlign);
|
||||
size_t alignmentOffset = (size_t)(alignedResult - currPointer);
|
||||
|
||||
// NOTE(doyle): Since all buffers can't change alignment once they've been
|
||||
// NOTE(doyle): Since all stack can't change alignment once they've been
|
||||
// initialised and that the base memory ptr is already aligned, then all
|
||||
// subsequent allocations should also be aligned automatically.
|
||||
// TODO(doyle): In the future, do we want to allow arbitrary alignment PER
|
||||
// allocation, not per MemBuffer?
|
||||
// allocation, not per MemStack?
|
||||
DQN_ASSERT(alignmentOffset == 0);
|
||||
|
||||
void *result = alignedResult;
|
||||
buffer->block->used += (alignedSize + alignmentOffset);
|
||||
DQN_ASSERT(buffer->block->used <= buffer->block->size);
|
||||
stack->block->used += (alignedSize + alignmentOffset);
|
||||
DQN_ASSERT(stack->block->used <= stack->block->size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE bool DqnMemBuffer_FreeBufferBlock(DqnMemBuffer *const buffer,
|
||||
DqnMemBufferBlock *block)
|
||||
DQN_FILE_SCOPE bool DqnMemStack_Pop(DqnMemStack *const stack, void *ptr, size_t size)
|
||||
{
|
||||
if (!buffer || !block || !buffer->block) return false;
|
||||
if (buffer->flags & DqnMemBufferFlag_IsFixedMemoryFromUser) return false;
|
||||
if (!stack || !stack->block) return false;
|
||||
|
||||
DqnMemBufferBlock **blockPtr = &buffer->block;
|
||||
u8 *currPtr = stack->block->memory + stack->block->used;
|
||||
DQN_ASSERT((u8 *)ptr >= stack->block->memory && ptr < currPtr);
|
||||
|
||||
size_t calcSize = (size_t)currPtr - (size_t)ptr;
|
||||
DQN_ASSERT(calcSize == size);
|
||||
|
||||
stack->block->used -= size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemStackBlock *block)
|
||||
{
|
||||
if (!stack || !block || !stack->block) return false;
|
||||
if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser) return false;
|
||||
|
||||
DqnMemStackBlock **blockPtr = &stack->block;
|
||||
|
||||
while (*blockPtr && (*blockPtr) != block)
|
||||
blockPtr = &((*blockPtr)->prevBlock);
|
||||
|
||||
if (*blockPtr)
|
||||
{
|
||||
DqnMemBufferBlock *blockToFree = *blockPtr;
|
||||
DqnMemStackBlock *blockToFree = *blockPtr;
|
||||
(*blockPtr) = blockToFree->prevBlock;
|
||||
DqnMem_Free(blockToFree);
|
||||
|
||||
// No more blocks, then last block has been freed
|
||||
if (!buffer->block) DQN_ASSERT(buffer->tempBufferCount == 0);
|
||||
if (!stack->block) DQN_ASSERT(stack->tempStackCount == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1513,73 +1539,72 @@ DQN_FILE_SCOPE bool DqnMemBuffer_FreeBufferBlock(DqnMemBuffer *const buffer,
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE bool
|
||||
DqnMemBuffer_FreeLastBlock(DqnMemBuffer *const buffer)
|
||||
DqnMemStack_FreeLastBlock(DqnMemStack *const stack)
|
||||
{
|
||||
bool result = DqnMemBuffer_FreeBufferBlock(buffer, buffer->block);
|
||||
bool result = DqnMemStack_FreeStackBlock(stack, stack->block);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE void DqnMemBuffer_Free(DqnMemBuffer *buffer)
|
||||
DQN_FILE_SCOPE void DqnMemStack_Free(DqnMemStack *stack)
|
||||
{
|
||||
if (!buffer) return;
|
||||
if (!stack) return;
|
||||
|
||||
// NOTE(doyle): User is in charge of freeing this memory, so all we need to
|
||||
// do is clear the block.
|
||||
if (buffer->flags & DqnMemBufferFlag_IsFixedMemoryFromUser)
|
||||
if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser)
|
||||
{
|
||||
DQN_ASSERT(!buffer->block->prevBlock);
|
||||
DqnMemBuffer_ClearCurrBlock(buffer, false);
|
||||
DQN_ASSERT(!stack->block->prevBlock);
|
||||
DqnMemStack_ClearCurrBlock(stack, false);
|
||||
return;
|
||||
}
|
||||
|
||||
while (buffer->block)
|
||||
DqnMemBuffer_FreeLastBlock(buffer);
|
||||
while (stack->block)
|
||||
DqnMemStack_FreeLastBlock(stack);
|
||||
|
||||
// After a buffer is free, we reset the not expandable flag so that if we
|
||||
// allocate on an empty buffer it still works.
|
||||
buffer->flags &= ~DqnMemBufferFlag_IsNotExpandable;
|
||||
// After a stack is free, we reset the not expandable flag so that if we
|
||||
// allocate on an empty stack it still works.
|
||||
stack->flags &= ~DqnMemStackFlag_IsNotExpandable;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE void DqnMemBuffer_ClearCurrBlock(DqnMemBuffer *const buffer,
|
||||
DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack,
|
||||
const bool zeroClear)
|
||||
{
|
||||
if (!buffer) return;
|
||||
if (buffer->block)
|
||||
if (!stack) return;
|
||||
if (stack->block)
|
||||
{
|
||||
buffer->block->used = 0;
|
||||
stack->block->used = 0;
|
||||
if (zeroClear)
|
||||
{
|
||||
DqnMem_Clear(buffer->block->memory, 0,
|
||||
buffer->block->size);
|
||||
DqnMem_Clear(stack->block->memory, 0, stack->block->size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE DqnTempBuffer
|
||||
DqnMemBuffer_BeginTempRegion(DqnMemBuffer *const buffer)
|
||||
DQN_FILE_SCOPE DqnTempMemStack
|
||||
DqnMemStack_BeginTempRegion(DqnMemStack *const stack)
|
||||
{
|
||||
DqnTempBuffer result = {};
|
||||
result.buffer = buffer;
|
||||
result.startingBlock = buffer->block;
|
||||
result.used = buffer->block->used;
|
||||
DqnTempMemStack result = {};
|
||||
result.stack = stack;
|
||||
result.startingBlock = stack->block;
|
||||
result.used = stack->block->used;
|
||||
|
||||
buffer->tempBufferCount++;
|
||||
stack->tempStackCount++;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE void DqnMemBuffer_EndTempRegion(DqnTempBuffer tempBuffer)
|
||||
DQN_FILE_SCOPE void DqnMemStack_EndTempRegion(DqnTempMemStack tempStack)
|
||||
{
|
||||
DqnMemBuffer *buffer = tempBuffer.buffer;
|
||||
while (buffer->block != tempBuffer.startingBlock)
|
||||
DqnMemBuffer_FreeLastBlock(buffer);
|
||||
DqnMemStack *stack = tempStack.stack;
|
||||
while (stack->block != tempStack.startingBlock)
|
||||
DqnMemStack_FreeLastBlock(stack);
|
||||
|
||||
if (buffer->block)
|
||||
if (stack->block)
|
||||
{
|
||||
DQN_ASSERT(buffer->block->used >= tempBuffer.used);
|
||||
buffer->block->used = tempBuffer.used;
|
||||
DQN_ASSERT(buffer->tempBufferCount >= 0);
|
||||
DQN_ASSERT(stack->block->used >= tempStack.used);
|
||||
stack->block->used = tempStack.used;
|
||||
DQN_ASSERT(stack->tempStackCount >= 0);
|
||||
}
|
||||
buffer->tempBufferCount--;
|
||||
stack->tempStackCount--;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -2096,6 +2121,18 @@ DQN_FILE_SCOPE DqnV3 DqnV3_Cross(DqnV3 a, DqnV3 b)
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z)
|
||||
{
|
||||
DqnV3i result = {x, y, z};
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE DqnV3i DqnV3i_3f(f32 x, f32 y, f32 z)
|
||||
{
|
||||
DqnV3i result = {(i32)x, (i32)y, (i32)z};
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Vec4
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1037,82 +1037,82 @@ void FileTest()
|
||||
printf("FileTest(): Completed successfully\n");
|
||||
}
|
||||
|
||||
void MemBufferTest()
|
||||
void MemStackTest()
|
||||
{
|
||||
// Test over allocation, alignments, temp regions
|
||||
{
|
||||
size_t allocSize = DQN_KILOBYTE(1);
|
||||
DqnMemBuffer buffer = {};
|
||||
DqnMemStack stack = {};
|
||||
const u32 ALIGNMENT = 4;
|
||||
DqnMemBuffer_Init(&buffer, allocSize, false, ALIGNMENT);
|
||||
DQN_ASSERT(buffer.block && buffer.block->memory);
|
||||
DQN_ASSERT(buffer.block->size == allocSize);
|
||||
DQN_ASSERT(buffer.block->used == 0);
|
||||
DQN_ASSERT(buffer.byteAlign == ALIGNMENT);
|
||||
DqnMemStack_Init(&stack, allocSize, false, ALIGNMENT);
|
||||
DQN_ASSERT(stack.block && stack.block->memory);
|
||||
DQN_ASSERT(stack.block->size == allocSize);
|
||||
DQN_ASSERT(stack.block->used == 0);
|
||||
DQN_ASSERT(stack.byteAlign == ALIGNMENT);
|
||||
|
||||
// Alocate A
|
||||
size_t sizeA = (size_t)(allocSize * 0.5f);
|
||||
void *resultA = DqnMemBuffer_Allocate(&buffer, sizeA);
|
||||
void *resultA = DqnMemStack_Allocate(&stack, sizeA);
|
||||
u64 resultAddrA = *((u64 *)resultA);
|
||||
DQN_ASSERT(resultAddrA % ALIGNMENT == 0);
|
||||
DQN_ASSERT(buffer.block && buffer.block->memory);
|
||||
DQN_ASSERT(buffer.block->size == allocSize);
|
||||
DQN_ASSERT(buffer.block->used >= sizeA + 0 &&
|
||||
buffer.block->used <= sizeA + 3);
|
||||
DQN_ASSERT(buffer.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(stack.block && stack.block->memory);
|
||||
DQN_ASSERT(stack.block->size == allocSize);
|
||||
DQN_ASSERT(stack.block->used >= sizeA + 0 &&
|
||||
stack.block->used <= sizeA + 3);
|
||||
DQN_ASSERT(stack.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(resultA);
|
||||
u8 *ptrA = (u8 *)resultA;
|
||||
for (u32 i = 0; i < sizeA; i++)
|
||||
ptrA[i] = 1;
|
||||
|
||||
DqnMemBufferBlock *blockA = buffer.block;
|
||||
DqnMemStackBlock *blockA = stack.block;
|
||||
// Alocate B
|
||||
size_t sizeB = (size_t)(allocSize * 2.0f);
|
||||
void *resultB = DqnMemBuffer_Allocate(&buffer, sizeB);
|
||||
void *resultB = DqnMemStack_Allocate(&stack, sizeB);
|
||||
u64 resultAddrB = *((u64 *)resultB);
|
||||
DQN_ASSERT(resultAddrB % ALIGNMENT == 0);
|
||||
DQN_ASSERT(buffer.block && buffer.block->memory);
|
||||
DQN_ASSERT(buffer.block->size == DQN_KILOBYTE(2));
|
||||
DQN_ASSERT(stack.block && stack.block->memory);
|
||||
DQN_ASSERT(stack.block->size == DQN_KILOBYTE(2));
|
||||
|
||||
// Since we alignment the pointers we return they can be within 0-3
|
||||
// bytes of what we expect and since this is in a new block as well used
|
||||
// will reflect just this allocation.
|
||||
DQN_ASSERT(buffer.block->used >= sizeB + 0 &&
|
||||
buffer.block->used <= sizeB + 3);
|
||||
DQN_ASSERT(stack.block->used >= sizeB + 0 &&
|
||||
stack.block->used <= sizeB + 3);
|
||||
DQN_ASSERT(resultB);
|
||||
u8 *ptrB = (u8 *)resultB;
|
||||
for (u32 i = 0; i < sizeB; i++)
|
||||
ptrB[i] = 2;
|
||||
|
||||
// Check that a new block was created since there wasn't enough space
|
||||
DQN_ASSERT(buffer.block->prevBlock == blockA);
|
||||
DQN_ASSERT(buffer.block != blockA);
|
||||
DQN_ASSERT(buffer.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(stack.block->prevBlock == blockA);
|
||||
DQN_ASSERT(stack.block != blockA);
|
||||
DQN_ASSERT(stack.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(blockA->used == sizeA);
|
||||
DqnMemBufferBlock *blockB = buffer.block;
|
||||
DqnMemStackBlock *blockB = stack.block;
|
||||
|
||||
// Check temp regions work
|
||||
DqnTempBuffer tempBuffer = DqnMemBuffer_BeginTempRegion(&buffer);
|
||||
DqnTempMemStack tempBuffer = DqnMemStack_BeginTempRegion(&stack);
|
||||
size_t sizeC = 1024 + 1;
|
||||
void *resultC = DqnMemBuffer_Allocate(tempBuffer.buffer, sizeC);
|
||||
void *resultC = DqnMemStack_Allocate(tempBuffer.stack, sizeC);
|
||||
u64 resultAddrC = *((u64 *)resultC);
|
||||
DQN_ASSERT(resultAddrC % ALIGNMENT == 0);
|
||||
DQN_ASSERT(buffer.block != blockB && buffer.block != blockA);
|
||||
DQN_ASSERT(buffer.block->used >= sizeC + 0 &&
|
||||
buffer.block->used <= sizeC + 3);
|
||||
DQN_ASSERT(buffer.tempBufferCount == 1);
|
||||
DQN_ASSERT(buffer.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(stack.block != blockB && stack.block != blockA);
|
||||
DQN_ASSERT(stack.block->used >= sizeC + 0 &&
|
||||
stack.block->used <= sizeC + 3);
|
||||
DQN_ASSERT(stack.tempStackCount == 1);
|
||||
DQN_ASSERT(stack.byteAlign == ALIGNMENT);
|
||||
|
||||
// NOTE: Allocation should be aligned to 4 byte boundary
|
||||
DQN_ASSERT(tempBuffer.buffer->block->size == 2048);
|
||||
DQN_ASSERT(tempBuffer.stack->block->size == 2048);
|
||||
u8 *ptrC = (u8 *)resultC;
|
||||
for (u32 i = 0; i < sizeC; i++)
|
||||
ptrC[i] = 3;
|
||||
|
||||
// Check that a new block was created since there wasn't enough space
|
||||
DQN_ASSERT(buffer.block->prevBlock == blockB);
|
||||
DQN_ASSERT(buffer.block != blockB);
|
||||
DQN_ASSERT(buffer.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(stack.block->prevBlock == blockB);
|
||||
DQN_ASSERT(stack.block != blockB);
|
||||
DQN_ASSERT(stack.byteAlign == ALIGNMENT);
|
||||
|
||||
for (u32 i = 0; i < sizeA; i++)
|
||||
DQN_ASSERT(ptrA[i] == 1);
|
||||
@ -1121,119 +1121,119 @@ void MemBufferTest()
|
||||
for (u32 i = 0; i < sizeC; i++)
|
||||
DQN_ASSERT(ptrC[i] == 3);
|
||||
|
||||
// End temp region which should revert back to 2 linked buffers, A and B
|
||||
DqnMemBuffer_EndTempRegion(tempBuffer);
|
||||
DQN_ASSERT(buffer.block && buffer.block->memory);
|
||||
DQN_ASSERT(buffer.block->size == sizeB);
|
||||
DQN_ASSERT(buffer.block->used >= sizeB + 0 &&
|
||||
buffer.block->used <= sizeB + 3);
|
||||
DQN_ASSERT(buffer.tempBufferCount == 0);
|
||||
// End temp region which should revert back to 2 linked stacks, A and B
|
||||
DqnMemStack_EndTempRegion(tempBuffer);
|
||||
DQN_ASSERT(stack.block && stack.block->memory);
|
||||
DQN_ASSERT(stack.block->size == sizeB);
|
||||
DQN_ASSERT(stack.block->used >= sizeB + 0 &&
|
||||
stack.block->used <= sizeB + 3);
|
||||
DQN_ASSERT(stack.tempStackCount == 0);
|
||||
DQN_ASSERT(resultB);
|
||||
|
||||
DQN_ASSERT(buffer.block->prevBlock == blockA);
|
||||
DQN_ASSERT(buffer.block != blockA);
|
||||
DQN_ASSERT(stack.block->prevBlock == blockA);
|
||||
DQN_ASSERT(stack.block != blockA);
|
||||
DQN_ASSERT(blockA->used == sizeA);
|
||||
DQN_ASSERT(buffer.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(stack.byteAlign == ALIGNMENT);
|
||||
|
||||
// Release the last linked buffer from the push buffer
|
||||
DqnMemBuffer_FreeLastBlock(&buffer);
|
||||
// Release the last linked stack from the push stack
|
||||
DqnMemStack_FreeLastBlock(&stack);
|
||||
|
||||
// Which should return back to the 1st allocation
|
||||
DQN_ASSERT(buffer.block == blockA);
|
||||
DQN_ASSERT(buffer.block->memory);
|
||||
DQN_ASSERT(buffer.block->size == allocSize);
|
||||
DQN_ASSERT(buffer.block->used == sizeA);
|
||||
DQN_ASSERT(buffer.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(stack.block == blockA);
|
||||
DQN_ASSERT(stack.block->memory);
|
||||
DQN_ASSERT(stack.block->size == allocSize);
|
||||
DQN_ASSERT(stack.block->used == sizeA);
|
||||
DQN_ASSERT(stack.byteAlign == ALIGNMENT);
|
||||
|
||||
// Free once more to release buffer A memory
|
||||
DqnMemBuffer_FreeLastBlock(&buffer);
|
||||
DQN_ASSERT(!buffer.block);
|
||||
DQN_ASSERT(buffer.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(buffer.tempBufferCount == 0);
|
||||
// Free once more to release stack A memory
|
||||
DqnMemStack_FreeLastBlock(&stack);
|
||||
DQN_ASSERT(!stack.block);
|
||||
DQN_ASSERT(stack.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(stack.tempStackCount == 0);
|
||||
}
|
||||
|
||||
// Test buffer with fixed memory does not allocate more
|
||||
// Test stack with fixed memory does not allocate more
|
||||
{
|
||||
u8 memory[DQN_KILOBYTE(1)] = {};
|
||||
DqnMemBuffer buffer = {};
|
||||
DqnMemStack stack = {};
|
||||
const u32 ALIGNMENT = 4;
|
||||
DqnMemBuffer_InitWithFixedMem(&buffer, memory, DQN_ARRAY_COUNT(memory),
|
||||
DqnMemStack_InitWithFixedMem(&stack, memory, DQN_ARRAY_COUNT(memory),
|
||||
ALIGNMENT);
|
||||
DQN_ASSERT(buffer.block && buffer.block->memory);
|
||||
DQN_ASSERT(buffer.block->size ==
|
||||
DQN_ARRAY_COUNT(memory) - sizeof(DqnMemBufferBlock));
|
||||
DQN_ASSERT(buffer.block->used == 0);
|
||||
DQN_ASSERT(buffer.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(stack.block && stack.block->memory);
|
||||
DQN_ASSERT(stack.block->size ==
|
||||
DQN_ARRAY_COUNT(memory) - sizeof(DqnMemStackBlock));
|
||||
DQN_ASSERT(stack.block->used == 0);
|
||||
DQN_ASSERT(stack.byteAlign == ALIGNMENT);
|
||||
|
||||
// Allocation larger than stack mem size should fail
|
||||
DQN_ASSERT(!DqnMemBuffer_Allocate(&buffer, DQN_ARRAY_COUNT(memory) * 2));
|
||||
DQN_ASSERT(!DqnMemStack_Allocate(&stack, DQN_ARRAY_COUNT(memory) * 2));
|
||||
|
||||
// Check free does nothing
|
||||
DqnMemBuffer_Free(&buffer);
|
||||
DqnMemBuffer_FreeLastBlock(&buffer);
|
||||
DQN_ASSERT(buffer.block && buffer.block->memory);
|
||||
DQN_ASSERT(buffer.block->size ==
|
||||
DQN_ARRAY_COUNT(memory) - sizeof(DqnMemBufferBlock));
|
||||
DQN_ASSERT(buffer.block->used == 0);
|
||||
DQN_ASSERT(buffer.byteAlign == ALIGNMENT);
|
||||
DqnMemStack_Free(&stack);
|
||||
DqnMemStack_FreeLastBlock(&stack);
|
||||
DQN_ASSERT(stack.block && stack.block->memory);
|
||||
DQN_ASSERT(stack.block->size ==
|
||||
DQN_ARRAY_COUNT(memory) - sizeof(DqnMemStackBlock));
|
||||
DQN_ASSERT(stack.block->used == 0);
|
||||
DQN_ASSERT(stack.byteAlign == ALIGNMENT);
|
||||
}
|
||||
|
||||
// Test buffer with fixed size, allocates once from platform but does not
|
||||
// Test stack with fixed size, allocates once from platform but does not
|
||||
// grow further
|
||||
{
|
||||
size_t allocSize = DQN_KILOBYTE(1);
|
||||
DqnMemBuffer buffer = {};
|
||||
DqnMemStack stack = {};
|
||||
const u32 ALIGNMENT = 4;
|
||||
DqnMemBuffer_InitWithFixedSize(&buffer, allocSize, false, ALIGNMENT);
|
||||
DQN_ASSERT(buffer.block && buffer.block->memory);
|
||||
DQN_ASSERT(buffer.block->size == allocSize);
|
||||
DQN_ASSERT(buffer.block->used == 0);
|
||||
DQN_ASSERT(buffer.byteAlign == ALIGNMENT);
|
||||
DqnMemStack_InitWithFixedSize(&stack, allocSize, false, ALIGNMENT);
|
||||
DQN_ASSERT(stack.block && stack.block->memory);
|
||||
DQN_ASSERT(stack.block->size == allocSize);
|
||||
DQN_ASSERT(stack.block->used == 0);
|
||||
DQN_ASSERT(stack.byteAlign == ALIGNMENT);
|
||||
|
||||
void *result = DqnMemBuffer_Allocate(&buffer, (size_t)(0.5f * allocSize));
|
||||
void *result = DqnMemStack_Allocate(&stack, (size_t)(0.5f * allocSize));
|
||||
DQN_ASSERT(result);
|
||||
|
||||
// Allocating more should fail
|
||||
DQN_ASSERT(!DqnMemBuffer_Allocate(&buffer, allocSize));
|
||||
DQN_ASSERT(!DqnMemStack_Allocate(&stack, allocSize));
|
||||
|
||||
// Freeing should work
|
||||
DqnMemBuffer_Free(&buffer);
|
||||
DQN_ASSERT(!buffer.block);
|
||||
DqnMemStack_Free(&stack);
|
||||
DQN_ASSERT(!stack.block);
|
||||
}
|
||||
|
||||
// Test freeing/clear block and alignment
|
||||
{
|
||||
size_t firstBlockSize = DQN_KILOBYTE(1);
|
||||
DqnMemBuffer buffer = {};
|
||||
DqnMemStack stack = {};
|
||||
const u32 ALIGNMENT = 16;
|
||||
DqnMemBuffer_Init(&buffer, firstBlockSize, false, ALIGNMENT);
|
||||
DqnMemStack_Init(&stack, firstBlockSize, false, ALIGNMENT);
|
||||
|
||||
DqnMemBufferBlock *firstBlock = buffer.block;
|
||||
DqnMemStackBlock *firstBlock = stack.block;
|
||||
u8 *first = NULL;
|
||||
{
|
||||
u32 allocate40Bytes = 40;
|
||||
u8 *data = (u8 *)DqnMemBuffer_Allocate(&buffer, allocate40Bytes);
|
||||
u8 *data = (u8 *)DqnMemStack_Allocate(&stack, allocate40Bytes);
|
||||
|
||||
// Test that the allocation got aligned to 16 byte boundary
|
||||
DQN_ASSERT(data);
|
||||
DQN_ASSERT(buffer.block->size == firstBlockSize);
|
||||
DQN_ASSERT(stack.block->size == firstBlockSize);
|
||||
DQN_ASSERT((size_t)data % ALIGNMENT == 0);
|
||||
|
||||
for (u32 i = 0; i < allocate40Bytes; i++)
|
||||
data[i] = 'a';
|
||||
|
||||
// Clear the block, but don't zero it out
|
||||
DqnMemBuffer_ClearCurrBlock(&buffer, false);
|
||||
DqnMemStack_ClearCurrBlock(&stack, false);
|
||||
for (u32 i = 0; i < allocate40Bytes; i++)
|
||||
DQN_ASSERT(data[i] == 'a');
|
||||
|
||||
// Test clear reverted the use pointer
|
||||
DQN_ASSERT(buffer.block->used == 0);
|
||||
DQN_ASSERT(buffer.block->size == firstBlockSize);
|
||||
DQN_ASSERT(stack.block->used == 0);
|
||||
DQN_ASSERT(stack.block->size == firstBlockSize);
|
||||
|
||||
// Reallocate the data
|
||||
data = (u8 *)DqnMemBuffer_Allocate(&buffer, firstBlockSize);
|
||||
DQN_ASSERT(buffer.block->size == firstBlockSize);
|
||||
data = (u8 *)DqnMemStack_Allocate(&stack, firstBlockSize);
|
||||
DQN_ASSERT(stack.block->size == firstBlockSize);
|
||||
DQN_ASSERT((size_t)data % ALIGNMENT == 0);
|
||||
|
||||
// Fill with 'b's
|
||||
@ -1241,19 +1241,19 @@ void MemBufferTest()
|
||||
data[i] = 'b';
|
||||
|
||||
// Clear block and zero it out
|
||||
DqnMemBuffer_ClearCurrBlock(&buffer, true);
|
||||
DqnMemStack_ClearCurrBlock(&stack, true);
|
||||
for (u32 i = 0; i < firstBlockSize; i++)
|
||||
DQN_ASSERT(data[i] == 0);
|
||||
|
||||
// General Check buffer struct contains the values we expect from
|
||||
// General Check stack struct contains the values we expect from
|
||||
// initialisation
|
||||
DQN_ASSERT(buffer.flags == 0);
|
||||
DQN_ASSERT(buffer.tempBufferCount == 0);
|
||||
DQN_ASSERT(buffer.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(buffer.block->size == firstBlockSize);
|
||||
DQN_ASSERT(stack.flags == 0);
|
||||
DQN_ASSERT(stack.tempStackCount == 0);
|
||||
DQN_ASSERT(stack.byteAlign == ALIGNMENT);
|
||||
DQN_ASSERT(stack.block->size == firstBlockSize);
|
||||
|
||||
// Write out data to current block
|
||||
data = (u8 *)DqnMemBuffer_Allocate(&buffer, firstBlockSize);
|
||||
data = (u8 *)DqnMemStack_Allocate(&stack, firstBlockSize);
|
||||
for (u32 i = 0; i < firstBlockSize; i++)
|
||||
data[i] = 'c';
|
||||
|
||||
@ -1262,20 +1262,20 @@ void MemBufferTest()
|
||||
|
||||
// Force it to allocate three new blocks and write out data to each
|
||||
size_t secondBlockSize = DQN_KILOBYTE(2);
|
||||
u8 *second = (u8 *)DqnMemBuffer_Allocate(&buffer, secondBlockSize);
|
||||
DqnMemBufferBlock *secondBlock = buffer.block;
|
||||
u8 *second = (u8 *)DqnMemStack_Allocate(&stack, secondBlockSize);
|
||||
DqnMemStackBlock *secondBlock = stack.block;
|
||||
for (u32 i = 0; i < secondBlockSize; i++)
|
||||
second[i] = 'd';
|
||||
|
||||
size_t thirdBlockSize = DQN_KILOBYTE(3);
|
||||
u8 *third = (u8 *)DqnMemBuffer_Allocate(&buffer, thirdBlockSize);
|
||||
DqnMemBufferBlock *thirdBlock = buffer.block;
|
||||
u8 *third = (u8 *)DqnMemStack_Allocate(&stack, thirdBlockSize);
|
||||
DqnMemStackBlock *thirdBlock = stack.block;
|
||||
for (u32 i = 0; i < thirdBlockSize; i++)
|
||||
third[i] = 'e';
|
||||
|
||||
size_t fourthBlockSize = DQN_KILOBYTE(4);
|
||||
u8 *fourth = (u8 *)DqnMemBuffer_Allocate(&buffer, fourthBlockSize);
|
||||
DqnMemBufferBlock *fourthBlock = buffer.block;
|
||||
u8 *fourth = (u8 *)DqnMemStack_Allocate(&stack, fourthBlockSize);
|
||||
DqnMemStackBlock *fourthBlock = stack.block;
|
||||
for (u32 i = 0; i < fourthBlockSize; i++)
|
||||
fourth[i] = 'f';
|
||||
|
||||
@ -1288,11 +1288,11 @@ void MemBufferTest()
|
||||
// NOTE: Making blocks manually is not really recommended ..
|
||||
// Try and free an invalid block by mocking a fake block
|
||||
u8 fakeBlockMem[DQN_KILOBYTE(3)] = {};
|
||||
DqnMemBufferBlock fakeBlock = {};
|
||||
DqnMemStackBlock fakeBlock = {};
|
||||
fakeBlock.memory = fakeBlockMem;
|
||||
fakeBlock.size = DQN_ARRAY_COUNT(fakeBlockMem);
|
||||
fakeBlock.used = 0;
|
||||
DQN_ASSERT(!DqnMemBuffer_FreeBufferBlock(&buffer, &fakeBlock));
|
||||
DQN_ASSERT(!DqnMemStack_FreeStackBlock(&stack, &fakeBlock));
|
||||
|
||||
//Ensure that the actual blocks are still valid and freeing did nothing
|
||||
DQN_ASSERT(firstBlock->size == firstBlockSize);
|
||||
@ -1324,7 +1324,7 @@ void MemBufferTest()
|
||||
DQN_ASSERT(fourth[i] == 'f');
|
||||
|
||||
// Free the first block
|
||||
DqnMemBuffer_FreeBufferBlock(&buffer, firstBlock);
|
||||
DqnMemStack_FreeStackBlock(&stack, firstBlock);
|
||||
|
||||
// Revalidate state
|
||||
DQN_ASSERT(secondBlock->size == secondBlockSize);
|
||||
@ -1350,7 +1350,7 @@ void MemBufferTest()
|
||||
DQN_ASSERT(fourth[i] == 'f');
|
||||
|
||||
// Free the third block
|
||||
DqnMemBuffer_FreeBufferBlock(&buffer, thirdBlock);
|
||||
DqnMemStack_FreeStackBlock(&stack, thirdBlock);
|
||||
|
||||
// Revalidate state
|
||||
DQN_ASSERT(secondBlock->size == secondBlockSize);
|
||||
@ -1370,7 +1370,7 @@ void MemBufferTest()
|
||||
DQN_ASSERT(fourth[i] == 'f');
|
||||
|
||||
// Free the second block
|
||||
DqnMemBuffer_FreeBufferBlock(&buffer, secondBlock);
|
||||
DqnMemStack_FreeStackBlock(&stack, secondBlock);
|
||||
|
||||
// Revalidate state
|
||||
DQN_ASSERT(fourthBlock->size == fourthBlockSize);
|
||||
@ -1380,9 +1380,22 @@ void MemBufferTest()
|
||||
for (u32 i = 0; i < fourthBlockSize; i++)
|
||||
DQN_ASSERT(fourth[i] == 'f');
|
||||
|
||||
// Free the buffer
|
||||
DqnMemBuffer_Free(&buffer);
|
||||
DQN_ASSERT(!buffer.block);
|
||||
// Free the stack
|
||||
DqnMemStack_Free(&stack);
|
||||
DQN_ASSERT(!stack.block);
|
||||
}
|
||||
|
||||
// Test pop
|
||||
{
|
||||
DqnMemStack stack = {};
|
||||
DqnMemStack_Init(&stack, DQN_KILOBYTE(1), true);
|
||||
|
||||
size_t allocSize = 512;
|
||||
void *alloc = DqnMemStack_Allocate(&stack, allocSize);
|
||||
DQN_ASSERT(stack.block->used == allocSize);
|
||||
|
||||
DQN_ASSERT(DqnMemStack_Pop(&stack, alloc, allocSize));
|
||||
DQN_ASSERT(stack.block->used == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1395,7 +1408,7 @@ int main(void)
|
||||
OtherTest();
|
||||
ArrayTest();
|
||||
FileTest();
|
||||
MemBufferTest();
|
||||
MemStackTest();
|
||||
|
||||
printf("\nPress 'Enter' Key to Exit\n");
|
||||
getchar();
|
||||
|
Loading…
Reference in New Issue
Block a user