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
////////////////////////////////////////////////////////////////////////////////
typedef struct DqnPushBufferBlock
typedef struct DqnMemBufferBlock
{
u8 *memory;
size_t used;
size_t size;
DqnPushBufferBlock *prevBlock;
} DqnPushBufferBlock;
DqnMemBufferBlock *prevBlock;
} 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;
size_t alignment;
} DqnPushBuffer;
} DqnMemBuffer;
typedef struct DqnTempBuffer
{
DqnPushBuffer *buffer;
DqnPushBufferBlock *startingBlock;
DqnMemBuffer *buffer;
DqnMemBufferBlock *startingBlock;
size_t used;
} DqnTempBuffer;
DQN_FILE_SCOPE bool DqnPushBuffer_Init (DqnPushBuffer *const buffer, size_t size, const size_t alignment);
DQN_FILE_SCOPE void *DqnPushBuffer_Allocate (DqnPushBuffer *const buffer, size_t size);
DQN_FILE_SCOPE void DqnPushBuffer_FreeLastBuffer(DqnPushBuffer *const buffer);
DQN_FILE_SCOPE void DqnPushBuffer_Free (DqnPushBuffer *const buffer);
DQN_FILE_SCOPE void DqnPushBuffer_ClearCurrBlock(DqnPushBuffer *const buffer, const bool clearToZero);
// NOTE: InitWithFixedMem() is for giving the MemBuffer a pre-allocated block AND the buffer WILL NOT allocate new blocks.
// Memory from user given memory is required for MemBufferBlock metadata which is equal to sizeof(DqnMemBufferBlock)
// InitWithFixedSize() will incur one allocation from the platform and WILL NOT allocate any more blocks after that
DQN_FILE_SCOPE bool DqnMemBuffer_InitWithFixedMem (DqnMemBuffer *const buffer, u8* mem, size_t memSize, const size_t alignment = 4);
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 DqnPushBuffer_EndTempRegion(DqnTempBuffer tempBuffer);
DQN_FILE_SCOPE void *DqnMemBuffer_Allocate (DqnMemBuffer *const buffer, size_t size);
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 */
@ -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)
{
@ -2778,13 +2791,13 @@ FILE_SCOPE size_t Dqn_SizeAlignmentInternal(size_t alignment, size_t size)
return result;
}
FILE_SCOPE DqnPushBufferBlock *
DqnPushBuffer_AllocBlockInternal(size_t alignment, size_t size)
FILE_SCOPE DqnMemBufferBlock *
DqnMemBuffer_AllocBlockInternal(size_t alignment, size_t 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;
result->memory = (u8 *)result + sizeof(*result);
@ -2793,36 +2806,84 @@ DqnPushBuffer_AllocBlockInternal(size_t alignment, size_t size)
return result;
}
DQN_FILE_SCOPE bool DqnPushBuffer_Init(DqnPushBuffer *const buffer,
size_t size, const size_t alignment)
DQN_FILE_SCOPE bool DqnMemBuffer_Init(DqnMemBuffer *const buffer, size_t size,
const size_t alignment)
{
if (!buffer || size <= 0) return false;
DQN_ASSERT(!buffer->block);
buffer->block = DqnPushBuffer_AllocBlockInternal(alignment, size);
buffer->block = DqnMemBuffer_AllocBlockInternal(alignment, size);
if (!buffer->block) return false;
buffer->tempBufferCount = 0;
buffer->alignment = alignment;
buffer->flags = DqnMemBufferFlag_IsExpandable;
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;
size_t alignedSize = Dqn_SizeAlignmentInternal(buffer->alignment, size);
if (!buffer->block ||
(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);
DqnPushBufferBlock *newBlock = DqnPushBuffer_AllocBlockInternal(
DqnMemBufferBlock *newBlock = DqnMemBuffer_AllocBlockInternal(
buffer->alignment, newBlockSize);
if (!newBlock) return NULL;
newBlock->prevBlock = buffer->block;
buffer->block = newBlock;
}
else
{
return NULL;
}
}
u8 *currPointer = buffer->block->memory + buffer->block->used;
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
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);
buffer->block = prevBlock;
@ -2845,17 +2908,18 @@ DqnPushBuffer_FreeLastBuffer(DqnPushBuffer *const buffer)
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->flags & DqnMemBufferFlag_IsFixedMemoryFromUser) return;
while (buffer->block)
{
DqnPushBuffer_FreeLastBuffer(buffer);
DqnMemBuffer_FreeLastBuffer(buffer);
}
}
DQN_FILE_SCOPE void
DqnPushBuffer_ClearCurrBlock(DqnPushBuffer *const buffer,
DQN_FILE_SCOPE void DqnMemBuffer_ClearCurrBlock(DqnMemBuffer *const buffer,
const bool clearToZero)
{
if (!buffer) return;
@ -2871,7 +2935,7 @@ DqnPushBuffer_ClearCurrBlock(DqnPushBuffer *const buffer,
}
DQN_FILE_SCOPE DqnTempBuffer
DqnPushBuffer_BeginTempRegion(DqnPushBuffer *const buffer)
DqnMemBuffer_BeginTempRegion(DqnMemBuffer *const buffer)
{
DqnTempBuffer result = {};
result.buffer = buffer;
@ -2882,11 +2946,11 @@ DqnPushBuffer_BeginTempRegion(DqnPushBuffer *const buffer)
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)
DqnPushBuffer_FreeLastBuffer(buffer);
DqnMemBuffer_FreeLastBuffer(buffer);
if (buffer->block)
{

View File

@ -893,12 +893,14 @@ void FileTest()
printf("FileTest(): Completed successfully\n");
}
void PushBufferTest()
void MemBufferTest()
{
// Expandable memory buffer
{
size_t allocSize = DQN_KILOBYTE(1);
DqnPushBuffer buffer = {};
DqnMemBuffer buffer = {};
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->size == allocSize);
DQN_ASSERT(buffer.block->used == 0);
@ -906,7 +908,7 @@ void PushBufferTest()
// Alocate A
size_t sizeA = (size_t)(allocSize * 0.5f);
void *resultA = DqnPushBuffer_Allocate(&buffer, sizeA);
void *resultA = DqnMemBuffer_Allocate(&buffer, sizeA);
u64 resultAddrA = *((u64 *)resultA);
DQN_ASSERT(resultAddrA % ALIGNMENT == 0);
DQN_ASSERT(buffer.block && buffer.block->memory);
@ -919,17 +921,19 @@ void PushBufferTest()
for (u32 i = 0; i < sizeA; i++)
ptrA[i] = 1;
DqnPushBufferBlock *blockA = buffer.block;
DqnMemBufferBlock *blockA = buffer.block;
// Alocate B
size_t sizeB = (size_t)(allocSize * 2.0f);
void *resultB = DqnPushBuffer_Allocate(&buffer, sizeB);
void *resultB = DqnMemBuffer_Allocate(&buffer, 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));
// 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
// 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);
@ -943,12 +947,12 @@ void PushBufferTest()
DQN_ASSERT(buffer.block != blockA);
DQN_ASSERT(buffer.alignment == ALIGNMENT);
DQN_ASSERT(blockA->used == sizeA);
DqnPushBufferBlock *blockB = buffer.block;
DqnMemBufferBlock *blockB = buffer.block;
// Check temp regions work
DqnTempBuffer tempBuffer = DqnPushBuffer_BeginTempRegion(&buffer);
DqnTempBuffer tempBuffer = DqnMemBuffer_BeginTempRegion(&buffer);
size_t sizeC = 1024 + 1;
void *resultC = DqnPushBuffer_Allocate(tempBuffer.buffer, sizeC);
void *resultC = DqnMemBuffer_Allocate(tempBuffer.buffer, sizeC);
u64 resultAddrC = *((u64 *)resultC);
DQN_ASSERT(resultAddrC % ALIGNMENT == 0);
DQN_ASSERT(buffer.block != blockB && buffer.block != blockA);
@ -976,7 +980,7 @@ void PushBufferTest()
DQN_ASSERT(ptrC[i] == 3);
// 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->size == sizeB);
DQN_ASSERT(buffer.block->used >= sizeB + 0 &&
@ -990,7 +994,7 @@ void PushBufferTest()
DQN_ASSERT(buffer.alignment == ALIGNMENT);
// Release the last linked buffer from the push buffer
DqnPushBuffer_FreeLastBuffer(&buffer);
DqnMemBuffer_FreeLastBuffer(&buffer);
// Which should return back to the 1st allocation
DQN_ASSERT(buffer.block == blockA);
@ -1000,12 +1004,62 @@ void PushBufferTest()
DQN_ASSERT(buffer.alignment == ALIGNMENT);
// Free once more to release buffer A memory
DqnPushBuffer_FreeLastBuffer(&buffer);
DqnMemBuffer_FreeLastBuffer(&buffer);
DQN_ASSERT(!buffer.block);
DQN_ASSERT(buffer.alignment == ALIGNMENT);
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)
{
StringsTest();
@ -1015,7 +1069,7 @@ int main(void)
OtherTest();
ArrayTest();
FileTest();
PushBufferTest();
MemBufferTest();
printf("\nPress 'Enter' Key to Exit\n");
getchar();