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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user