Draft double-ended stack allocator for temp malloc
This commit is contained in:
parent
7228a8b344
commit
b8157a3c9f
425
dqn.h
425
dqn.h
@ -242,19 +242,19 @@ public:
|
|||||||
struct Alloc_
|
struct Alloc_
|
||||||
{
|
{
|
||||||
bool zeroClear;
|
bool zeroClear;
|
||||||
usize requestSize;
|
isize requestSize;
|
||||||
} alloc;
|
} alloc;
|
||||||
|
|
||||||
struct Free_
|
struct Free_
|
||||||
{
|
{
|
||||||
void *ptrToFree;
|
void *ptrToFree;
|
||||||
usize sizeToFree;
|
isize sizeToFree;
|
||||||
} free;
|
} free;
|
||||||
|
|
||||||
struct Realloc_
|
struct Realloc_
|
||||||
{
|
{
|
||||||
usize newRequestSize;
|
isize newSize;
|
||||||
usize oldSize;
|
isize oldSize;
|
||||||
void *oldMemPtr;
|
void *oldMemPtr;
|
||||||
} realloc;
|
} realloc;
|
||||||
};
|
};
|
||||||
@ -265,18 +265,18 @@ public:
|
|||||||
Allocator *allocator;
|
Allocator *allocator;
|
||||||
void *userContext;
|
void *userContext;
|
||||||
|
|
||||||
usize bytesAllocated;
|
isize bytesAllocated;
|
||||||
usize lifetimeBytesAllocated;
|
isize lifetimeBytesAllocated;
|
||||||
usize lifetimeBytesFreed;
|
isize lifetimeBytesFreed;
|
||||||
|
|
||||||
static DqnMemAPI HeapAllocator ();
|
static DqnMemAPI HeapAllocator ();
|
||||||
|
|
||||||
// TODO(doyle): No longer necessary now that stack creates its own on init?
|
// TODO(doyle): No longer necessary now that stack creates its own on init?
|
||||||
static DqnMemAPI StackAllocator(struct DqnMemStack *const stack);
|
static DqnMemAPI StackAllocator(struct DqnMemStack *const stack);
|
||||||
|
|
||||||
void *Realloc(void *const oldPtr, usize const oldSize, usize const newSize);
|
void *Realloc(void *const oldPtr, isize const oldSize, isize const newSize);
|
||||||
void *Alloc (usize const size, bool const zeroClear = true);
|
void *Alloc (isize const size, bool const zeroClear = true);
|
||||||
void Free (void *const ptrToFree, usize const sizeToFree);
|
void Free (void *const ptrToFree, isize const sizeToFree);
|
||||||
bool IsValid() const { return (this->allocator != nullptr); }
|
bool IsValid() const { return (this->allocator != nullptr); }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -376,13 +376,13 @@ struct DqnAllocatorMetadata
|
|||||||
auto GetAllocHeadSize () const { return allocHeadSize; }
|
auto GetAllocHeadSize () const { return allocHeadSize; }
|
||||||
auto GetAllocTailSize () const { return allocTailSize; }
|
auto GetAllocTailSize () const { return allocTailSize; }
|
||||||
|
|
||||||
usize GetAllocationSize (usize size, u8 alignment) const { return GetAllocHeadSize() + size + GetAllocTailSize() + (alignment - 1); }
|
isize GetAllocationSize (isize size, u8 alignment) const { return GetAllocHeadSize() + size + GetAllocTailSize() + (alignment - 1); }
|
||||||
|
|
||||||
u32 *PtrToHeadBoundsGuard(u8 const *ptr) const; // ptr: The ptr given to the client when allocating.
|
u32 *PtrToHeadBoundsGuard(u8 const *ptr) const; // ptr: The ptr given to the client when allocating.
|
||||||
u32 *PtrToTailBoundsGuard(u8 const *ptr) const; // IMPORTANT: Uses "Alloc Amount" metadata to find the tail!
|
u32 *PtrToTailBoundsGuard(u8 const *ptr) const; // IMPORTANT: Uses "Alloc Amount" metadata to find the tail!
|
||||||
u8 *PtrToAlignment (u8 const *ptr) const;
|
u8 *PtrToAlignment (u8 const *ptr) const;
|
||||||
u8 *PtrToOffsetToSrc (u8 const *ptr) const;
|
u8 *PtrToOffsetToSrc (u8 const *ptr) const;
|
||||||
usize *PtrToAllocAmount (u8 const *ptr) const;
|
isize *PtrToAllocAmount (u8 const *ptr) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u32 boundsGuardSize; // sizeof(GUARD_VALUE) OR 0 if BoundsGuard is disabled.
|
u32 boundsGuardSize; // sizeof(GUARD_VALUE) OR 0 if BoundsGuard is disabled.
|
||||||
@ -419,10 +419,10 @@ struct DqnMemStack
|
|||||||
|
|
||||||
struct Info // Statistics of the memory stack.
|
struct Info // Statistics of the memory stack.
|
||||||
{
|
{
|
||||||
usize totalUsed;
|
isize totalUsed;
|
||||||
usize totalSize;
|
isize totalSize;
|
||||||
usize wastedSize;
|
isize wastedSize;
|
||||||
i32 numBlocks;
|
i32 numBlocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Blocks are freely modifiable if you want fine grained control. Size value and memory ptr should
|
// Blocks are freely modifiable if you want fine grained control. Size value and memory ptr should
|
||||||
@ -430,9 +430,12 @@ struct DqnMemStack
|
|||||||
struct Block
|
struct Block
|
||||||
{
|
{
|
||||||
u8 *memory;
|
u8 *memory;
|
||||||
usize size; // Read-Only
|
isize size; // Read-Only
|
||||||
usize used; // Read/Write at your peril.
|
isize used_; // Read/Write at your peril.
|
||||||
Block *prevBlock; // Uses a linked list approach for additional blocks
|
Block *prevBlock; // Uses a linked list approach for additional blocks
|
||||||
|
|
||||||
|
u8 *head;
|
||||||
|
u8 *tail;
|
||||||
};
|
};
|
||||||
|
|
||||||
DqnAllocatorMetadata metadata;
|
DqnAllocatorMetadata metadata;
|
||||||
@ -447,17 +450,19 @@ struct DqnMemStack
|
|||||||
// Uses fixed buffer, allocations will be sourced from the buffer and fail after buffer is full.
|
// Uses fixed buffer, allocations will be sourced from the buffer and fail after buffer is full.
|
||||||
// mem: Memory to use for the memory stack
|
// mem: Memory to use for the memory stack
|
||||||
// return: FALSE if args are invalid, or insufficient memSize.
|
// return: FALSE if args are invalid, or insufficient memSize.
|
||||||
bool Init(void *const mem, usize size, bool zeroClear, u32 flags_ = 0);
|
bool Init(void *const mem, isize size, bool zeroClear, u32 flags_ = 0);
|
||||||
|
|
||||||
// Memory Stack capable of expanding when full, but only if NonExpandable flag is not set.
|
// Memory Stack capable of expanding when full, but only if NonExpandable flag is not set.
|
||||||
bool Init(usize size, bool zeroClear, u32 flags_ = 0, DqnMemAPI *const api = &DQN_DEFAULT_HEAP_ALLOCATOR);
|
bool Init(isize size, bool zeroClear, u32 flags_ = 0, DqnMemAPI *const api = &DQN_DEFAULT_HEAP_ALLOCATOR);
|
||||||
|
|
||||||
// Allocation API
|
// Allocation API
|
||||||
// =============================================================================================
|
// =============================================================================================
|
||||||
|
void *PushOnTail (isize size, u8 alignment = 4);
|
||||||
|
|
||||||
// Allocate memory from the MemStack.
|
// Allocate memory from the MemStack.
|
||||||
// alignment: Ptr returned from allocator is aligned to this value and MUST be power of 2.
|
// alignment: Ptr returned from allocator is aligned to this value and MUST be power of 2.
|
||||||
// return: nullptr if out of space OR stack is using fixed memory/size OR stack full and platform malloc fails.
|
// return: nullptr if out of space OR stack is using fixed memory/size OR stack full and platform malloc fails.
|
||||||
void *Push (usize size, u8 alignment = 4);
|
void *Push (isize size, u8 alignment = 4);
|
||||||
|
|
||||||
// Frees the given ptr. It MUST be the last allocated item in the stack, asserts otherwise.
|
// Frees the given ptr. It MUST be the last allocated item in the stack, asserts otherwise.
|
||||||
void Pop (void *const ptr, bool zeroClear = false);
|
void Pop (void *const ptr, bool zeroClear = false);
|
||||||
@ -482,10 +487,11 @@ struct DqnMemStack
|
|||||||
// TODO(doyle): Look into a way of "preventing/guarding" against anual calls to free/clear in temp regions
|
// TODO(doyle): Look into a way of "preventing/guarding" against anual calls to free/clear in temp regions
|
||||||
struct TempRegion
|
struct TempRegion
|
||||||
{
|
{
|
||||||
DqnMemStack *stack; // Stack associated with this TempRegion
|
DqnMemStack *stack; // Stack associated with this TempRegion
|
||||||
Block *startingBlock; // Remember the block to revert to and its memory usage.
|
Block *startingBlock; // Remember the block to revert to and its memory usage.
|
||||||
usize used;
|
u8 *startingBlockHead;
|
||||||
isize allocationCount; // Debug only: For ensuring BoundsGuard allocation tracker reverts as well.
|
u8 *startingBlockTail;
|
||||||
|
isize allocationCount; // Debug only: For ensuring BoundsGuard allocation tracker reverts as well.
|
||||||
};
|
};
|
||||||
|
|
||||||
class TempRegionGuard_
|
class TempRegionGuard_
|
||||||
@ -3010,7 +3016,8 @@ FILE_SCOPE void DqnMemAPIInternal_ValidateRequest(DqnMemAPI::Request request_)
|
|||||||
{
|
{
|
||||||
auto *request = &request_.realloc;
|
auto *request = &request_.realloc;
|
||||||
DQN_ASSERT(request->oldSize > 0);
|
DQN_ASSERT(request->oldSize > 0);
|
||||||
DQN_ASSERT(request->newRequestSize > 0);
|
DQN_ASSERT(request->newSize > 0);
|
||||||
|
DQN_ASSERT((request->newSize - request->oldSize) != 0);
|
||||||
DQN_ASSERT(request->oldMemPtr);
|
DQN_ASSERT(request->oldMemPtr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3029,10 +3036,10 @@ FILE_SCOPE void DqnMemAPIInternal_UpdateAPIStatistics(DqnMemAPI *api, DqnMemAPI:
|
|||||||
if (request_->type == DqnMemAPI::Type::Realloc)
|
if (request_->type == DqnMemAPI::Type::Realloc)
|
||||||
{
|
{
|
||||||
auto *request = &request_->realloc;
|
auto *request = &request_->realloc;
|
||||||
api->lifetimeBytesAllocated += request->newRequestSize;
|
api->lifetimeBytesAllocated += request->newSize;
|
||||||
api->lifetimeBytesFreed += request->oldSize;
|
api->lifetimeBytesFreed += request->oldSize;
|
||||||
|
|
||||||
api->bytesAllocated += request->newRequestSize;
|
api->bytesAllocated += request->newSize;
|
||||||
api->bytesAllocated -= request->oldSize;
|
api->bytesAllocated -= request->oldSize;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3066,13 +3073,13 @@ FILE_SCOPE u8 *DqnMemAPIInternal_HeapAllocatorCallback(DqnMemAPI *api, DqnMemAPI
|
|||||||
else if (request_.type == DqnMemAPI::Type::Realloc)
|
else if (request_.type == DqnMemAPI::Type::Realloc)
|
||||||
{
|
{
|
||||||
auto const *request = &request_.realloc;
|
auto const *request = &request_.realloc;
|
||||||
if (request->newRequestSize == request->oldSize)
|
if (request->newSize == request->oldSize)
|
||||||
{
|
{
|
||||||
result = (u8 *)request->oldMemPtr;
|
result = (u8 *)request->oldMemPtr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = (u8 *)DqnMem_Realloc(request->oldMemPtr, request->newRequestSize);
|
result = (u8 *)DqnMem_Realloc(request->oldMemPtr, request->newSize);
|
||||||
success = (result != nullptr);
|
success = (result != nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3104,6 +3111,50 @@ FILE_SCOPE u8 *DqnMemAPIInternal_StackAllocatorCallback(DqnMemAPI *api, DqnMemAP
|
|||||||
u8 *result = nullptr;
|
u8 *result = nullptr;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
|
enum class PtrType
|
||||||
|
{
|
||||||
|
NotInCurrentBlock,
|
||||||
|
Head,
|
||||||
|
Tail,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto ClassifyPtr = [](DqnMemStack::Block const *block, u8 const *ptr) -> PtrType {
|
||||||
|
|
||||||
|
PtrType result = PtrType::NotInCurrentBlock;
|
||||||
|
u8 const *const blockEnd = block->memory + block->size;
|
||||||
|
if (ptr >= block->memory && ptr < block->head)
|
||||||
|
{
|
||||||
|
result = PtrType::Head;
|
||||||
|
}
|
||||||
|
else if (ptr >= block->tail && ptr < blockEnd)
|
||||||
|
{
|
||||||
|
result = PtrType::Tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto PtrIsLastAllocationInBlock = [&ClassifyPtr](DqnAllocatorMetadata const *metadata,
|
||||||
|
DqnMemStack::Block const *block,
|
||||||
|
u8 const *ptr) -> bool {
|
||||||
|
PtrType type = ClassifyPtr(block, ptr);
|
||||||
|
bool result = false;
|
||||||
|
if (type == PtrType::Head)
|
||||||
|
{
|
||||||
|
isize const oldMemSize = *(metadata->PtrToAllocAmount(ptr));
|
||||||
|
u8 const *ptrEnd = ptr + oldMemSize + metadata->GetAllocTailSize();
|
||||||
|
result = (ptrEnd == block->head);
|
||||||
|
}
|
||||||
|
else if (type == PtrType::Tail)
|
||||||
|
{
|
||||||
|
u8 offsetToSrc = *(metadata->PtrToOffsetToSrc(ptr));
|
||||||
|
auto *actualPtr = ptr - offsetToSrc;
|
||||||
|
result = (actualPtr == block->tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
if (request_.type == DqnMemAPI::Type::Alloc)
|
if (request_.type == DqnMemAPI::Type::Alloc)
|
||||||
{
|
{
|
||||||
auto *request = &request_.alloc;
|
auto *request = &request_.alloc;
|
||||||
@ -3119,61 +3170,115 @@ FILE_SCOPE u8 *DqnMemAPIInternal_StackAllocatorCallback(DqnMemAPI *api, DqnMemAP
|
|||||||
{
|
{
|
||||||
// IMPORTANT: This is a _naive_ realloc scheme for stack allocation.
|
// IMPORTANT: This is a _naive_ realloc scheme for stack allocation.
|
||||||
auto *request = &request_.realloc;
|
auto *request = &request_.realloc;
|
||||||
|
u8 *const ptr = (u8 *)request->oldMemPtr;
|
||||||
DqnMemStack::Block *block = stack->block;
|
for (DqnMemStack::Block *block = stack->block; block; block = block->prevBlock)
|
||||||
u8 const *blockEnd = block->memory + block->size;
|
|
||||||
u8 const *blockUsedUpTo = block->memory + block->used;
|
|
||||||
|
|
||||||
usize oldMemSize = *stack->metadata.PtrToAllocAmount((u8 *)request->oldMemPtr);
|
|
||||||
u8 alignment = *stack->metadata.PtrToAlignment((u8 *)request->oldMemPtr);
|
|
||||||
u8 *checkPtr = (u8 *)request->oldMemPtr + oldMemSize + stack->metadata.GetAllocTailSize();
|
|
||||||
|
|
||||||
// Last allocation, can safely allocate the remainder space.
|
|
||||||
if (checkPtr == blockUsedUpTo)
|
|
||||||
{
|
{
|
||||||
usize extraBytesRequired = request->newRequestSize - oldMemSize;
|
DQN_ASSERT(ptr >= block->memory && ptr <= (block->memory + block->size));
|
||||||
DQN_ASSERT(extraBytesRequired > 0);
|
}
|
||||||
|
|
||||||
bool enoughSpace = (blockUsedUpTo + extraBytesRequired) < blockEnd;
|
DqnMemStack::Block *const block = stack->block;
|
||||||
if (enoughSpace)
|
isize const oldMemSize = *stack->metadata.PtrToAllocAmount(ptr);
|
||||||
|
isize const extraBytesReq = request->newSize - oldMemSize;
|
||||||
|
u8 const alignment = *stack->metadata.PtrToAlignment(ptr);
|
||||||
|
DQN_ASSERT(extraBytesReq > 0);
|
||||||
|
|
||||||
|
PtrType type = ClassifyPtr(block, ptr);
|
||||||
|
if (PtrIsLastAllocationInBlock(&stack->metadata, block, ptr))
|
||||||
|
{
|
||||||
|
bool enoughSpace = false;
|
||||||
|
if (type == PtrType::Head)
|
||||||
{
|
{
|
||||||
stack->Pop(request->oldMemPtr, false);
|
DQN_ASSERT((block->head + extraBytesReq) >= block->memory);
|
||||||
|
|
||||||
result = (u8 *)stack->Push(request->newRequestSize, alignment);
|
enoughSpace = (block->head + extraBytesReq) < block->tail;
|
||||||
DQN_ASSERT(stack->block == block && result == request->oldMemPtr);
|
if (enoughSpace)
|
||||||
success = true;
|
{
|
||||||
|
stack->Pop(ptr, false);
|
||||||
|
|
||||||
|
result = (u8 *)stack->Push(request->newSize, alignment);
|
||||||
|
DQN_ASSERT(stack->block == block && result == request->oldMemPtr);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
DQN_ASSERT(type == PtrType::Tail);
|
||||||
|
DQN_ASSERT((block->tail - extraBytesReq) < (block->memory + block->size));
|
||||||
|
|
||||||
|
enoughSpace = (block->tail - extraBytesReq) > block->head;
|
||||||
|
if (enoughSpace)
|
||||||
|
{
|
||||||
|
stack->Pop(ptr, false);
|
||||||
|
result = (u8 *)stack->Push(request->newSize, alignment);
|
||||||
|
DqnMem_Copy(result, ptr, oldMemSize);
|
||||||
|
result[oldMemSize] = 0;
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
DQN_ASSERT(stack->block == block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enoughSpace)
|
||||||
{
|
{
|
||||||
// TODO(doyle): Does realloc need clear to zero flag as well?
|
// TODO(doyle): Does realloc need clear to zero flag as well?
|
||||||
// Else, last allocation but not enough space in block. Create a new block and copy
|
// Else, last allocation but not enough space in block. Create a new block and
|
||||||
|
// copy
|
||||||
DqnMemStack::Block *oldBlock = block;
|
DqnMemStack::Block *oldBlock = block;
|
||||||
result = (u8 *)stack->Push(request->newRequestSize, alignment);
|
if (type == PtrType::Head)
|
||||||
|
{
|
||||||
|
result = (u8 *)stack->Push(request->newSize, alignment);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DQN_ASSERT(type == PtrType::Tail);
|
||||||
|
result = (u8 *)stack->PushOnTail(request->newSize, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
DQN_ASSERT(stack->block->prevBlock == oldBlock);
|
DQN_ASSERT(stack->block->prevBlock == oldBlock);
|
||||||
DQN_ASSERT(stack->block != oldBlock);
|
DQN_ASSERT(stack->block != oldBlock);
|
||||||
|
DqnMem_Copy(result, ptr, oldMemSize);
|
||||||
DqnMem_Copy(result, (u8 *)request->oldMemPtr, request->oldSize);
|
|
||||||
|
|
||||||
// Switch to old block, pop the ptr and return the new block on top.
|
// Switch to old block, pop the ptr and return the new block on top.
|
||||||
auto *newBlock = stack->block;
|
auto *newBlock = stack->block;
|
||||||
stack->block = oldBlock;
|
stack->block = oldBlock;
|
||||||
stack->Pop(request->oldMemPtr, false);
|
stack->Pop(ptr, false);
|
||||||
stack->block = newBlock;
|
stack->block = newBlock;
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DQN_LOGE(
|
if (request->newSize < request->oldSize)
|
||||||
"Lost %$_d, the ptr to realloc is sandwiched between other allocations (LIFO)", oldMemSize);
|
|
||||||
result = (u8 *)stack->Push(request->newRequestSize, alignment);
|
|
||||||
if (result)
|
|
||||||
{
|
{
|
||||||
success = true;
|
// NOTE: This is questionable behaviour. We don't reclaim data since it's not
|
||||||
DqnMem_Copy(result, (u8 *)request->oldMemPtr, request->oldSize);
|
// well-defined in a stack allocator. This would cause gaps in memory.
|
||||||
|
success = false; // Deny updating statistics.
|
||||||
|
result = ptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DQN_LOGE(
|
||||||
|
"Lost %$_d, the ptr to realloc is sandwiched between other allocations (LIFO)",
|
||||||
|
oldMemSize);
|
||||||
|
|
||||||
|
if (type == PtrType::Head)
|
||||||
|
{
|
||||||
|
result = (u8 *)stack->Push(request->newSize, alignment);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DQN_ASSERT(type == PtrType::Tail);
|
||||||
|
result = (u8 *)stack->PushOnTail(request->newSize, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
success = true;
|
||||||
|
DqnMem_Copy(result, ptr, oldMemSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3182,22 +3287,17 @@ FILE_SCOPE u8 *DqnMemAPIInternal_StackAllocatorCallback(DqnMemAPI *api, DqnMemAP
|
|||||||
auto *request = &request_.free;
|
auto *request = &request_.free;
|
||||||
DQN_ASSERT(request_.type == DqnMemAPI::Type::Free);
|
DQN_ASSERT(request_.type == DqnMemAPI::Type::Free);
|
||||||
|
|
||||||
DqnMemStack::Block *block = stack->block;
|
DqnMemStack::Block *const block = stack->block;
|
||||||
u8 const *blockUsedUpTo = block->memory + block->used;
|
u8 *const ptr = (u8 *)request->ptrToFree;
|
||||||
|
|
||||||
usize oldMemSize = *stack->metadata.PtrToAllocAmount((u8 *)request->ptrToFree);
|
if (PtrIsLastAllocationInBlock(&stack->metadata, block, ptr))
|
||||||
u8 *checkPtr = (u8 *)request->ptrToFree + oldMemSize + stack->metadata.GetAllocTailSize();
|
|
||||||
|
|
||||||
// Last allocation, can safely pop the allocation
|
|
||||||
if (checkPtr == blockUsedUpTo)
|
|
||||||
{
|
{
|
||||||
success = true;
|
stack->Pop(ptr, false);
|
||||||
stack->Pop(request->ptrToFree);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DQN_LOGE("Lost %$_d, the ptr to free is sandwiched between other allocations (LIFO)",
|
isize const oldMemSize = *(stack->metadata.PtrToAllocAmount(ptr));
|
||||||
oldMemSize);
|
DQN_LOGE("Lost %$_d, the ptr to free is sandwiched between other allocations (LIFO)", oldMemSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3209,20 +3309,20 @@ FILE_SCOPE u8 *DqnMemAPIInternal_StackAllocatorCallback(DqnMemAPI *api, DqnMemAP
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *DqnMemAPI::Realloc(void *const oldPtr, usize const oldSize, usize const newSize)
|
void *DqnMemAPI::Realloc(void *const oldPtr, isize const oldSize, isize const newSize)
|
||||||
{
|
{
|
||||||
Request request = {};
|
Request request = {};
|
||||||
request.type = Type::Realloc;
|
request.type = Type::Realloc;
|
||||||
request.userContext = this->userContext;
|
request.userContext = this->userContext;
|
||||||
request.realloc.newRequestSize = newSize;
|
request.realloc.newSize = newSize;
|
||||||
request.realloc.oldMemPtr = oldPtr;
|
request.realloc.oldMemPtr = oldPtr;
|
||||||
request.realloc.oldSize = oldSize;
|
request.realloc.oldSize = oldSize;
|
||||||
|
|
||||||
void *result = (void *)this->allocator(this, request);
|
void *result = (void *)this->allocator(this, request);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *DqnMemAPI::Alloc(usize const size, bool const zeroClear)
|
void *DqnMemAPI::Alloc(isize const size, bool const zeroClear)
|
||||||
{
|
{
|
||||||
Request request = {};
|
Request request = {};
|
||||||
request.type = Type::Alloc;
|
request.type = Type::Alloc;
|
||||||
@ -3234,7 +3334,7 @@ void *DqnMemAPI::Alloc(usize const size, bool const zeroClear)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DqnMemAPI::Free(void *const ptrToFree, usize const sizeToFree)
|
void DqnMemAPI::Free(void *const ptrToFree, isize const sizeToFree)
|
||||||
{
|
{
|
||||||
Request request = {};
|
Request request = {};
|
||||||
request.type = Type::Free;
|
request.type = Type::Free;
|
||||||
@ -3355,20 +3455,20 @@ u8 *DqnAllocatorMetadata::PtrToOffsetToSrc(u8 const *ptr) const
|
|||||||
return (u8 *)u8Ptr;
|
return (u8 *)u8Ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize *DqnAllocatorMetadata::PtrToAllocAmount(u8 const *ptr) const
|
isize *DqnAllocatorMetadata::PtrToAllocAmount(u8 const *ptr) const
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
u8 const *u8Ptr;
|
u8 const *u8Ptr;
|
||||||
u32 const *u32Ptr;
|
u32 const *u32Ptr;
|
||||||
usize const *usizePtr;
|
isize const *isizePtr;
|
||||||
};
|
};
|
||||||
u8Ptr = ptr - this->allocHeadSize + OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE;
|
u8Ptr = ptr - this->allocHeadSize + OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE;
|
||||||
return (usize *)usizePtr;
|
return (isize *)isizePtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 *DqnAllocatorMetadata::PtrToTailBoundsGuard(u8 const *ptr) const
|
u32 *DqnAllocatorMetadata::PtrToTailBoundsGuard(u8 const *ptr) const
|
||||||
{
|
{
|
||||||
usize size = *PtrToAllocAmount(ptr);
|
isize size = *PtrToAllocAmount(ptr);
|
||||||
union {
|
union {
|
||||||
u8 const *u8Ptr;
|
u8 const *u8Ptr;
|
||||||
u32 const *u32Ptr;
|
u32 const *u32Ptr;
|
||||||
@ -3379,30 +3479,32 @@ u32 *DqnAllocatorMetadata::PtrToTailBoundsGuard(u8 const *ptr) const
|
|||||||
|
|
||||||
// #DqnMemStack
|
// #DqnMemStack
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
DQN_FILE_SCOPE DqnMemStack::Block *DqnMemStackInternal_AllocateBlock(usize size, bool zeroClear,
|
DQN_FILE_SCOPE DqnMemStack::Block *DqnMemStackInternal_AllocateBlock(isize size, bool zeroClear,
|
||||||
DqnMemAPI *const api)
|
DqnMemAPI *const api)
|
||||||
{
|
{
|
||||||
|
DQN_ASSERT(size > 0);
|
||||||
if (!api || !api->IsValid())
|
if (!api || !api->IsValid())
|
||||||
{
|
{
|
||||||
DQN_LOGE("Could not allocate block with api, api is null or is valid check failed.");
|
DQN_LOGE("Could not allocate block with api, api is null or is valid check failed.");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize totalSize = sizeof(DqnMemStack::Block) + size;
|
isize totalSize = sizeof(DqnMemStack::Block) + size;
|
||||||
auto *result = (DqnMemStack::Block *)api->Alloc(totalSize, zeroClear);
|
auto *result = (DqnMemStack::Block *)api->Alloc(totalSize, zeroClear);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
result->memory = (u8 *)(result + sizeof(*result));
|
result->memory = ((u8 *)result) + sizeof(*result);
|
||||||
result->size = size;
|
result->size = size;
|
||||||
result->used = 0;
|
result->head = result->memory;
|
||||||
|
result->tail = result->memory + size;
|
||||||
result->prevBlock = nullptr;
|
result->prevBlock = nullptr;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DqnMemStack::Init(void *const mem, usize size, bool zeroClear, u32 flags_)
|
bool DqnMemStack::Init(void *const mem, isize size, bool zeroClear, u32 flags_)
|
||||||
{
|
{
|
||||||
if (!mem)
|
if (!mem)
|
||||||
{
|
{
|
||||||
@ -3424,8 +3526,9 @@ bool DqnMemStack::Init(void *const mem, usize size, bool zeroClear, u32 flags_)
|
|||||||
|
|
||||||
this->block = (DqnMemStack::Block *)mem;
|
this->block = (DqnMemStack::Block *)mem;
|
||||||
this->block->memory = (u8 *)mem + sizeof(DqnMemStack::Block);
|
this->block->memory = (u8 *)mem + sizeof(DqnMemStack::Block);
|
||||||
this->block->used = 0;
|
|
||||||
this->block->size = size - sizeof(DqnMemStack::Block);
|
this->block->size = size - sizeof(DqnMemStack::Block);
|
||||||
|
this->block->head = this->block->memory;
|
||||||
|
this->block->tail = this->block->memory + this->block->size;
|
||||||
this->block->prevBlock = nullptr;
|
this->block->prevBlock = nullptr;
|
||||||
|
|
||||||
this->memAPI = nullptr;
|
this->memAPI = nullptr;
|
||||||
@ -3439,7 +3542,7 @@ bool DqnMemStack::Init(void *const mem, usize size, bool zeroClear, u32 flags_)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DqnMemStack::Init(usize size, bool zeroClear, u32 flags_, DqnMemAPI *const api)
|
bool DqnMemStack::Init(isize size, bool zeroClear, u32 flags_, DqnMemAPI *const api)
|
||||||
{
|
{
|
||||||
if (!this || size < 0) return false;
|
if (!this || size < 0) return false;
|
||||||
|
|
||||||
@ -3474,7 +3577,7 @@ bool DqnMemStack::Init(usize size, bool zeroClear, u32 flags_, DqnMemAPI *const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *DqnMemStack::Push(usize size, u8 alignment)
|
FILE_SCOPE void *DqnMemStackInternal_Push(DqnMemStack *stack, isize size, u8 alignment, bool pushToHead)
|
||||||
{
|
{
|
||||||
DQN_ASSERT(size >= 0 && (alignment % 2 == 0));
|
DQN_ASSERT(size >= 0 && (alignment % 2 == 0));
|
||||||
DQN_ASSERTM(alignment <= 128,
|
DQN_ASSERTM(alignment <= 128,
|
||||||
@ -3486,33 +3589,28 @@ void *DqnMemStack::Push(usize size, u8 alignment)
|
|||||||
|
|
||||||
// Allocate New Block If Full
|
// Allocate New Block If Full
|
||||||
// =============================================================================================
|
// =============================================================================================
|
||||||
DqnAllocatorMetadata *myMetadata = &this->metadata;
|
DqnAllocatorMetadata *myMetadata = &stack->metadata;
|
||||||
usize actualSize = myMetadata->GetAllocationSize(size, alignment);
|
isize actualSize = myMetadata->GetAllocationSize(size, alignment);
|
||||||
bool needNewBlock = false;
|
bool needNewBlock = false;
|
||||||
if (!block)
|
if (stack->block)
|
||||||
{
|
{
|
||||||
needNewBlock = true;
|
if (pushToHead) needNewBlock = ((stack->block->head + actualSize) > stack->block->tail);
|
||||||
}
|
else needNewBlock = ((stack->block->tail - actualSize) < stack->block->head);
|
||||||
else
|
|
||||||
{
|
|
||||||
u8 const *blockUsedUpTo = this->block->memory + this->block->used;
|
|
||||||
u8 const *blockEnd = this->block->memory + this->block->size;
|
|
||||||
needNewBlock = ((blockUsedUpTo + actualSize) > blockEnd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needNewBlock)
|
if (needNewBlock)
|
||||||
{
|
{
|
||||||
if (Dqn_BitIsSet(this->flags, Flag::NonExpandable))
|
if (Dqn_BitIsSet(stack->flags, DqnMemStack::Flag::NonExpandable))
|
||||||
{
|
{
|
||||||
DQN_LOGE("Allocator is non-expandable and has run out of memory.");
|
DQN_LOGE("Allocator is non-expandable and has run out of memory.");
|
||||||
if (Dqn_BitIsSet(this->flags, Flag::NonExpandableAssert))
|
if (Dqn_BitIsSet(stack->flags, DqnMemStack::Flag::NonExpandableAssert))
|
||||||
DQN_ASSERT(DQN_INVALID_CODE_PATH);
|
DQN_ASSERT(DQN_INVALID_CODE_PATH);
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize newBlockSize = DQN_MAX(actualSize, DqnMemStack::MINIMUM_BLOCK_SIZE);
|
isize newBlockSize = DQN_MAX(actualSize, DqnMemStack::MINIMUM_BLOCK_SIZE);
|
||||||
DqnMemStack::Block *newBlock = DqnMemStackInternal_AllocateBlock(newBlockSize, true, this->memAPI);
|
DqnMemStack::Block *newBlock = DqnMemStackInternal_AllocateBlock(newBlockSize, true, stack->memAPI);
|
||||||
if (!newBlock)
|
if (!newBlock)
|
||||||
{
|
{
|
||||||
DQN_LOGE(
|
DQN_LOGE(
|
||||||
@ -3521,20 +3619,28 @@ void *DqnMemStack::Push(usize size, u8 alignment)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
newBlock->prevBlock = this->block;
|
newBlock->prevBlock = stack->block;
|
||||||
this->block = newBlock;
|
stack->block = newBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate Ptr To Give Client
|
// Calculate Ptr To Give Client
|
||||||
// =============================================================================================
|
// =============================================================================================
|
||||||
u8 *currPtr = this->block->memory + this->block->used;
|
u8 *currPtr = (pushToHead) ? (stack->block->head) : (stack->block->tail - actualSize);
|
||||||
u8 *result = (u8 *)DQN_ALIGN_POW_N((currPtr + myMetadata->GetAllocHeadSize()), alignment);
|
u8 *result = (u8 *)DQN_ALIGN_POW_N((currPtr + myMetadata->GetAllocHeadSize()), alignment);
|
||||||
|
|
||||||
usize const offsetToSrc = result - currPtr;
|
isize const offsetToSrc = result - currPtr;
|
||||||
DQN_ASSERT(offsetToSrc > 0 && offsetToSrc < (u8)-1);
|
DQN_ASSERT(offsetToSrc > 0 && offsetToSrc < (u8)-1);
|
||||||
|
|
||||||
this->block->used += actualSize;
|
if (pushToHead)
|
||||||
DQN_ASSERT(this->block->used <= this->block->size);
|
{
|
||||||
|
stack->block->head += actualSize;
|
||||||
|
DQN_ASSERT(stack->block->head <= stack->block->tail);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stack->block->tail -= actualSize;
|
||||||
|
DQN_ASSERT(stack->block->tail >= stack->block->head);
|
||||||
|
}
|
||||||
|
|
||||||
// Instrument allocation with guards and metadata
|
// Instrument allocation with guards and metadata
|
||||||
// =============================================================================================
|
// =============================================================================================
|
||||||
@ -3548,7 +3654,7 @@ void *DqnMemStack::Push(usize size, u8 alignment)
|
|||||||
auto *allocAmount = myMetadata->PtrToAllocAmount(result);
|
auto *allocAmount = myMetadata->PtrToAllocAmount(result);
|
||||||
*allocAmount = size;
|
*allocAmount = size;
|
||||||
|
|
||||||
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
|
if (Dqn_BitIsSet(stack->flags, DqnMemStack::Flag::BoundsGuard))
|
||||||
{
|
{
|
||||||
auto *headGuard = myMetadata->PtrToHeadBoundsGuard(result);
|
auto *headGuard = myMetadata->PtrToHeadBoundsGuard(result);
|
||||||
auto *tailGuard = myMetadata->PtrToTailBoundsGuard(result);
|
auto *tailGuard = myMetadata->PtrToTailBoundsGuard(result);
|
||||||
@ -3565,7 +3671,7 @@ void *DqnMemStack::Push(usize size, u8 alignment)
|
|||||||
"Adding bounds guard should not destroy alignment! %p != %p", result,
|
"Adding bounds guard should not destroy alignment! %p != %p", result,
|
||||||
checkAlignment);
|
checkAlignment);
|
||||||
|
|
||||||
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
|
if (Dqn_BitIsSet(stack->flags, DqnMemStack::Flag::BoundsGuard))
|
||||||
{
|
{
|
||||||
myMetadata->AddAllocation(result);
|
myMetadata->AddAllocation(result);
|
||||||
myMetadata->CheckAllocations();
|
myMetadata->CheckAllocations();
|
||||||
@ -3575,6 +3681,18 @@ void *DqnMemStack::Push(usize size, u8 alignment)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *DqnMemStack::PushOnTail(isize size, u8 alignment)
|
||||||
|
{
|
||||||
|
void *result = DqnMemStackInternal_Push(this, size, alignment, false);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *DqnMemStack::Push(isize size, u8 alignment)
|
||||||
|
{
|
||||||
|
void *result = DqnMemStackInternal_Push(this, size, alignment, true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
FILE_SCOPE void DqnMemStackInternal_KillMetadataPtrsExistingInBlock(DqnAllocatorMetadata *metadata,
|
FILE_SCOPE void DqnMemStackInternal_KillMetadataPtrsExistingInBlock(DqnAllocatorMetadata *metadata,
|
||||||
DqnMemStack::Block const *block)
|
DqnMemStack::Block const *block)
|
||||||
{
|
{
|
||||||
@ -3606,20 +3724,34 @@ void DqnMemStack::Pop(void *const ptr, bool zeroClear)
|
|||||||
myMetadata->RemoveAllocation(bytePtr);
|
myMetadata->RemoveAllocation(bytePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
usize size = *(myMetadata->PtrToAllocAmount(bytePtr));
|
isize const size = *(myMetadata->PtrToAllocAmount(bytePtr));
|
||||||
u8 alignment = *(myMetadata->PtrToAlignment(bytePtr));
|
u8 const alignment = *(myMetadata->PtrToAlignment(bytePtr));
|
||||||
u8 offsetToSrc = *(myMetadata->PtrToOffsetToSrc(bytePtr));
|
u8 const offsetToSrc = *(myMetadata->PtrToOffsetToSrc(bytePtr));
|
||||||
|
|
||||||
usize actualSize = myMetadata->GetAllocationSize(size, alignment);
|
isize actualSize = myMetadata->GetAllocationSize(size, alignment);
|
||||||
u8 *start = bytePtr - offsetToSrc;
|
u8 *const start = bytePtr - offsetToSrc;
|
||||||
u8 *end = start + actualSize;
|
u8 *const end = start + actualSize;
|
||||||
|
u8 const *const blockEnd = this->block->memory + this->block->size;
|
||||||
|
|
||||||
u8 const *blockUsedUpTo = this->block->memory + this->block->used;
|
if (bytePtr >= this->block->memory && bytePtr < this->block->head)
|
||||||
DQN_ASSERTM(end == blockUsedUpTo, "Pointer to pop was not the last allocation! %p != %p", end,
|
{
|
||||||
blockUsedUpTo);
|
DQN_ASSERTM(end == this->block->head, "Pointer to pop was not the last allocation! %p != %p", end, this->block->head);
|
||||||
|
|
||||||
this->block->used -= actualSize;
|
this->block->head -= actualSize;
|
||||||
DQN_ASSERT(this->block->used >= 0);
|
DQN_ASSERT(this->block->head >= this->block->memory);
|
||||||
|
}
|
||||||
|
else if (bytePtr >= this->block->tail && bytePtr < blockEnd)
|
||||||
|
{
|
||||||
|
DQN_ASSERTM(start == this->block->tail, "Pointer to pop was not the last allocation! %p != %p", start,
|
||||||
|
this->block->tail);
|
||||||
|
|
||||||
|
this->block->tail += actualSize;
|
||||||
|
DQN_ASSERT(this->block->tail <= blockEnd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DQN_ASSERTM(DQN_INVALID_CODE_PATH, "Pointer to free does not belong to current block!");
|
||||||
|
}
|
||||||
|
|
||||||
if (zeroClear)
|
if (zeroClear)
|
||||||
DqnMem_Set(start, 0, end - start);
|
DqnMem_Set(start, 0, end - start);
|
||||||
@ -3661,7 +3793,7 @@ bool DqnMemStack::FreeMemBlock(DqnMemStack::Block *memBlock)
|
|||||||
DqnMemStackInternal_KillMetadataPtrsExistingInBlock(&this->metadata, blockToFree);
|
DqnMemStackInternal_KillMetadataPtrsExistingInBlock(&this->metadata, blockToFree);
|
||||||
}
|
}
|
||||||
|
|
||||||
usize realSize = blockToFree->size + sizeof(DqnMemStack::Block);
|
isize realSize = blockToFree->size + sizeof(DqnMemStack::Block);
|
||||||
this->memAPI->Free(blockToFree, realSize);
|
this->memAPI->Free(blockToFree, realSize);
|
||||||
|
|
||||||
// No more blocks, then last block has been freed
|
// No more blocks, then last block has been freed
|
||||||
@ -3687,7 +3819,8 @@ void DqnMemStack::ClearCurrBlock(bool zeroClear)
|
|||||||
DqnMemStackInternal_KillMetadataPtrsExistingInBlock(&this->metadata, this->block);
|
DqnMemStackInternal_KillMetadataPtrsExistingInBlock(&this->metadata, this->block);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->block->used = 0;
|
this->block->head = this->block->memory;
|
||||||
|
this->block->tail = this->block->memory + this->block->size;
|
||||||
if (zeroClear)
|
if (zeroClear)
|
||||||
{
|
{
|
||||||
DqnMem_Clear(this->block->memory, 0, this->block->size);
|
DqnMem_Clear(this->block->memory, 0, this->block->size);
|
||||||
@ -3701,21 +3834,31 @@ DqnMemStack::Info DqnMemStack::GetInfo() const
|
|||||||
Info result = {};
|
Info result = {};
|
||||||
for (Block *block_ = this->block; block_; block_ = block_->prevBlock)
|
for (Block *block_ = this->block; block_; block_ = block_->prevBlock)
|
||||||
{
|
{
|
||||||
result.totalUsed += block_->used;
|
u8 const *blockEnd = block_->memory + block_->size;
|
||||||
|
isize usageFromHead = block_->head - block_->memory;
|
||||||
|
isize usageFromTail = blockEnd - block_->tail;
|
||||||
|
|
||||||
|
result.totalUsed += usageFromHead + usageFromTail;
|
||||||
result.totalSize += block_->size;
|
result.totalSize += block_->size;
|
||||||
result.wastedSize += block_->size - block_->used;
|
result.wastedSize += (block_->size - usageFromHead - usageFromTail);
|
||||||
result.numBlocks++;
|
result.numBlocks++;
|
||||||
}
|
}
|
||||||
result.wastedSize -= this->block->size - this->block->used; // Don't include the curr block
|
|
||||||
|
u8 const *blockEnd = this->block->memory + this->block->size;
|
||||||
|
isize usageFromHead = this->block->head - this->block->memory;
|
||||||
|
isize usageFromTail = blockEnd - this->block->tail;
|
||||||
|
result.wastedSize -= (this->block->size - usageFromHead - usageFromTail); // Don't include the curr block
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DqnMemStack::TempRegion DqnMemStack::TempRegionBegin()
|
DqnMemStack::TempRegion DqnMemStack::TempRegionBegin()
|
||||||
{
|
{
|
||||||
TempRegion result = {};
|
TempRegion result = {};
|
||||||
result.stack = this;
|
result.stack = this;
|
||||||
result.startingBlock = this->block;
|
result.startingBlock = this->block;
|
||||||
result.used = this->block->used;
|
result.startingBlockHead = (this->block) ? this->block->head : nullptr;
|
||||||
|
result.startingBlockTail = (this->block) ? this->block->tail : nullptr;
|
||||||
|
|
||||||
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
|
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
|
||||||
{
|
{
|
||||||
@ -3737,8 +3880,16 @@ void DqnMemStack::TempRegionEnd(TempRegion region)
|
|||||||
|
|
||||||
if (this->block)
|
if (this->block)
|
||||||
{
|
{
|
||||||
DQN_ASSERT(this->block->used >= region.used);
|
// Debug checks
|
||||||
this->block->used = region.used;
|
{
|
||||||
|
u8 const *const start = this->block->memory;
|
||||||
|
u8 const *const end = start + this->block->size;
|
||||||
|
DQN_ASSERT(region.startingBlockHead >= start && region.startingBlockHead <= end);
|
||||||
|
DQN_ASSERT(region.startingBlockTail >= start && region.startingBlockTail <= end);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->block->head = region.startingBlockHead;
|
||||||
|
this->block->tail = region.startingBlockTail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
|
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
|
||||||
|
@ -2354,7 +2354,6 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
{
|
{
|
||||||
DqnMemStack stack = {};
|
DqnMemStack stack = {};
|
||||||
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true, DqnMemStack::Flag::BoundsGuard));
|
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true, DqnMemStack::Flag::BoundsGuard));
|
||||||
stack.Free();
|
|
||||||
|
|
||||||
i32 const ALIGN64 = 64;
|
i32 const ALIGN64 = 64;
|
||||||
i32 const ALIGN16 = 16;
|
i32 const ALIGN16 = 16;
|
||||||
@ -2384,6 +2383,7 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
stack.Pop(result1);
|
stack.Pop(result1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stack.Free();
|
||||||
Log(Status::Ok, "Check allocated alignment to 4, 16, 64");
|
Log(Status::Ok, "Check allocated alignment to 4, 16, 64");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2410,7 +2410,8 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
auto *oldBlock = stack.block;
|
auto *oldBlock = stack.block;
|
||||||
DQN_ASSERT(oldBlock);
|
DQN_ASSERT(oldBlock);
|
||||||
DQN_ASSERT(oldBlock->size == DQN_MEGABYTE(1));
|
DQN_ASSERT(oldBlock->size == DQN_MEGABYTE(1));
|
||||||
DQN_ASSERT(oldBlock->used == 0);
|
DQN_ASSERT(oldBlock->head == oldBlock->head);
|
||||||
|
DQN_ASSERT(oldBlock->tail == oldBlock->tail);
|
||||||
DQN_ASSERT(oldBlock->prevBlock == nullptr);
|
DQN_ASSERT(oldBlock->prevBlock == nullptr);
|
||||||
|
|
||||||
auto *result1 = stack.Push(DQN_MEGABYTE(2));
|
auto *result1 = stack.Push(DQN_MEGABYTE(2));
|
||||||
@ -2432,7 +2433,8 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
if (1)
|
if (1)
|
||||||
{
|
{
|
||||||
DqnMemStack::Block *blockToReturnTo = stack.block;
|
DqnMemStack::Block *blockToReturnTo = stack.block;
|
||||||
auto usedBefore = blockToReturnTo->used;
|
auto headBefore = blockToReturnTo->head;
|
||||||
|
auto tailBefore = blockToReturnTo->tail;
|
||||||
if (1)
|
if (1)
|
||||||
{
|
{
|
||||||
auto memGuard1 = stack.TempRegionGuard();
|
auto memGuard1 = stack.TempRegionGuard();
|
||||||
@ -2440,7 +2442,8 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
auto *result3 = stack.Push(100);
|
auto *result3 = stack.Push(100);
|
||||||
auto *result4 = stack.Push(100);
|
auto *result4 = stack.Push(100);
|
||||||
DQN_ASSERT(result2 && result3 && result4);
|
DQN_ASSERT(result2 && result3 && result4);
|
||||||
DQN_ASSERT(stack.block->used > usedBefore);
|
DQN_ASSERT(stack.block->head != headBefore);
|
||||||
|
DQN_ASSERT(stack.block->tail == tailBefore);
|
||||||
DQN_ASSERT(stack.block->memory == blockToReturnTo->memory);
|
DQN_ASSERT(stack.block->memory == blockToReturnTo->memory);
|
||||||
|
|
||||||
// Force allocation of new block
|
// Force allocation of new block
|
||||||
@ -2451,14 +2454,16 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
}
|
}
|
||||||
|
|
||||||
DQN_ASSERT(stack.block == blockToReturnTo);
|
DQN_ASSERT(stack.block == blockToReturnTo);
|
||||||
DQN_ASSERT(stack.block->used == usedBefore);
|
DQN_ASSERT(stack.block->head == headBefore);
|
||||||
|
DQN_ASSERT(stack.block->tail == tailBefore);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check temporary regions keep state
|
// Check temporary regions keep state
|
||||||
if (1)
|
if (1)
|
||||||
{
|
{
|
||||||
DqnMemStack::Block *blockToReturnTo = stack.block;
|
DqnMemStack::Block *blockToReturnTo = stack.block;
|
||||||
auto usedBefore = blockToReturnTo->used;
|
auto headBefore = blockToReturnTo->head;
|
||||||
|
auto tailBefore = blockToReturnTo->tail;
|
||||||
if (1)
|
if (1)
|
||||||
{
|
{
|
||||||
auto memGuard1 = stack.TempRegionGuard();
|
auto memGuard1 = stack.TempRegionGuard();
|
||||||
@ -2466,7 +2471,8 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
auto *result3 = stack.Push(100);
|
auto *result3 = stack.Push(100);
|
||||||
auto *result4 = stack.Push(100);
|
auto *result4 = stack.Push(100);
|
||||||
DQN_ASSERT(result2 && result3 && result4);
|
DQN_ASSERT(result2 && result3 && result4);
|
||||||
DQN_ASSERT(stack.block->used > usedBefore);
|
DQN_ASSERT(stack.block->head != headBefore);
|
||||||
|
DQN_ASSERT(stack.block->tail == tailBefore);
|
||||||
DQN_ASSERT(stack.block->memory == blockToReturnTo->memory);
|
DQN_ASSERT(stack.block->memory == blockToReturnTo->memory);
|
||||||
|
|
||||||
// Force allocation of new block
|
// Force allocation of new block
|
||||||
@ -2597,8 +2603,20 @@ FILE_SCOPE void DqnMemStack_Test()
|
|||||||
DQN_ASSERT(*head == DqnAllocatorMetadata::HEAD_GUARD_VALUE);
|
DQN_ASSERT(*head == DqnAllocatorMetadata::HEAD_GUARD_VALUE);
|
||||||
DQN_ASSERT(*tail == DqnAllocatorMetadata::TAIL_GUARD_VALUE);
|
DQN_ASSERT(*tail == DqnAllocatorMetadata::TAIL_GUARD_VALUE);
|
||||||
|
|
||||||
|
stack.Free();
|
||||||
Log(Status::Ok, "Bounds guards are placed adjacent and have magic values.");
|
Log(Status::Ok, "Bounds guards are placed adjacent and have magic values.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Push to tail and head
|
||||||
|
if (1)
|
||||||
|
{
|
||||||
|
DqnMemStack stack = {};
|
||||||
|
DQN_ASSERT(stack.Init(DQN_MEGABYTE(1), true, DqnMemStack::Flag::BoundsGuard));
|
||||||
|
|
||||||
|
auto *result1 = stack.Push(100);
|
||||||
|
auto *result2 = stack.PushOnTail(100);
|
||||||
|
stack.Free();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
|
Loading…
Reference in New Issue
Block a user