Add better push buffer system for dynamic alloc
This commit is contained in:
parent
f42fca6350
commit
00685b9ce9
184
dqn.h
184
dqn.h
@ -100,7 +100,8 @@ bool dqn_array_init(DqnArray<T> *array, size_t capacity)
|
||||
if (!dqn_array_free(array)) return false;
|
||||
}
|
||||
|
||||
array->data = (T *)calloc((size_t)capacity, sizeof(T));
|
||||
array->data =
|
||||
(T *)dqn_mem_alloc_internal((size_t)capacity * sizeof(T), true);
|
||||
if (!array->data) return false;
|
||||
|
||||
array->count = 0;
|
||||
@ -117,7 +118,8 @@ bool dqn_array_grow(DqnArray<T> *array)
|
||||
size_t newCapacity = (size_t)(array->capacity * GROWTH_FACTOR);
|
||||
if (newCapacity == array->capacity) newCapacity++;
|
||||
|
||||
T *newMem = (T *)realloc(array->data, (size_t)(newCapacity * sizeof(T)));
|
||||
T *newMem = (T *)dqn_mem_realloc_internal(
|
||||
array->data, (size_t)(newCapacity * sizeof(T)));
|
||||
if (newMem)
|
||||
{
|
||||
array->data = newMem;
|
||||
@ -480,25 +482,37 @@ DQN_FILE_SCOPE i32 dqn_rnd_pcg_range(DqnRandPCGState *pcg, i32 min, i32 max);
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// PushBuffer Header
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct PushBuffer
|
||||
typedef struct DqnPushBufferBlock
|
||||
{
|
||||
u8 *memory;
|
||||
size_t used;
|
||||
size_t size;
|
||||
i32 tempBufferCount;
|
||||
} PushBuffer;
|
||||
|
||||
typedef struct TempBuffer
|
||||
DqnPushBufferBlock *prevBlock;
|
||||
} DqnPushBufferBlock;
|
||||
|
||||
typedef struct DqnPushBuffer
|
||||
{
|
||||
PushBuffer *buffer;
|
||||
DqnPushBufferBlock *block;
|
||||
|
||||
i32 tempBufferCount;
|
||||
u32 alignment;
|
||||
} DqnPushBuffer;
|
||||
|
||||
typedef struct DqnTempBuffer
|
||||
{
|
||||
DqnPushBuffer *buffer;
|
||||
DqnPushBufferBlock *startingBlock;
|
||||
size_t used;
|
||||
} TempBuffer;
|
||||
|
||||
DQN_FILE_SCOPE bool push_buffer_init (PushBuffer *buffer, void *memory, size_t size);
|
||||
DQN_FILE_SCOPE void *push_buffer_allocate(PushBuffer *buffer, size_t size);
|
||||
} DqnTempBuffer;
|
||||
|
||||
DQN_FILE_SCOPE TempBuffer push_buffer_begin_temp_region(PushBuffer *buffer);
|
||||
DQN_FILE_SCOPE void push_buffer_end_temp_region (TempBuffer tempBuffer);
|
||||
DQN_FILE_SCOPE bool dqn_push_buffer_init (DqnPushBuffer *buffer, size_t size, u32 alignment);
|
||||
DQN_FILE_SCOPE void *dqn_push_buffer_allocate(DqnPushBuffer *buffer, size_t size);
|
||||
DQN_FILE_SCOPE void dqn_push_buffer_free_last_buffer(DqnPushBuffer *buffer);
|
||||
|
||||
DQN_FILE_SCOPE DqnTempBuffer dqn_push_buffer_begin_temp_region(DqnPushBuffer *buffer);
|
||||
DQN_FILE_SCOPE void dqn_push_buffer_end_temp_region (DqnTempBuffer tempBuffer);
|
||||
|
||||
#endif /* DQN_H */
|
||||
|
||||
@ -1036,6 +1050,41 @@ STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char comma, char peri
|
||||
// NOTE: DQN_INI_IMPLEMENTATION modified to be included when DQN_IMPLEMENTATION defined
|
||||
// #define DQN_INI_IMPLEMENTATION
|
||||
#define DQN_INI_STRLEN(s) dqn_strlen(s)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Memory
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: All memory allocations in dqn.h go through these functions. So they can
|
||||
// be rerouted fairly easily especially for platform specific mallocs.
|
||||
FILE_SCOPE void *dqn_mem_alloc_internal(size_t size, bool zeroClear)
|
||||
{
|
||||
void *result = NULL;
|
||||
|
||||
if (zeroClear)
|
||||
{
|
||||
result = calloc(1, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = malloc(size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
FILE_SCOPE void *dqn_mem_realloc_internal(void *memory, size_t newSize)
|
||||
{
|
||||
void *result = realloc(memory, newSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
FILE_SCOPE void dqn_mem_free_internal(void *memory)
|
||||
{
|
||||
if (memory)
|
||||
{
|
||||
free(memory);
|
||||
memory = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Math
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -1159,7 +1208,7 @@ DQN_FILE_SCOPE inline f32 dqn_v2_length(DqnV2 a, DqnV2 b)
|
||||
DQN_FILE_SCOPE inline DqnV2 dqn_v2_normalise(DqnV2 a)
|
||||
{
|
||||
f32 magnitude = dqn_v2_length(dqn_v2(0, 0), a);
|
||||
DqnV2 result = dqn_v2(a.x, a.y);
|
||||
DqnV2 result = dqn_v2(a.x, a.y);
|
||||
result = dqn_v2_scale(a, 1 / magnitude);
|
||||
return result;
|
||||
}
|
||||
@ -2155,25 +2204,26 @@ DQN_FILE_SCOPE char **dqn_dir_read(char *dir, u32 *numFiles)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
char **list = (char **)calloc(1, sizeof(*list) * (currNumFiles));
|
||||
char **list = (char **)dqn_mem_alloc_internal(
|
||||
sizeof(*list) * (currNumFiles), true);
|
||||
if (!list)
|
||||
{
|
||||
DQN_WIN32_ERROR_BOX("calloc() failed.", NULL);
|
||||
DQN_WIN32_ERROR_BOX("dqn_mem_alloc_internal() failed.", NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < currNumFiles; i++)
|
||||
{
|
||||
list[i] = (char *)calloc(1, sizeof(**list) * MAX_PATH);
|
||||
list[i] =
|
||||
(char *)dqn_mem_alloc_internal(sizeof(**list) * MAX_PATH, true);
|
||||
if (!list[i])
|
||||
{
|
||||
for (u32 j = 0; j < i; j++)
|
||||
{
|
||||
free(list[j]);
|
||||
dqn_mem_free_internal(list[j]);
|
||||
}
|
||||
|
||||
DQN_WIN32_ERROR_BOX("calloc() failed.", NULL);
|
||||
DQN_WIN32_ERROR_BOX("dqn_mem_alloc_internal() failed.", NULL);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@ -2200,11 +2250,11 @@ DQN_FILE_SCOPE inline void dqn_dir_read_free(char **fileList, u32 numFiles)
|
||||
{
|
||||
for (u32 i = 0; i < numFiles; i++)
|
||||
{
|
||||
if (fileList[i]) free(fileList[i]);
|
||||
if (fileList[i]) dqn_mem_free_internal(fileList[i]);
|
||||
fileList[i] = NULL;
|
||||
}
|
||||
|
||||
free(fileList);
|
||||
dqn_mem_free_internal(fileList);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2324,47 +2374,99 @@ DQN_FILE_SCOPE i32 dqn_rnd_pcg_range(DqnRandPCGState *pcg, i32 min, i32 max)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// PushBuffer Header
|
||||
// DqnPushBuffer Header
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
DQN_FILE_SCOPE bool push_buffer_init(PushBuffer *buffer, void *memory, size_t size)
|
||||
FILE_SCOPE size_t inline dqn_size_alignment_internal(u32 alignment, size_t size)
|
||||
{
|
||||
if (!buffer || !memory || size <= 0) return false;
|
||||
size_t result = ((size + (alignment-1)) & ~(alignment-1));
|
||||
return result;
|
||||
}
|
||||
|
||||
FILE_SCOPE DqnPushBufferBlock *
|
||||
dqn_push_buffer_alloc_block_internal(u32 alignment, size_t size)
|
||||
{
|
||||
size_t alignedSize = dqn_size_alignment_internal(alignment, size);
|
||||
size_t totalSize = alignedSize + sizeof(DqnPushBufferBlock);
|
||||
|
||||
DqnPushBufferBlock *result = (DqnPushBufferBlock *)dqn_mem_alloc_internal(totalSize, true);
|
||||
if (!result) return NULL;
|
||||
|
||||
result->memory = (u8 *)result + sizeof(*result);
|
||||
result->size = alignedSize;
|
||||
result->used = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE bool dqn_push_buffer_init(DqnPushBuffer *buffer, size_t size, u32 alignment)
|
||||
{
|
||||
if (!buffer || size <= 0) return false;
|
||||
|
||||
buffer->block = dqn_push_buffer_alloc_block_internal(alignment, size);
|
||||
if (!buffer->block) return false;
|
||||
|
||||
buffer->memory = (u8 *)memory;
|
||||
buffer->size = size;
|
||||
buffer->used = 0;
|
||||
buffer->tempBufferCount = 0;
|
||||
buffer->alignment = alignment;
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE inline void *push_buffer_allocate(PushBuffer *buffer, size_t size)
|
||||
DQN_FILE_SCOPE void *dqn_push_buffer_allocate(DqnPushBuffer *buffer, size_t size)
|
||||
{
|
||||
DQN_ASSERT((buffer->used + size) <= buffer->size);
|
||||
void *result = buffer->memory + buffer->used;
|
||||
buffer->used += size;
|
||||
size_t alignedSize = dqn_size_alignment_internal(buffer->alignment, size);
|
||||
if ((buffer->block->used + alignedSize) > buffer->block->size)
|
||||
{
|
||||
size_t newBlockSize = DQN_MAX(alignedSize, buffer->block->size);
|
||||
DqnPushBufferBlock *newBlock = dqn_push_buffer_alloc_block_internal(
|
||||
buffer->alignment, newBlockSize);
|
||||
if (!newBlock) return NULL;
|
||||
|
||||
newBlock->prevBlock = buffer->block;
|
||||
buffer->block = newBlock;
|
||||
}
|
||||
|
||||
u8 *currPointer = buffer->block->memory + buffer->block->used;
|
||||
u8 *alignedResult = (u8 *)dqn_size_alignment_internal(buffer->alignment, (size_t)currPointer);
|
||||
size_t alignmentOffset = (size_t)(alignedResult - currPointer);
|
||||
|
||||
void *result = alignedResult;
|
||||
buffer->block->used += (alignedSize + alignmentOffset);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE TempBuffer push_buffer_begin_temp_region(PushBuffer *buffer)
|
||||
DQN_FILE_SCOPE void dqn_push_buffer_free_last_buffer(DqnPushBuffer *buffer)
|
||||
{
|
||||
TempBuffer result = {};
|
||||
result.buffer = buffer;
|
||||
result.used = buffer->used;
|
||||
DqnPushBufferBlock *prevBlock = buffer->block->prevBlock;
|
||||
dqn_mem_free_internal(buffer->block);
|
||||
buffer->block = prevBlock;
|
||||
|
||||
// No more blocks, then last block has been freed
|
||||
if (!buffer->block) DQN_ASSERT(buffer->tempBufferCount == 0);
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE DqnTempBuffer dqn_push_buffer_begin_temp_region(DqnPushBuffer *buffer)
|
||||
{
|
||||
DqnTempBuffer result = {};
|
||||
result.buffer = buffer;
|
||||
result.startingBlock = buffer->block;
|
||||
result.used = buffer->block->used;
|
||||
|
||||
buffer->tempBufferCount++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE void push_buffer_end_temp_region(TempBuffer tempBuffer)
|
||||
DQN_FILE_SCOPE void dqn_push_buffer_end_temp_region(DqnTempBuffer tempBuffer)
|
||||
{
|
||||
PushBuffer *buffer = tempBuffer.buffer;
|
||||
DQN_ASSERT(buffer->used > tempBuffer.used)
|
||||
DqnPushBuffer *buffer = tempBuffer.buffer;
|
||||
while (buffer->block != tempBuffer.startingBlock)
|
||||
dqn_push_buffer_free_last_buffer(buffer);
|
||||
|
||||
buffer->used = tempBuffer.used;
|
||||
if (buffer->block)
|
||||
{
|
||||
DQN_ASSERT(buffer->block->used >= tempBuffer.used);
|
||||
buffer->block->used = tempBuffer.used;
|
||||
DQN_ASSERT(buffer->tempBufferCount >= 0);
|
||||
}
|
||||
buffer->tempBufferCount--;
|
||||
DQN_ASSERT(buffer->tempBufferCount >= 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -815,22 +815,115 @@ void dqn_file_test()
|
||||
|
||||
void dqn_push_buffer_test()
|
||||
{
|
||||
size_t blockSize = DQN_KILOBYTE(1);
|
||||
void *block = calloc(1, blockSize);
|
||||
PushBuffer buffer = {};
|
||||
push_buffer_init(&buffer, block, blockSize);
|
||||
size_t allocSize = DQN_KILOBYTE(1);
|
||||
DqnPushBuffer buffer = {};
|
||||
const u32 ALIGNMENT = 4;
|
||||
dqn_push_buffer_init(&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);
|
||||
|
||||
DQN_ASSERT(buffer.memory == block);
|
||||
DQN_ASSERT(buffer.size == blockSize);
|
||||
DQN_ASSERT(buffer.used == 0);
|
||||
// Alocate A
|
||||
size_t sizeA = (size_t)(allocSize * 0.5f);
|
||||
void *resultA = dqn_push_buffer_allocate(&buffer, 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.alignment == ALIGNMENT);
|
||||
DQN_ASSERT(resultA);
|
||||
u8 *ptrA = (u8 *)resultA;
|
||||
for (u32 i = 0; i < sizeA; i++)
|
||||
ptrA[i] = 1;
|
||||
|
||||
void *result = push_buffer_allocate(&buffer, (size_t)(blockSize * 0.5f));
|
||||
DQN_ASSERT(buffer.memory == block);
|
||||
DQN_ASSERT(buffer.size == blockSize);
|
||||
DQN_ASSERT(buffer.used == (size_t)(blockSize * 0.5f));
|
||||
DQN_ASSERT(result);
|
||||
DqnPushBufferBlock *blockA = buffer.block;
|
||||
// Alocate B
|
||||
size_t sizeB = (size_t)(allocSize * 2.0f);
|
||||
void *resultB = dqn_push_buffer_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));
|
||||
|
||||
free(block);
|
||||
// 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(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.alignment == ALIGNMENT);
|
||||
DQN_ASSERT(blockA->used == sizeA);
|
||||
DqnPushBufferBlock *blockB = buffer.block;
|
||||
|
||||
// Check temp regions work
|
||||
DqnTempBuffer tempBuffer = dqn_push_buffer_begin_temp_region(&buffer);
|
||||
size_t sizeC = 1024 + 1;
|
||||
void *resultC = dqn_push_buffer_allocate(tempBuffer.buffer, 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.alignment == ALIGNMENT);
|
||||
|
||||
// NOTE: Allocation should be aligned to 4 byte boundary
|
||||
DQN_ASSERT(tempBuffer.buffer->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.alignment == ALIGNMENT);
|
||||
|
||||
for (u32 i = 0; i < sizeA; i++)
|
||||
DQN_ASSERT(ptrA[i] == 1);
|
||||
for (u32 i = 0; i < sizeB; i++)
|
||||
DQN_ASSERT(ptrB[i] == 2);
|
||||
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
|
||||
dqn_push_buffer_end_temp_region(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);
|
||||
DQN_ASSERT(resultB);
|
||||
|
||||
DQN_ASSERT(buffer.block->prevBlock == blockA);
|
||||
DQN_ASSERT(buffer.block != blockA);
|
||||
DQN_ASSERT(blockA->used == sizeA);
|
||||
DQN_ASSERT(buffer.alignment == ALIGNMENT);
|
||||
|
||||
// Release the last linked buffer from the push buffer
|
||||
dqn_push_buffer_free_last_buffer(&buffer);
|
||||
|
||||
// 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.alignment == ALIGNMENT);
|
||||
|
||||
// Free once more to release buffer A memory
|
||||
dqn_push_buffer_free_last_buffer(&buffer);
|
||||
DQN_ASSERT(!buffer.block);
|
||||
DQN_ASSERT(buffer.alignment == ALIGNMENT);
|
||||
DQN_ASSERT(buffer.tempBufferCount == 0);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
|
Loading…
Reference in New Issue
Block a user