Add MemBuffer with allocation from stack memory

This commit is contained in:
Doyle Thai 2017-05-01 18:29:45 +10:00
parent fbb9c8bf9d
commit fef763aaff
2 changed files with 258 additions and 140 deletions

134
dqn.h
View File

@ -567,39 +567,52 @@ DQN_FILE_SCOPE i32 DqnRnd_PCGRange(DqnRandPCGState *pcg, i32 min, i32 max);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// PushBuffer Header // PushBuffer Header
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
typedef struct DqnPushBufferBlock typedef struct DqnMemBufferBlock
{ {
u8 *memory; u8 *memory;
size_t used; size_t used;
size_t size; size_t size;
DqnPushBufferBlock *prevBlock; DqnMemBufferBlock *prevBlock;
} DqnPushBufferBlock; } DqnMemBufferBlock;
typedef struct DqnPushBuffer enum DqnMemBufferFlag
{ {
DqnPushBufferBlock *block; DqnMemBufferFlag_IsExpandable = (1 << 0),
DqnMemBufferFlag_IsFixedMemoryFromUser = (1 << 1),
};
typedef struct DqnMemBuffer
{
DqnMemBufferBlock *block;
u32 flags;
i32 tempBufferCount; i32 tempBufferCount;
size_t alignment; size_t alignment;
} DqnPushBuffer; } DqnMemBuffer;
typedef struct DqnTempBuffer typedef struct DqnTempBuffer
{ {
DqnPushBuffer *buffer; DqnMemBuffer *buffer;
DqnPushBufferBlock *startingBlock; DqnMemBufferBlock *startingBlock;
size_t used; size_t used;
} DqnTempBuffer; } DqnTempBuffer;
DQN_FILE_SCOPE bool DqnPushBuffer_Init (DqnPushBuffer *const buffer, size_t size, const size_t alignment); // NOTE: InitWithFixedMem() is for giving the MemBuffer a pre-allocated block AND the buffer WILL NOT allocate new blocks.
DQN_FILE_SCOPE void *DqnPushBuffer_Allocate (DqnPushBuffer *const buffer, size_t size); // Memory from user given memory is required for MemBufferBlock metadata which is equal to sizeof(DqnMemBufferBlock)
DQN_FILE_SCOPE void DqnPushBuffer_FreeLastBuffer(DqnPushBuffer *const buffer); // InitWithFixedSize() will incur one allocation from the platform and WILL NOT allocate any more blocks after that
DQN_FILE_SCOPE void DqnPushBuffer_Free (DqnPushBuffer *const buffer); DQN_FILE_SCOPE bool DqnMemBuffer_InitWithFixedMem (DqnMemBuffer *const buffer, u8* mem, size_t memSize, const size_t alignment = 4);
DQN_FILE_SCOPE void DqnPushBuffer_ClearCurrBlock(DqnPushBuffer *const buffer, const bool clearToZero); DQN_FILE_SCOPE bool DqnMemBuffer_InitWithFixedSize(DqnMemBuffer *const buffer, size_t size, const size_t alignment = 4);
DQN_FILE_SCOPE bool DqnMemBuffer_Init (DqnMemBuffer *const buffer, size_t size, const size_t alignment = 4);
DQN_FILE_SCOPE DqnTempBuffer DqnPushBuffer_BeginTempRegion(DqnPushBuffer *const buffer); DQN_FILE_SCOPE void *DqnMemBuffer_Allocate (DqnMemBuffer *const buffer, size_t size);
DQN_FILE_SCOPE void DqnPushBuffer_EndTempRegion(DqnTempBuffer tempBuffer); DQN_FILE_SCOPE void DqnMemBuffer_FreeLastBuffer(DqnMemBuffer *const buffer);
DQN_FILE_SCOPE void DqnMemBuffer_Free (DqnMemBuffer *const buffer);
DQN_FILE_SCOPE void DqnMemBuffer_ClearCurrBlock(DqnMemBuffer *const buffer, const bool clearToZero);
DQN_FILE_SCOPE DqnTempBuffer DqnMemBuffer_BeginTempRegion(DqnMemBuffer *const buffer);
DQN_FILE_SCOPE void DqnMemBuffer_EndTempRegion (DqnTempBuffer tempBuffer);
#endif /* DQN_H */ #endif /* DQN_H */
@ -2770,7 +2783,7 @@ DQN_FILE_SCOPE i32 DqnRnd_PCGRange(DqnRandPCGState *pcg, i32 min, i32 max)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// DqnPushBuffer Header // DqnMemBuffer Header
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
FILE_SCOPE size_t Dqn_SizeAlignmentInternal(size_t alignment, size_t size) FILE_SCOPE size_t Dqn_SizeAlignmentInternal(size_t alignment, size_t size)
{ {
@ -2778,13 +2791,13 @@ FILE_SCOPE size_t Dqn_SizeAlignmentInternal(size_t alignment, size_t size)
return result; return result;
} }
FILE_SCOPE DqnPushBufferBlock * FILE_SCOPE DqnMemBufferBlock *
DqnPushBuffer_AllocBlockInternal(size_t alignment, size_t size) DqnMemBuffer_AllocBlockInternal(size_t alignment, size_t size)
{ {
size_t alignedSize = Dqn_SizeAlignmentInternal(alignment, size); size_t alignedSize = Dqn_SizeAlignmentInternal(alignment, size);
size_t totalSize = alignedSize + sizeof(DqnPushBufferBlock); size_t totalSize = alignedSize + sizeof(DqnMemBufferBlock);
DqnPushBufferBlock *result = (DqnPushBufferBlock *)Dqn_MemAllocInternal(totalSize, true); DqnMemBufferBlock *result = (DqnMemBufferBlock *)Dqn_MemAllocInternal(totalSize, true);
if (!result) return NULL; if (!result) return NULL;
result->memory = (u8 *)result + sizeof(*result); result->memory = (u8 *)result + sizeof(*result);
@ -2793,36 +2806,84 @@ DqnPushBuffer_AllocBlockInternal(size_t alignment, size_t size)
return result; return result;
} }
DQN_FILE_SCOPE bool DqnPushBuffer_Init(DqnPushBuffer *const buffer, DQN_FILE_SCOPE bool DqnMemBuffer_Init(DqnMemBuffer *const buffer, size_t size,
size_t size, const size_t alignment) const size_t alignment)
{ {
if (!buffer || size <= 0) return false; if (!buffer || size <= 0) return false;
DQN_ASSERT(!buffer->block); DQN_ASSERT(!buffer->block);
buffer->block = DqnPushBuffer_AllocBlockInternal(alignment, size); buffer->block = DqnMemBuffer_AllocBlockInternal(alignment, size);
if (!buffer->block) return false; if (!buffer->block) return false;
buffer->tempBufferCount = 0; buffer->tempBufferCount = 0;
buffer->alignment = alignment; buffer->alignment = alignment;
buffer->flags = DqnMemBufferFlag_IsExpandable;
return true; return true;
} }
DQN_FILE_SCOPE void *DqnPushBuffer_Allocate(DqnPushBuffer *const buffer, size_t size) DQN_FILE_SCOPE bool DqnMemBuffer_InitWithFixedSize(DqnMemBuffer *const buffer,
size_t size,
const size_t alignment)
{
bool result = DqnMemBuffer_Init(buffer, size, alignment);
if (result)
{
buffer->flags = 0;
return true;
}
return false;
}
DQN_FILE_SCOPE bool DqnMemBuffer_InitWithFixedMem(DqnMemBuffer *const buffer,
u8 *mem, size_t memSize,
const size_t alignment)
{
if (!buffer || !mem) return false;
DQN_ASSERT(!buffer->block);
// TODO(doyle): Better logging
if (memSize < sizeof(DqnMemBufferBlock))
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;
const u32 DEFAULT_ALIGNMENT = 4;
buffer->tempBufferCount = 0;
buffer->alignment = (alignment == 0) ? DEFAULT_ALIGNMENT : alignment;
return true;
}
DQN_FILE_SCOPE void *DqnMemBuffer_Allocate(DqnMemBuffer *const buffer, size_t size)
{ {
if (!buffer || size == 0) return NULL; if (!buffer || size == 0) return NULL;
size_t alignedSize = Dqn_SizeAlignmentInternal(buffer->alignment, size); size_t alignedSize = Dqn_SizeAlignmentInternal(buffer->alignment, size);
if (!buffer->block || if (!buffer->block ||
(buffer->block->used + alignedSize) > buffer->block->size) (buffer->block->used + alignedSize) > buffer->block->size)
{
if (buffer->flags & DqnMemBufferFlag_IsFixedMemoryFromUser) return NULL;
// TODO: Better notifying to user, out of space in buffer
if (buffer->flags & DqnMemBufferFlag_IsExpandable)
{ {
size_t newBlockSize = DQN_MAX(alignedSize, buffer->block->size); size_t newBlockSize = DQN_MAX(alignedSize, buffer->block->size);
DqnPushBufferBlock *newBlock = DqnPushBuffer_AllocBlockInternal( DqnMemBufferBlock *newBlock = DqnMemBuffer_AllocBlockInternal(
buffer->alignment, newBlockSize); buffer->alignment, newBlockSize);
if (!newBlock) return NULL; if (!newBlock) return NULL;
newBlock->prevBlock = buffer->block; newBlock->prevBlock = buffer->block;
buffer->block = newBlock; buffer->block = newBlock;
} }
else
{
return NULL;
}
}
u8 *currPointer = buffer->block->memory + buffer->block->used; u8 *currPointer = buffer->block->memory + buffer->block->used;
u8 *alignedResult = (u8 *)Dqn_SizeAlignmentInternal(buffer->alignment, (size_t)currPointer); u8 *alignedResult = (u8 *)Dqn_SizeAlignmentInternal(buffer->alignment, (size_t)currPointer);
@ -2835,9 +2896,11 @@ DQN_FILE_SCOPE void *DqnPushBuffer_Allocate(DqnPushBuffer *const buffer, size_t
} }
DQN_FILE_SCOPE void DQN_FILE_SCOPE void
DqnPushBuffer_FreeLastBuffer(DqnPushBuffer *const buffer) DqnMemBuffer_FreeLastBuffer(DqnMemBuffer *const buffer)
{ {
DqnPushBufferBlock *prevBlock = buffer->block->prevBlock; if (buffer->flags & DqnMemBufferFlag_IsFixedMemoryFromUser) return;
DqnMemBufferBlock *prevBlock = buffer->block->prevBlock;
Dqn_MemFreeInternal(buffer->block); Dqn_MemFreeInternal(buffer->block);
buffer->block = prevBlock; buffer->block = prevBlock;
@ -2845,17 +2908,18 @@ DqnPushBuffer_FreeLastBuffer(DqnPushBuffer *const buffer)
if (!buffer->block) DQN_ASSERT(buffer->tempBufferCount == 0); if (!buffer->block) DQN_ASSERT(buffer->tempBufferCount == 0);
} }
DQN_FILE_SCOPE void DqnPushBuffer_Free(DqnPushBuffer *buffer) DQN_FILE_SCOPE void DqnMemBuffer_Free(DqnMemBuffer *buffer)
{ {
if (!buffer) return; if (!buffer) return;
if (buffer->flags & DqnMemBufferFlag_IsFixedMemoryFromUser) return;
while (buffer->block) while (buffer->block)
{ {
DqnPushBuffer_FreeLastBuffer(buffer); DqnMemBuffer_FreeLastBuffer(buffer);
} }
} }
DQN_FILE_SCOPE void DQN_FILE_SCOPE void DqnMemBuffer_ClearCurrBlock(DqnMemBuffer *const buffer,
DqnPushBuffer_ClearCurrBlock(DqnPushBuffer *const buffer,
const bool clearToZero) const bool clearToZero)
{ {
if (!buffer) return; if (!buffer) return;
@ -2871,7 +2935,7 @@ DqnPushBuffer_ClearCurrBlock(DqnPushBuffer *const buffer,
} }
DQN_FILE_SCOPE DqnTempBuffer DQN_FILE_SCOPE DqnTempBuffer
DqnPushBuffer_BeginTempRegion(DqnPushBuffer *const buffer) DqnMemBuffer_BeginTempRegion(DqnMemBuffer *const buffer)
{ {
DqnTempBuffer result = {}; DqnTempBuffer result = {};
result.buffer = buffer; result.buffer = buffer;
@ -2882,11 +2946,11 @@ DqnPushBuffer_BeginTempRegion(DqnPushBuffer *const buffer)
return result; return result;
} }
DQN_FILE_SCOPE void DqnPushBuffer_EndTempRegion(DqnTempBuffer tempBuffer) DQN_FILE_SCOPE void DqnMemBuffer_EndTempRegion(DqnTempBuffer tempBuffer)
{ {
DqnPushBuffer *buffer = tempBuffer.buffer; DqnMemBuffer *buffer = tempBuffer.buffer;
while (buffer->block != tempBuffer.startingBlock) while (buffer->block != tempBuffer.startingBlock)
DqnPushBuffer_FreeLastBuffer(buffer); DqnMemBuffer_FreeLastBuffer(buffer);
if (buffer->block) if (buffer->block)
{ {

View File

@ -893,12 +893,14 @@ void FileTest()
printf("FileTest(): Completed successfully\n"); printf("FileTest(): Completed successfully\n");
} }
void PushBufferTest() void MemBufferTest()
{ {
// Expandable memory buffer
{
size_t allocSize = DQN_KILOBYTE(1); size_t allocSize = DQN_KILOBYTE(1);
DqnPushBuffer buffer = {}; DqnMemBuffer buffer = {};
const u32 ALIGNMENT = 4; const u32 ALIGNMENT = 4;
DqnPushBuffer_Init(&buffer, allocSize, ALIGNMENT); DqnMemBuffer_Init(&buffer, allocSize, ALIGNMENT);
DQN_ASSERT(buffer.block && buffer.block->memory); DQN_ASSERT(buffer.block && buffer.block->memory);
DQN_ASSERT(buffer.block->size == allocSize); DQN_ASSERT(buffer.block->size == allocSize);
DQN_ASSERT(buffer.block->used == 0); DQN_ASSERT(buffer.block->used == 0);
@ -906,7 +908,7 @@ void PushBufferTest()
// Alocate A // Alocate A
size_t sizeA = (size_t)(allocSize * 0.5f); size_t sizeA = (size_t)(allocSize * 0.5f);
void *resultA = DqnPushBuffer_Allocate(&buffer, sizeA); void *resultA = DqnMemBuffer_Allocate(&buffer, sizeA);
u64 resultAddrA = *((u64 *)resultA); u64 resultAddrA = *((u64 *)resultA);
DQN_ASSERT(resultAddrA % ALIGNMENT == 0); DQN_ASSERT(resultAddrA % ALIGNMENT == 0);
DQN_ASSERT(buffer.block && buffer.block->memory); DQN_ASSERT(buffer.block && buffer.block->memory);
@ -919,17 +921,19 @@ void PushBufferTest()
for (u32 i = 0; i < sizeA; i++) for (u32 i = 0; i < sizeA; i++)
ptrA[i] = 1; ptrA[i] = 1;
DqnPushBufferBlock *blockA = buffer.block; DqnMemBufferBlock *blockA = buffer.block;
// Alocate B // Alocate B
size_t sizeB = (size_t)(allocSize * 2.0f); size_t sizeB = (size_t)(allocSize * 2.0f);
void *resultB = DqnPushBuffer_Allocate(&buffer, sizeB); void *resultB = DqnMemBuffer_Allocate(&buffer, sizeB);
u64 resultAddrB = *((u64 *)resultB); u64 resultAddrB = *((u64 *)resultB);
DQN_ASSERT(resultAddrB % ALIGNMENT == 0); DQN_ASSERT(resultAddrB % ALIGNMENT == 0);
DQN_ASSERT(buffer.block && buffer.block->memory); DQN_ASSERT(buffer.block && buffer.block->memory);
DQN_ASSERT(buffer.block->size == DQN_KILOBYTE(2)); DQN_ASSERT(buffer.block->size == DQN_KILOBYTE(2));
// Since we alignment the pointers we return they can be within 0-3 bytes of // Since we alignment the pointers we return they can be within 0-3
// what we expect and since this is in a new block as well used will reflect // bytes of
// what we expect and since this is in a new block as well used will
// reflect
// just this allocation. // just this allocation.
DQN_ASSERT(buffer.block->used >= sizeB + 0 && DQN_ASSERT(buffer.block->used >= sizeB + 0 &&
buffer.block->used <= sizeB + 3); buffer.block->used <= sizeB + 3);
@ -943,12 +947,12 @@ void PushBufferTest()
DQN_ASSERT(buffer.block != blockA); DQN_ASSERT(buffer.block != blockA);
DQN_ASSERT(buffer.alignment == ALIGNMENT); DQN_ASSERT(buffer.alignment == ALIGNMENT);
DQN_ASSERT(blockA->used == sizeA); DQN_ASSERT(blockA->used == sizeA);
DqnPushBufferBlock *blockB = buffer.block; DqnMemBufferBlock *blockB = buffer.block;
// Check temp regions work // Check temp regions work
DqnTempBuffer tempBuffer = DqnPushBuffer_BeginTempRegion(&buffer); DqnTempBuffer tempBuffer = DqnMemBuffer_BeginTempRegion(&buffer);
size_t sizeC = 1024 + 1; size_t sizeC = 1024 + 1;
void *resultC = DqnPushBuffer_Allocate(tempBuffer.buffer, sizeC); void *resultC = DqnMemBuffer_Allocate(tempBuffer.buffer, sizeC);
u64 resultAddrC = *((u64 *)resultC); u64 resultAddrC = *((u64 *)resultC);
DQN_ASSERT(resultAddrC % ALIGNMENT == 0); DQN_ASSERT(resultAddrC % ALIGNMENT == 0);
DQN_ASSERT(buffer.block != blockB && buffer.block != blockA); DQN_ASSERT(buffer.block != blockB && buffer.block != blockA);
@ -976,7 +980,7 @@ void PushBufferTest()
DQN_ASSERT(ptrC[i] == 3); DQN_ASSERT(ptrC[i] == 3);
// End temp region which should revert back to 2 linked buffers, A and B // End temp region which should revert back to 2 linked buffers, A and B
DqnPushBuffer_EndTempRegion(tempBuffer); DqnMemBuffer_EndTempRegion(tempBuffer);
DQN_ASSERT(buffer.block && buffer.block->memory); DQN_ASSERT(buffer.block && buffer.block->memory);
DQN_ASSERT(buffer.block->size == sizeB); DQN_ASSERT(buffer.block->size == sizeB);
DQN_ASSERT(buffer.block->used >= sizeB + 0 && DQN_ASSERT(buffer.block->used >= sizeB + 0 &&
@ -990,7 +994,7 @@ void PushBufferTest()
DQN_ASSERT(buffer.alignment == ALIGNMENT); DQN_ASSERT(buffer.alignment == ALIGNMENT);
// Release the last linked buffer from the push buffer // Release the last linked buffer from the push buffer
DqnPushBuffer_FreeLastBuffer(&buffer); DqnMemBuffer_FreeLastBuffer(&buffer);
// Which should return back to the 1st allocation // Which should return back to the 1st allocation
DQN_ASSERT(buffer.block == blockA); DQN_ASSERT(buffer.block == blockA);
@ -1000,10 +1004,60 @@ void PushBufferTest()
DQN_ASSERT(buffer.alignment == ALIGNMENT); DQN_ASSERT(buffer.alignment == ALIGNMENT);
// Free once more to release buffer A memory // Free once more to release buffer A memory
DqnPushBuffer_FreeLastBuffer(&buffer); DqnMemBuffer_FreeLastBuffer(&buffer);
DQN_ASSERT(!buffer.block); DQN_ASSERT(!buffer.block);
DQN_ASSERT(buffer.alignment == ALIGNMENT); DQN_ASSERT(buffer.alignment == ALIGNMENT);
DQN_ASSERT(buffer.tempBufferCount == 0); DQN_ASSERT(buffer.tempBufferCount == 0);
}
// Test buffer with fixed memory does not allocate more
{
u8 memory[DQN_KILOBYTE(1)] = {};
DqnMemBuffer buffer = {};
const u32 ALIGNMENT = 4;
DqnMemBuffer_InitWithFixedMem(&buffer, 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.alignment == ALIGNMENT);
// Allocation larger than stack mem size should fail
DQN_ASSERT(!DqnMemBuffer_Allocate(&buffer, DQN_ARRAY_COUNT(memory) * 2));
// Check free does nothing
DqnMemBuffer_Free(&buffer);
DqnMemBuffer_FreeLastBuffer(&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.alignment == ALIGNMENT);
}
// Test buffer with fixed size, allocates once from platform but does not
// grow further
{
size_t allocSize = DQN_KILOBYTE(1);
DqnMemBuffer buffer = {};
const u32 ALIGNMENT = 4;
DqnMemBuffer_InitWithFixedSize(&buffer, allocSize, ALIGNMENT);
DQN_ASSERT(buffer.block && buffer.block->memory);
DQN_ASSERT(buffer.block->size == allocSize);
DQN_ASSERT(buffer.block->used == 0);
DQN_ASSERT(buffer.alignment == ALIGNMENT);
void *result = DqnMemBuffer_Allocate(&buffer, (size_t)(0.5f * allocSize));
DQN_ASSERT(result);
// Allocating more should fail
DQN_ASSERT(!DqnMemBuffer_Allocate(&buffer, allocSize));
// Freeing should work
DqnMemBuffer_Free(&buffer);
DQN_ASSERT(!buffer.block);
}
} }
int main(void) int main(void)
@ -1015,7 +1069,7 @@ int main(void)
OtherTest(); OtherTest();
ArrayTest(); ArrayTest();
FileTest(); FileTest();
PushBufferTest(); MemBufferTest();
printf("\nPress 'Enter' Key to Exit\n"); printf("\nPress 'Enter' Key to Exit\n");
getchar(); getchar();