Add MemBuffer with allocation from stack memory
This commit is contained in:
parent
fbb9c8bf9d
commit
fef763aaff
134
dqn.h
134
dqn.h
@ -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)
|
||||
{
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user