From 4f665562f127d1662765cf2d62fb2b0c5a74e47a Mon Sep 17 00:00:00 2001 From: Doyle Date: Sun, 2 Sep 2018 13:28:19 +1000 Subject: [PATCH] Add default allocation type for memstacks, operator bool for slices --- DqnMemStack.cpp | 24 ++++++------ dqn.h | 100 +++++++++++++++++++++++++++--------------------- 2 files changed, 68 insertions(+), 56 deletions(-) diff --git a/DqnMemStack.cpp b/DqnMemStack.cpp index 779c39b..0f93b6f 100644 --- a/DqnMemStack.cpp +++ b/DqnMemStack.cpp @@ -10,10 +10,10 @@ FILE_SCOPE void DqnMemStack_Test() i32 const ALIGN64 = 64; i32 const ALIGN16 = 16; i32 const ALIGN4 = 4; - DqnMemStack::AllocTo allocTo = DqnMemStack::AllocTo::Head; + DqnMemStack::PushType push_type = DqnMemStack::PushType::Head; if (1) { - u8 *result1 = (u8 *)stack.Push(2, allocTo, ALIGN4); + u8 *result1 = (u8 *)stack.Push(2, push_type, ALIGN4); u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN4); DQN_ASSERT(result1 == result2); stack.Pop(result1); @@ -22,7 +22,7 @@ FILE_SCOPE void DqnMemStack_Test() if (1) { - u8 *result1 = (u8 *)stack.Push(120, allocTo, ALIGN16); + u8 *result1 = (u8 *)stack.Push(120, push_type, ALIGN16); u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN16); DQN_ASSERT(result1 == result2); stack.Pop(result1); @@ -31,7 +31,7 @@ FILE_SCOPE void DqnMemStack_Test() if (1) { - u8 *result1 = (u8 *)stack.Push(12, allocTo, ALIGN64); + u8 *result1 = (u8 *)stack.Push(12, push_type, ALIGN64); u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN64); DQN_ASSERT(result1 == result2); stack.Pop(result1); @@ -151,7 +151,7 @@ FILE_SCOPE void DqnMemStack_Test() auto stack = DqnMemStack(DQN_MEGABYTE(1), Dqn::ZeroClear::Yes, DqnMemStack::Flag::BoundsGuard); auto *pop1 = stack.Push(222); - auto *pop2 = stack.Push(333, DqnMemStack::AllocTo::Tail); + auto *pop2 = stack.Push(333, DqnMemStack::PushType::Tail); DqnMemStack::Block *blockToReturnTo = stack.block; auto headBefore = blockToReturnTo->head; @@ -160,9 +160,9 @@ FILE_SCOPE void DqnMemStack_Test() { auto memGuard1 = stack.TempRegionGuard(); auto *result2 = stack.Push(100); - auto *result3 = stack.Push(100, DqnMemStack::AllocTo::Tail); + auto *result3 = stack.Push(100, DqnMemStack::PushType::Tail); auto *result4 = stack.Push(100); - auto *result5 = stack.Push(100, DqnMemStack::AllocTo::Tail); + auto *result5 = stack.Push(100, DqnMemStack::PushType::Tail); DQN_ASSERT(result2 && result3 && result4 && result5); DQN_ASSERT(result3 > result5); DQN_ASSERT(result2 < result4); @@ -298,7 +298,7 @@ FILE_SCOPE void DqnMemStack_Test() DqnMemStack stack = DqnMemStack(DQN_MEGABYTE(1), Dqn::ZeroClear::Yes, DqnMemStack::Flag::BoundsGuard); auto *result1 = stack.Push(100); - auto *result2 = stack.Push(100, DqnMemStack::AllocTo::Tail); + auto *result2 = stack.Push(100, DqnMemStack::PushType::Tail); auto *headBefore = stack.block->head; auto *tail_before = stack.block->tail; DQN_ASSERT(result2 && result1); @@ -330,7 +330,7 @@ FILE_SCOPE void DqnMemStack_Test() DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size); auto *block_before = stack.block; - auto *result2 = stack.Push(DQN_MEGABYTE(1), DqnMemStack::AllocTo::Tail); + auto *result2 = stack.Push(DQN_MEGABYTE(1), DqnMemStack::PushType::Tail); DQN_ASSERT(result2 && result1); DQN_ASSERT(result2 != result1); DQN_ASSERT(stack.block->prev_block == block_before); @@ -360,7 +360,7 @@ FILE_SCOPE void DqnMemStack_Test() DQN_ASSERT(stack.block->tail == stack.block->memory + stack.block->size); auto *block_before = stack.block; - auto *result2 = stack.Push(DQN_MEGABYTE(1), DqnMemStack::AllocTo::Tail); + auto *result2 = stack.Push(DQN_MEGABYTE(1), DqnMemStack::PushType::Tail); DQN_ASSERT(result2 == nullptr); DQN_ASSERT(stack.block->prev_block == nullptr); DQN_ASSERT(stack.block == block_before); @@ -427,7 +427,7 @@ FILE_SCOPE void DqnMemStack_Test() auto *tail_before = stack.block->tail; isize buf_size = 16; - char *buf = (char *)stack.Push(buf_size, DqnMemStack::AllocTo::Tail); + char *buf = (char *)stack.Push(buf_size, DqnMemStack::PushType::Tail); DqnMem_Set(buf, 'X', buf_size); for (auto i = 0; i < buf_size; i++) DQN_ASSERT(buf[i] == 'X'); @@ -491,7 +491,7 @@ FILE_SCOPE void DqnMemStack_Test() auto *tail_before = stack.block->tail; isize buf_size = 16; - char *buf = (char *)stack.Push(buf_size, DqnMemStack::AllocTo::Tail); + char *buf = (char *)stack.Push(buf_size, DqnMemStack::PushType::Tail); DqnMem_Set(buf, 'X', buf_size); for (auto i = 0; i < buf_size; i++) DQN_ASSERT(buf[i] == 'X'); diff --git a/dqn.h b/dqn.h index eaf509d..2777c6b 100644 --- a/dqn.h +++ b/dqn.h @@ -770,19 +770,20 @@ struct DqnSlice union { T *data; T *str; }; int len; + operator bool() const { bool result = (str != nullptr); return result; } DqnSlice() = default; DqnSlice(T *data_, int len_) : data(data_), len(len_) {} }; -template +template using DqnBuffer = DqnSlice; #define DQN_BUFFER_STR_LIT(literal) DqnBuffer(literal, DQN_CHAR_COUNT(literal)) #define DQN_BUFFER_STRCMP(a, b, ignore_case) ((a).len == (b).len && (DqnStr_Cmp((char *)((a).str), (char *)((b).str), (a).len, ignore_case) == 0)) #define DQN_BUFFER_MEMCMP(a, b) ((a).len == (b).len && (DqnMem_Cmp((void *)((a).str), (void *)((b).str), (a).len) == 0)) -#define DQN_BUFFER_STR_LIT_STRCMP(a, b, ignore_case) ((a).len == (b).len && (DqnStr_Cmp((char *)((a).str), (char *)((b).str), (a).len, ignore_case) == 0)) -#define DQN_BUFFER_STR_LIT_MEMCMP(a, b) ((a).len == (b).len && (DqnMem_Cmp((void *)((a).str), (void *)((b).str), (a).len) == 0)) +#define DQN_SLICE_STRCMP(a, b, ignore_case) ((a).len == (b).len && (DqnStr_Cmp((char *)((a).str), (char *)((b).str), (a).len, ignore_case) == 0)) +#define DQN_SLICE_MEMCMP(a, b) ((a).len == (b).len && (DqnMem_Cmp((void *)((a).str), (void *)((b).str), (a).len) == 0)) // #DqnChar API // ================================================================================================= DQN_FILE_SCOPE char DqnChar_ToLower (char c); @@ -902,8 +903,9 @@ i32 Dqn_SplitString(char const *src, i32 src_len, char split_char, DqnSlice void DqnArray::Reserve(isize new_max) // #DqnMemTracker // ================================================================================================= // Allocation Layout -// +--------------------+-------------------------------------------------------------------------+-----------------------------+-----------------+ -// | Ptr From Allocator | Offset To Src | Alignment | Alloc Type | Alloc Amount | B. Guard (Opt.) | Aligned Ptr For Client | B. Guard (Opt.) | -// +--------------------+-------------------------------------------------------------------------+-----------------------------+-----------------+ +// +--------------------+-------------------------------------------------------------------------+------------------------+-----------------+ +// | Ptr From Allocator | Offset To Src | Alignment | Alloc Type | Alloc Amount | B. Guard (Opt.) | Aligned Ptr For Client | B. Guard (Opt.) | +// +--------------------+-------------------------------------------------------------------------+------------------------+-----------------+ // Ptr From Allocator: The pointer returned by the allocator, not aligned +// Offset To Src: Number of bytes to subtract from the "Aligned Ptr For Client" to return to the "Ptr From Allocator" // Alignment: The pointer given to the client is aligned to this power of two boundary // Alloc Type: If the allocation was allocated from the head or tail of the memory block (mainly for memstacks). // Alloc Amount: The requested allocation amount by the client (so does not include metadata) // B. Guard: Bounds Guard value. // Aligned Ptr For Client: The allocated memory for the client. -// Offset To Src: Number of bytes to subtract from the "Aligned Ptr For Client" to return to the "Ptr From Allocator" #pragma pack(push, 1) struct DqnPtrHeader @@ -1428,7 +1430,8 @@ struct DqnMemStack NonExpandableAssert = (1 << 1), // Assert when non-expandable is set and we run out of space BoundsGuard = (1 << 2), // Track, check and add 4 byte guards on the boundaries of all allocations PushAssertsOnFail = (1 << 3), // Assert when push() fails. - All = (NonExpandable | NonExpandableAssert | BoundsGuard | PushAssertsOnFail), + DefaultAllocateTail = (1 << 4), // If set, allocate to tail when push_type is unspecified, otherwise allocate to head + DefaultFlags = (NonExpandable | NonExpandableAssert | BoundsGuard | PushAssertsOnFail), }; struct Info // Statistics of the memory stack. @@ -1441,16 +1444,15 @@ struct DqnMemStack struct Block { - char *memory; // Read - isize size; // Read - isize used_; // Read - Block *prev_block; // Read Uses a linked list approach for additional blocks + char *memory; + isize size; + char *head; + char *tail; + Block *prev_block; - char *head; // Read - char *tail; // Read - - Block() = default; - Block(void *memory_, isize size_) { *this = {}; memory = (char *)memory_; size = size_; head = memory; tail = memory + size; } + Block() = default; + Block(void *memory_, isize size_) { *this = {}; memory = (char *)memory_; size = size_; head = memory; tail = memory + size; } + isize Usage() const { return size - (tail - head); } }; DqnMemTracker tracker; // Read: Metadata for managing ptr allocation @@ -1470,12 +1472,13 @@ struct DqnMemStack // Allocation API // ============================================================================================= - enum struct AllocTo { Head, Tail }; + enum struct PushType { Default, Head, Tail }; // Allocate memory from the MemStack. // 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. - void *Push (isize size, AllocTo allocTo = AllocTo::Head, u8 alignment = 4); + void *Push (isize size, PushType push_type = PushType::Default, u8 alignment = 4); + void SetDefaultAllocate(PushType type) { if (type == PushType::Default) return; if (type == PushType::Head) flags |= Flag::DefaultAllocateTail; else flags &= ~Flag::DefaultAllocateTail; } // Frees the given ptr. It MUST be the last allocated item in the stack, asserts otherwise. void Pop (void *ptr, Dqn::ZeroClear clear = Dqn::ZeroClear::No); @@ -1532,10 +1535,10 @@ struct DqnMemStack void FreeHead (void *ptr) { (void)ptr; return; } void *ReallocHead(void *ptr, size_t size) { DqnPtrHeader *header = tracker.PtrToHeader((char *)ptr); void *result = Push(size); DqnMem_Copy(result, ptr, header->alloc_amount); return result; } - void *MallocTail (size_t size) { return Push(size, AllocTo::Tail); } - void *CallocTail (size_t size) { void *result = Push(size, AllocTo::Tail); DqnMem_Clear(result, 0, size); } + void *MallocTail (size_t size) { return Push(size, PushType::Tail); } + void *CallocTail (size_t size) { void *result = Push(size, PushType::Tail); DqnMem_Clear(result, 0, size); } void FreeTail (void *ptr) { (void)ptr; return; } - void *ReallocTail(void *ptr, size_t size) { DqnPtrHeader *header = tracker.PtrToHeader((char *)ptr); void *result = Push(size, AllocTo::Tail); DqnMem_Copy(result, ptr, header->alloc_amount); return result; } + void *ReallocTail(void *ptr, size_t size) { DqnPtrHeader *header = tracker.PtrToHeader((char *)ptr); void *result = Push(size, PushType::Tail); DqnMem_Copy(result, ptr, header->alloc_amount); return result; } }; // #DqnHash API @@ -2407,8 +2410,8 @@ DQN_FILE_SCOPE bool DqnFile_ReadAll(wchar_t const *path, u8 *buf, usize buf_si // return: False if file access failure OR nullptr arguments. DQN_FILE_SCOPE u8 *DqnFile_ReadAll(char const *path, usize *buf_size, DqnAllocator *allocator = DQN_DEFAULT_ALLOCATOR); DQN_FILE_SCOPE u8 *DqnFile_ReadAll(wchar_t const *path, usize *buf_size, DqnAllocator *allocator = DQN_DEFAULT_ALLOCATOR); -DQN_FILE_SCOPE u8 *DqnFile_ReadAll(wchar_t const *path, usize *buf_size, DqnMemStack *stack, DqnMemStack::AllocTo allocTo = DqnMemStack::AllocTo::Head); -DQN_FILE_SCOPE u8 *DqnFile_ReadAll(char const *path, usize *buf_size, DqnMemStack *stack, DqnMemStack::AllocTo allocTo = DqnMemStack::AllocTo::Head); +DQN_FILE_SCOPE u8 *DqnFile_ReadAll(wchar_t const *path, usize *buf_size, DqnMemStack *stack, DqnMemStack::PushType push_type = DqnMemStack::PushType::Head); +DQN_FILE_SCOPE u8 *DqnFile_ReadAll(char const *path, usize *buf_size, DqnMemStack *stack, DqnMemStack::PushType push_type = DqnMemStack::PushType::Head); DQN_FILE_SCOPE bool DqnFile_WriteAll(char const *path, u8 const *buf, usize const buf_size); DQN_FILE_SCOPE bool DqnFile_WriteAll(wchar_t const *path, u8 const *buf, usize const buf_size); @@ -2989,19 +2992,29 @@ void DqnMemStack::LazyInit(isize size, Dqn::ZeroClear clear, u32 flags_, DqnAllo this->tracker.Init(bounds_guard); } -void *DqnMemStack::Push(isize size, AllocTo allocTo, u8 alignment) +void *DqnMemStack::Push(isize size, PushType push_type, u8 alignment) { DQN_ASSERT(size >= 0 && (alignment % 2 == 0)); - DQN_ALWAYS_ASSERTM(alignment <= 128, "Alignment supported. Update metadata to use u16 for storing the offset!"); + DQN_ALWAYS_ASSERTM(alignment <= 128, "Alignment _not_ supported. Update metadata to use u16 for storing the offset!"); if (size == 0) return nullptr; if (!this->block) - LazyInit(MINIMUM_BLOCK_SIZE, Dqn::ZeroClear::Yes, Flag::All, DQN_DEFAULT_ALLOCATOR); + LazyInit(MINIMUM_BLOCK_SIZE, Dqn::ZeroClear::Yes, Flag::DefaultFlags, DQN_DEFAULT_ALLOCATOR); + + isize size_to_alloc = this->tracker.GetAllocationSize(size, alignment); + bool push_to_head = true; + if (push_type == PushType::Default) + { + if (this->flags & Flag::DefaultAllocateTail) push_to_head = false; + else push_to_head = true; + } + else + { + push_to_head = (push_type == PushType::Head); + } - bool const push_to_head = (allocTo == AllocTo::Head); - isize size_to_alloc = this->tracker.GetAllocationSize(size, alignment); // Allocate New Block If Full // ============================================================================================= @@ -3045,15 +3058,14 @@ void *DqnMemStack::Push(isize size, AllocTo allocTo, u8 alignment) DQN_ASSERT(this->block->tail >= this->block->head); } - this->block->used_ = this->block->size - (this->block->tail - this->block->head); // Instrument allocation with guards and tracker // ============================================================================================= { - auto *ptr_header = reinterpret_cast(src_ptr + offset_to_ptr_header); + auto *ptr_header = reinterpret_cast(src_ptr + offset_to_ptr_header); ptr_header->offset_to_src_ptr = static_cast(aligned_result - src_ptr); - ptr_header->alignment = alignment; - ptr_header->alloc_type = (push_to_head) ? 0 : 1; - ptr_header->alloc_amount = size; + ptr_header->alignment = alignment; + ptr_header->alloc_type = (push_to_head) ? 0 : 1; + ptr_header->alloc_amount = size; if (Dqn_BitIsSet(this->flags, DqnMemStack::Flag::BoundsGuard)) { @@ -4240,13 +4252,13 @@ DQN_FILE_SCOPE char *Dqn_EatLine(char **input, int *line_len) { char *ptr_to_rune = (*input - rune_len); *ptr_to_rune = 0; - *line_len = static_cast(ptr_to_rune - result); + if (line_len) *line_len = static_cast(ptr_to_rune - result); return result; } } else if (rune_len == 0) { - *line_len = static_cast(*input - result); + if (line_len) *line_len = static_cast(*input - result); *input = nullptr; return result; } @@ -4262,7 +4274,7 @@ DQN_FILE_SCOPE wchar_t *Dqn_EatLine(wchar_t **input, int *line_len) { if (!(*input)) { - *line_len = static_cast(*input - result); + if (line_len) *line_len = static_cast(*input - result); return result; } @@ -4270,7 +4282,7 @@ DQN_FILE_SCOPE wchar_t *Dqn_EatLine(wchar_t **input, int *line_len) if (ch == '\r' || ch == '\n') { (*input)[0] = 0; - *line_len = static_cast(*input - result); + if (line_len) *line_len = static_cast(*input - result); (*input)++; return result; } @@ -7760,7 +7772,7 @@ DQN_FILE_SCOPE u8 *DqnFile_ReadAll(char const *path, usize *buf_size, DqnAllocat return nullptr; } -DQN_FILE_SCOPE u8 *DqnFile_ReadAll(wchar_t const *path, usize *buf_size, DqnMemStack *stack, DqnMemStack::AllocTo allocTo) +DQN_FILE_SCOPE u8 *DqnFile_ReadAll(wchar_t const *path, usize *buf_size, DqnMemStack *stack, DqnMemStack::PushType push_type) { u8 *result = nullptr; DqnFile file = {}; @@ -7771,7 +7783,7 @@ DQN_FILE_SCOPE u8 *DqnFile_ReadAll(wchar_t const *path, usize *buf_size, DqnMemS } DQN_DEFER(file.Close()); - result = static_cast(stack->Push(file.size, allocTo)); + result = static_cast(stack->Push(file.size, push_type)); usize bytes_read = file.Read(result, file.size); if (bytes_read == file.size) { @@ -7785,7 +7797,7 @@ DQN_FILE_SCOPE u8 *DqnFile_ReadAll(wchar_t const *path, usize *buf_size, DqnMemS return result; } -DQN_FILE_SCOPE u8 *DqnFile_ReadAll(char const *path, usize *buf_size, DqnMemStack *stack, DqnMemStack::AllocTo allocTo) +DQN_FILE_SCOPE u8 *DqnFile_ReadAll(char const *path, usize *buf_size, DqnMemStack *stack, DqnMemStack::PushType push_type) { u8 *result = nullptr; DqnFile file = {}; @@ -7796,7 +7808,7 @@ DQN_FILE_SCOPE u8 *DqnFile_ReadAll(char const *path, usize *buf_size, DqnMemStac } DQN_DEFER(file.Close()); - result = static_cast(stack->Push(file.size, allocTo)); + result = static_cast(stack->Push(file.size, push_type)); usize bytes_read = file.Read(result, file.size); if (bytes_read == file.size) {