From 1c5fdc6ce1dda5c4e1a822baf86476a9e6c767f6 Mon Sep 17 00:00:00 2001 From: Doyle T Date: Sun, 15 Jul 2018 20:09:55 +1000 Subject: [PATCH] Update quicksort/bsearch to use templates better --- DqnUnitTest.cpp | 93 ++++++------ dqn.h | 377 ++++++++++++------------------------------------ 2 files changed, 137 insertions(+), 333 deletions(-) diff --git a/DqnUnitTest.cpp b/DqnUnitTest.cpp index 7386f93..0100d64 100644 --- a/DqnUnitTest.cpp +++ b/DqnUnitTest.cpp @@ -1882,7 +1882,7 @@ FILE_SCOPE void JobQueueDebugCallbackIncrementCounter(DqnJobQueue *const queue, (void)userData; DQN_ASSERT(queue->size == QUEUE_SIZE); { - DqnLockGuard guard = globalJobQueueLock.LockGuard(); + auto guard = globalJobQueueLock.Guard(); globalDebugCounter++; // u32 number = globalDebugCounter; @@ -1915,7 +1915,7 @@ FILE_SCOPE void DqnJobQueue_Test() DQN_ASSERT(DqnJobQueue_Init(&jobQueue, jobList, QUEUE_SIZE, totalThreads)); const u32 WORK_ENTRIES = 2048; - DQN_ASSERT(DqnLock_Init(&globalJobQueueLock)); + DQN_ASSERT(globalJobQueueLock.Init()); for (u32 i = 0; i < WORK_ENTRIES; i++) { DqnJob job = {}; @@ -1928,7 +1928,7 @@ FILE_SCOPE void DqnJobQueue_Test() DqnJobQueue_BlockAndCompleteAllJobs(&jobQueue); DQN_ASSERT(globalDebugCounter == WORK_ENTRIES); - DqnLock_Delete(&globalJobQueueLock); + globalJobQueueLock.Delete(); Log("Final incremented value: %d\n", globalDebugCounter); } @@ -1977,7 +1977,7 @@ void DqnQuickSort_Test() // Time Dqn_QuickSort { f64 start = DqnTimer_NowInS(); - Dqn_QuickSort(dqnCPPArray, numInts); + DqnQuickSort(dqnCPPArray, numInts); f64 duration = DqnTimer_NowInS() - start; dqnCPPTimings[timingsIndex] = duration; @@ -2102,33 +2102,22 @@ void DqnHashTable_Test() Log(Status::Ok, "HashTable"); } -void Dqn_BSearch_Test() +void DqnBSearch_Test() { LOG_HEADER(); if (1) { - auto IsLessThan = [](const u32 &a, const u32 &b) -> bool { - bool result = a < b; - return result; - }; - - auto Equals = [](const u32 &a, const u32 &b) -> bool { - bool result = (a == b); - return result; - }; - - u32 array[] = {1, 2, 3}; - i64 result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 1, Equals, IsLessThan); + i64 result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 1); DQN_ASSERT(result == 0); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 2, Equals, IsLessThan); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 2); DQN_ASSERT(result == 1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 3, Equals, IsLessThan); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 3); DQN_ASSERT(result == 2); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 4, Equals, IsLessThan); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 4); DQN_ASSERT(result == -1); Log(Status::Ok, "With odd sized array and custom compare"); } @@ -2136,19 +2125,19 @@ void Dqn_BSearch_Test() if (1) { i64 array[] = {1, 2, 3, 4}; - i64 result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 1); + i64 result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 1); DQN_ASSERT(result == 0); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 2); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 2); DQN_ASSERT(result == 1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 3); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 3); DQN_ASSERT(result == 2); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 4); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 4); DQN_ASSERT(result == 3); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 5); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 5); DQN_ASSERT(result == -1); Log(Status::Ok, "With even sized array"); } @@ -2156,22 +2145,22 @@ void Dqn_BSearch_Test() if (1) { i64 array[] = {1, 2, 3}; - i64 result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 0, Dqn_BSearchBound_Lower); + i64 result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 0, DqnBSearchType::MinusOne); DQN_ASSERT(result == -1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 1, Dqn_BSearchBound_Lower); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 1, DqnBSearchType::MinusOne); DQN_ASSERT(result == -1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 2, Dqn_BSearchBound_Lower); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 2, DqnBSearchType::MinusOne); DQN_ASSERT(result == 0); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 3, Dqn_BSearchBound_Lower); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 3, DqnBSearchType::MinusOne); DQN_ASSERT(result == 1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 4, Dqn_BSearchBound_Lower); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 4, DqnBSearchType::MinusOne); DQN_ASSERT(result == 2); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 5, Dqn_BSearchBound_Lower); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 5, DqnBSearchType::MinusOne); DQN_ASSERT(result == 2); Log(Status::Ok, "Lower bound with odd sized array"); } @@ -2180,25 +2169,25 @@ void Dqn_BSearch_Test() { i64 array[] = {1, 2, 3, 4}; - i64 result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 0, Dqn_BSearchBound_Lower); + i64 result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 0, DqnBSearchType::MinusOne); DQN_ASSERT(result == -1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 1, Dqn_BSearchBound_Lower); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 1, DqnBSearchType::MinusOne); DQN_ASSERT(result == -1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 2, Dqn_BSearchBound_Lower); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 2, DqnBSearchType::MinusOne); DQN_ASSERT(result == 0); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 3, Dqn_BSearchBound_Lower); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 3, DqnBSearchType::MinusOne); DQN_ASSERT(result == 1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 4, Dqn_BSearchBound_Lower); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 4, DqnBSearchType::MinusOne); DQN_ASSERT(result == 2); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 5, Dqn_BSearchBound_Lower); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 5, DqnBSearchType::MinusOne); DQN_ASSERT(result == 3); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 6, Dqn_BSearchBound_Lower); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 6, DqnBSearchType::MinusOne); DQN_ASSERT(result == 3); Log(Status::Ok, "Lower bound with even sized array"); } @@ -2206,22 +2195,22 @@ void Dqn_BSearch_Test() if (1) { i64 array[] = {1, 2, 3}; - i64 result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 0, Dqn_BSearchBound_Higher); + i64 result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 0, DqnBSearchType::PlusOne); DQN_ASSERT(result == 0); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 1, Dqn_BSearchBound_Higher); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 1, DqnBSearchType::PlusOne); DQN_ASSERT(result == 1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 2, Dqn_BSearchBound_Higher); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 2, DqnBSearchType::PlusOne); DQN_ASSERT(result == 2); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 3, Dqn_BSearchBound_Higher); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 3, DqnBSearchType::PlusOne); DQN_ASSERT(result == -1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 4, Dqn_BSearchBound_Higher); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 4, DqnBSearchType::PlusOne); DQN_ASSERT(result == -1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 5, Dqn_BSearchBound_Higher); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 5, DqnBSearchType::PlusOne); DQN_ASSERT(result == -1); Log(Status::Ok, "Higher bound with odd sized array"); } @@ -2230,25 +2219,25 @@ void Dqn_BSearch_Test() { i64 array[] = {1, 2, 3, 4}; - i64 result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 0, Dqn_BSearchBound_Higher); + i64 result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 0, DqnBSearchType::PlusOne); DQN_ASSERT(result == 0); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 1, Dqn_BSearchBound_Higher); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 1, DqnBSearchType::PlusOne); DQN_ASSERT(result == 1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 2, Dqn_BSearchBound_Higher); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 2, DqnBSearchType::PlusOne); DQN_ASSERT(result == 2); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 3, Dqn_BSearchBound_Higher); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 3, DqnBSearchType::PlusOne); DQN_ASSERT(result == 3); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 4, Dqn_BSearchBound_Higher); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 4, DqnBSearchType::PlusOne); DQN_ASSERT(result == -1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 5, Dqn_BSearchBound_Higher); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 5, DqnBSearchType::PlusOne); DQN_ASSERT(result == -1); - result = Dqn_BSearch(array, DQN_ARRAY_COUNT(array), 6, Dqn_BSearchBound_Higher); + result = DqnBSearch(array, DQN_ARRAY_COUNT(array), 6, DqnBSearchType::PlusOne); DQN_ASSERT(result == -1); Log(Status::Ok, "Higher bound with even sized array"); } @@ -2896,7 +2885,7 @@ int main(void) DqnArray_Test(); DqnQuickSort_Test(); DqnHashTable_Test(); - Dqn_BSearch_Test(); + DqnBSearch_Test(); DqnMemSet_Test(); DqnFixedString_Test(); DqnJson_Test(); diff --git a/dqn.h b/dqn.h index 98746fc..df4ea48 100644 --- a/dqn.h +++ b/dqn.h @@ -898,16 +898,25 @@ DQN_FILE_SCOPE inline u32 Dqn_BitSet (u32 bits, u32 flag); DQN_FILE_SCOPE inline u32 Dqn_BitUnset (u32 bits, u32 flag); DQN_FILE_SCOPE inline u32 Dqn_BitToggle(u32 bits, u32 flag); -template -using Dqn_QuickSortLessThanCallback = bool (*)(T const *, T const *, void *); - -DQN_FILE_SCOPE inline bool Dqn_QuickSortDqnStringSorter(struct DqnString const *a, struct DqnString const *b, void *); - -template -DQN_FILE_SCOPE void Dqn_QuickSort(T *array, i64 size, - Dqn_QuickSortLessThanCallback IsLessThan, void *userContext = nullptr) +template using DqnQuickSort_LessThanProc = bool (*) (T const &a, T const &b, void *userContext); +#define DQN_QUICK_SORT_LESS_THAN_PROC(name) template inline bool name(T const &a, T const &b, void *userContext) +DQN_QUICK_SORT_LESS_THAN_PROC(DqnQuickSort_DefaultLessThan) { - if (!array || size <= 1 || !IsLessThan) return; + (void)userContext; + bool result = a < b; + return result; +} + +DQN_QUICK_SORT_LESS_THAN_PROC(DqnQuickSort_StringLessThan) +{ + bool result = DqnString::Cmp(a, b); + return result; +} + +template IsLessThan = DqnQuickSort_DefaultLessThan> +DQN_FILE_SCOPE void DqnQuickSort(T *array, isize size, void *userContext = nullptr) +{ + if (!array || size <= 1) return; #if 1 // Insertion Sort, under 24->32 is an optimal amount @@ -919,7 +928,7 @@ DQN_FILE_SCOPE void Dqn_QuickSort(T *array, i64 size, { for (i32 checkIndex = 0; checkIndex < itemToInsertIndex; checkIndex++) { - if (!IsLessThan(&array[checkIndex], &array[itemToInsertIndex], userContext)) + if (!IsLessThan(array[checkIndex], array[itemToInsertIndex], userContext)) { T itemToInsert = array[itemToInsertIndex]; for (i32 i = itemToInsertIndex; i > checkIndex; i--) @@ -948,7 +957,7 @@ DQN_FILE_SCOPE void Dqn_QuickSort(T *array, i64 size, pivotIndex = lastIndex; // 4^, 8, 7, 5, 2, 3, 6 - if (IsLessThan(&array[startIndex], &array[pivotIndex], userContext)) partitionIndex++; + if (IsLessThan(array[startIndex], array[pivotIndex], userContext)) partitionIndex++; startIndex++; // 4, |8, 7, 5^, 2, 3, 6* @@ -957,7 +966,7 @@ DQN_FILE_SCOPE void Dqn_QuickSort(T *array, i64 size, // 4, 5, 2, 3, |7, 8, ^6* for (auto checkIndex = startIndex; checkIndex < lastIndex; checkIndex++) { - if (IsLessThan(&array[checkIndex], &array[pivotIndex], userContext)) + if (IsLessThan(array[checkIndex], array[pivotIndex], userContext)) { DQN_SWAP(T, array[partitionIndex], array[checkIndex]); partitionIndex++; @@ -967,128 +976,61 @@ DQN_FILE_SCOPE void Dqn_QuickSort(T *array, i64 size, // Move pivot to right of partition // 4, 5, 2, 3, |6, 8, ^7* DQN_SWAP(T, array[partitionIndex], array[pivotIndex]); - Dqn_QuickSort(array, partitionIndex, IsLessThan, userContext); + DqnQuickSort(array, partitionIndex, userContext); // Skip the value at partion index since that is guaranteed to be sorted. // 4, 5, 2, 3, (x), 8, 7 i32 oneAfterPartitionIndex = partitionIndex + 1; - Dqn_QuickSort(array + oneAfterPartitionIndex, (size - oneAfterPartitionIndex), IsLessThan, userContext); + DqnQuickSort(array + oneAfterPartitionIndex, (size - oneAfterPartitionIndex), userContext); } -template -DQN_FILE_SCOPE void Dqn_QuickSort(T *array, i64 size) +template using DqnBSearch_LessThanProc = bool (*)(const T&, const T&); +template using DqnBSearch_EqualsProc = bool (*)(const T&, const T&); +#define DQN_BSEARCH_LESS_THAN_PROC(name) template inline bool name(T const &a, T const &b) +#define DQN_BSEARCH_EQUALS_PROC(name) template inline bool name(T const &a, T const &b) +DQN_BSEARCH_LESS_THAN_PROC(DqnBSearch_DefaultLessThan) { return a < b; } +DQN_BSEARCH_EQUALS_PROC (DqnBSearch_DefaultEquals) { return a == b; } + +enum struct DqnBSearchType { - if (!array || size <= 1) return; + Match, // Return the index of the first item that matches the find value + MinusOne, // Return the index of the first item lower than the find value + PlusOne, // Return the index of the first item higher than the find value -#if 1 - // Insertion Sort, under 24->32 is an optimal amount - const i32 QUICK_SORT_THRESHOLD = 24; - if (size < QUICK_SORT_THRESHOLD) - { - i32 itemToInsertIndex = 1; - while (itemToInsertIndex < size) - { - for (i32 checkIndex = 0; checkIndex < itemToInsertIndex; checkIndex++) - { - if (!(array[checkIndex] < array[itemToInsertIndex])) - { - T itemToInsert = array[itemToInsertIndex]; - for (i32 i = itemToInsertIndex; i > checkIndex; i--) - array[i] = array[i - 1]; - - array[checkIndex] = itemToInsert; - break; - } - } - itemToInsertIndex++; - } - - return; - } -#endif - - auto state = DqnRndPCG(); - auto lastIndex = size - 1; - auto pivotIndex = (i64)state.Range(0, (i32)lastIndex); // TODO(doyle): RNG 64bit - auto partitionIndex = 0; - auto startIndex = 0; - - // Swap pivot with last index, so pivot is always at the end of the array. - // This makes logic much simpler. - DQN_SWAP(T, array[lastIndex], array[pivotIndex]); - pivotIndex = lastIndex; - - // 4^, 8, 7, 5, 2, 3, 6 - if (array[startIndex] < array[pivotIndex]) partitionIndex++; - startIndex++; - - // 4, |8, 7, 5^, 2, 3, 6* - // 4, 5, |7, 8, 2^, 3, 6* - // 4, 5, 2, |8, 7, ^3, 6* - // 4, 5, 2, 3, |7, 8, ^6* - for (auto checkIndex = startIndex; checkIndex < lastIndex; checkIndex++) - { - if (array[checkIndex] < array[pivotIndex]) - { - DQN_SWAP(T, array[partitionIndex], array[checkIndex]); - partitionIndex++; - } - } - - // Move pivot to right of partition - // 4, 5, 2, 3, |6, 8, ^7* - DQN_SWAP(T, array[partitionIndex], array[pivotIndex]); - Dqn_QuickSort(array, partitionIndex); - - // Skip the value at partion index since that is guaranteed to be sorted. - // 4, 5, 2, 3, (x), 8, 7 - i32 oneAfterPartitionIndex = partitionIndex + 1; - Dqn_QuickSort(array + oneAfterPartitionIndex, (size - oneAfterPartitionIndex)); -} - -template -using Dqn_BSearchLessThanCallback = bool (*)(const T&, const T&); - -template -using Dqn_BSearchEqualsCallback = bool (*)(const T&, const T&); - -enum Dqn_BSearchBound -{ - Dqn_BSearchBound_Normal, // Return the index of the first item that matches the find value - Dqn_BSearchBound_Lower, // Return the index of the first item lower than the find value - Dqn_BSearchBound_Higher, // Return the index of the first item higher than the find value - - Dqn_BSearchBound_NormalLower, // Return the index of the matching item if not found the first item lower - Dqn_BSearchBound_NormalHigher, // Return the index of the matching item if not found the first item higher + MatchOrMinusOne, // Return the index of the matching item if not found the first item lower + MatchOrPlusOne, // Return the index of the matching item if not found the first item higher }; -// bound: The behaviour of the binary search, +// type: The matching behaviour of the binary search, // return: -1 if element not found, otherwise index of the element. // For higher and lower bounds return -1 if there is no element higher/lower than the // find value (i.e. -1 if the 0th element is the find val for lower bound). -template -DQN_FILE_SCOPE i64 Dqn_BSearch(T *array, i64 size, T const &find, - Dqn_BSearchEqualsCallback Equals, - Dqn_BSearchLessThanCallback IsLessThan, - Dqn_BSearchBound bound = Dqn_BSearchBound_Normal) +template IsLessThan = DqnBSearch_DefaultLessThan, + DqnBSearch_EqualsProc Equals = DqnBSearch_DefaultEquals> +DQN_FILE_SCOPE i64 +DqnBSearch(T const *array, isize size, T const &find, DqnBSearchType type = DqnBSearchType::Match) { - if (size == 0 || !array) return -1; + if (size == 0 || !array) + { + return -1; + } - i64 start = 0; - i64 end = size - 1; - i64 mid = (i64)((start + end) * 0.5f); + isize start = 0; + isize end = size - 1; + isize mid = static_cast((start + end) * 0.5f); while (start <= end) { if (Equals(array[mid], find)) { - if (bound == Dqn_BSearchBound_Normal || - bound == Dqn_BSearchBound_NormalLower || - bound == Dqn_BSearchBound_NormalHigher) + if (type == DqnBSearchType::Match || + type == DqnBSearchType::MatchOrMinusOne || + type == DqnBSearchType::MatchOrPlusOne) { return mid; } - else if (bound == Dqn_BSearchBound_Lower) + else if (type == DqnBSearchType::MinusOne) { // NOTE: We can always -1 because at worst case, 0 index will go to -1 which is // correct behaviour. @@ -1096,32 +1038,29 @@ DQN_FILE_SCOPE i64 Dqn_BSearch(T *array, i64 size, T const &find, } else { - if ((mid + 1) >= size) return -1; - return mid + 1; + return ((mid + 1) >= size) ? -1 : mid + 1; } } else if (IsLessThan(array[mid], find)) start = mid + 1; else end = mid - 1; - mid = (i64)((start + end) * 0.5f); + mid = static_cast((start + end) * 0.5f); } - if (bound == Dqn_BSearchBound_Normal) + if (type == DqnBSearchType::Match) { return -1; } - if (bound == Dqn_BSearchBound_Lower || bound == Dqn_BSearchBound_NormalLower) + if (type == DqnBSearchType::MinusOne || type == DqnBSearchType::MatchOrMinusOne) { - if (IsLessThan(find, array[mid])) return -1; - return mid; + return (IsLessThan(find, array[mid])) ? -1 : mid; } else { - if (IsLessThan(array[mid], find)) return -1; - return mid; + return (IsLessThan(find, array[mid])) ? mid : -1; } } -DQN_FILE_SCOPE i64 Dqn_BSearch(i64 *array, i64 size, i64 find, Dqn_BSearchBound bound = Dqn_BSearchBound_Normal); +DQN_FILE_SCOPE inline i64 DqnBSearch(i64 const *array, i64 size, i64 find, DqnBSearchType type = DqnBSearchType::Match) { return DqnBSearch(array, size, find, type); } // #DqnMem API // ================================================================================================= @@ -1325,7 +1264,7 @@ struct DqnArray ~DqnArray () { if (this->data && this->memAPI) this->memAPI->Free(data); } void UseMemory (T *data_, isize max_, isize count_ = 0) { this->memAPI = nullptr; this->data = data_; this->max = max_; this->count = count_; } - void Clear (Dqn::ZeroClear clear = Dqn::ZeroClear::No) { if (data) { count = 0; if (clear == Dqn::ZeroClear::Yes) DqnMem_Clear(data, 0, sizeof(T) * max); } } + void Clear (Dqn::ZeroClear clear = Dqn::ZeroClear::No) { if (!data) return; count = 0; if (clear == Dqn::ZeroClear::Yes) DqnMem_Clear(data, 0, sizeof(T) * max); } void Free () { if (data) { memAPI->Free(data); } *this = {}; } T *Front () { DQN_ASSERT(count > 0); return data + 0; } T *Back () { DQN_ASSERT(count > 0); return data + (count - 1); } @@ -1577,24 +1516,6 @@ struct DqnMemStack void TempRegionKeepChanges(TempRegion region); }; -inline bool DqnMemStack_Init (DqnMemStack *me, isize size, Dqn::ZeroClear clear, u32 flags_ = 0, DqnMemAPI *api = DQN_DEFAULT_HEAP_ALLOCATOR) { return me->Init(size, clear, flags_, api); } -inline void *DqnMemStack_Push (DqnMemStack *me, isize size, u8 alignment = 4) { return me->Push(size, alignment); } -inline void *DqnMemStack_PushOnTail (DqnMemStack *me, isize size, u8 alignment = 4) { return me->PushOnTail(size, alignment); } -inline void DqnMemStack_Pop (DqnMemStack *me, void *ptr, Dqn::ZeroClear clear = Dqn::ZeroClear::No) { me->Pop(ptr, clear); } -inline void DqnMemStack_PopOnTail (DqnMemStack *me, void *ptr, Dqn::ZeroClear clear = Dqn::ZeroClear::No) { me->PopOnTail(ptr, clear); } -inline void DqnMemStack_Free (DqnMemStack *me) { me->Free(); } -inline bool DqnMemStack_FreeMemBlock (DqnMemStack *me, DqnMemStack::Block *memBlock) { return me->FreeMemBlock(memBlock); } -inline bool DqnMemStack_FreeLastBlock (DqnMemStack *me) { return me->FreeLastBlock(); } -inline void DqnMemStack_Reset (DqnMemStack *me) { me->Reset(); } -inline void DqnMemStack_ResetTail (DqnMemStack *me) { me->ResetTail(); } -inline void DqnMemStack_ClearCurrBlock (DqnMemStack *me, Dqn::ZeroClear clear) { me->ClearCurrBlock(clear); } -inline DqnMemStack::Info DqnMemStack_GetInfo (DqnMemStack *me) { return me->GetInfo(); } -inline DqnMemStack::TempRegion DqnMemStack_TempRegionBegin (DqnMemStack *me) { return me->TempRegionBegin(); } -inline void DqnMemStack_TempRegionEnd (DqnMemStack *me, DqnMemStack::TempRegion region) { me->TempRegionEnd(region); } -inline DqnMemStack::TempRegionGuard_ DqnMemStack_TempRegionGuard (DqnMemStack *me) { return me->TempRegionGuard(); } -inline void DqnMemStack_TempRegionKeepChanges(DqnMemStack *me, DqnMemStack::TempRegion region) { me->TempRegionKeepChanges(region); } - - // Implementation taken from Milton, developed by Serge at // https://github.com/serge-rgb/milton#license #if 0 @@ -1836,14 +1757,14 @@ void DqnArray::EraseStable(isize *indexList, isize numIndexes) if (numIndexes == 0 || !indexList) return; // NOTE: Sort the index list and ensure we only remove indexes up to the size of our array - Dqn_QuickSort(indexList, numIndexes); + DqnQuickSort(indexList, numIndexes); auto arrayHighestIndex = this->count - 1; auto realCount = numIndexes; if (indexList[numIndexes - 1] > arrayHighestIndex) { auto realNumIndexes = - Dqn_BSearch(indexList, numIndexes, arrayHighestIndex, Dqn_BSearchBound_Lower); + DqnBSearch(indexList, numIndexes, arrayHighestIndex, DqnBSearchType::MinusOne); // NOTE: If -1, then there's no index in the indexlist that is within the range of our array // i.e. no index we can remove without out of array bounds access if (realNumIndexes == -1) @@ -2555,8 +2476,8 @@ typename DqnHashTable::Entry *DqnHashTable::Make(char const *const key, i3 // If entry for hashIndex not used yet, mark it down as a used slot. if (!this->entries[hashIndex]) { - i64 index = Dqn_BSearch(this->usedEntries, this->usedEntriesIndex, hashIndex, - Dqn_BSearchBound_Lower); + i64 index = DqnBSearch(this->usedEntries, this->usedEntriesIndex, hashIndex, + DqnBSearchType::MinusOne); i64 indexToEndAt = index; if (index == -1) indexToEndAt = 0; @@ -2615,8 +2536,8 @@ void DqnHashTable::Remove(char const *const key, i32 keyLen) if (entryToFree == entry) { // Unique entry, so remove this index from the used list as well. - i64 indexToRemove = Dqn_BSearch(this->usedEntries, this->usedEntriesIndex, - hashIndex, Dqn_BSearchBound_Lower); + i64 indexToRemove = DqnBSearch(this->usedEntries, this->usedEntriesIndex, + hashIndex, DqnBSearchType::MinusOne); for (i64 i = indexToRemove; i < this->usedEntriesIndex - 1; i++) this->usedEntries[i] = this->usedEntries[i + 1]; @@ -2899,13 +2820,13 @@ struct DqnVArray DqnVArray () = default; DqnVArray (isize size) { LazyInit(size); } void LazyInit (isize size) { if (data) return; count = 0; max = size; data = (T *)DqnOS_VAlloc(max * sizeof(T)); DQN_ALWAYS_ASSERT(data); } - ~DqnVArray () { if (data) DqnOS_VFree(data, sizeof(T) * max); } + // ~DqnVArray () { if (data) DqnOS_VFree(data, sizeof(T) * max); } void Clear (Dqn::ZeroClear clear = Dqn::ZeroClear::No) { if (data) { count = 0; if (clear == Dqn::ZeroClear::Yes) DqnMem_Clear(data, 0, sizeof(T) * max); } } void Free () { if (data) { DqnOS_VFree(data, sizeof(T) * max); } *this = {}; } T *Front () { return (count > 0) (data + 0) : nullptr; } T *Back () { return (count > 0) (data + (count - 1)) : nullptr; } - T *Make (isize num = 1) { LazyInit(1024); count += num; DQN_ASSERT(count < max); return &data[count - num]; } + T *Make (isize num = 1) { LazyInit(1024); count += num; DQN_ASSERT(count <= max); return &data[count - num]; } T *Push (T const &v) { return Insert(count, &v, 1); } T *Push (T const *v, isize numItems = 1) { return Insert(count, v, numItems); } void Pop () { if (count > 0) count--; } @@ -2927,7 +2848,7 @@ template T *DqnVArray::Insert(isize index, T const *v, isize numI index = DQN_CLAMP(index, 0, count); isize const newCount = count + numItems; - DQN_ASSERT(newCount < max); + DQN_ASSERT(newCount <= max); T *src = data + index; T *dest = src + numItems; @@ -3193,7 +3114,7 @@ DQN_FILE_SCOPE f64 DqnTimer_NowInS (); // XPlatform > #DqnLock API // ================================================================================================= -typedef struct DqnLock +struct DqnLock { #if defined(DQN_IS_WIN32) CRITICAL_SECTION win32Handle; @@ -3210,32 +3131,19 @@ typedef struct DqnLock void Release(); void Delete (); + struct Guard_ + { + Guard_(DqnLock *lock_) : lock(lock_) { lock->Acquire(); } + ~Guard_() { lock->Release(); } + + private: + DqnLock *lock; + }; + // Create a lock guard on the lock this is invoked on. - struct DqnLockGuard LockGuard(); -} DqnLock; - -// Lock guard automatically acquires a lock on construction and releases the associated lock on -// destruction. If the lock is unable to be acquired, the program blocks at construction until it -// can. -struct DqnLockGuard -{ - // lock: Takes a pointer to a pre-existing and already initialised lock - // bool: Pass in (optionally) a pointer to a bool which returns whether a lock was successful. - // FALSE if lock is nullptr. - DqnLockGuard(DqnLock *const lock_, bool *const succeeded); - ~DqnLockGuard(); - -private: - DqnLock *lock; + Guard_ Guard() { return Guard_(this); } }; -// lock: Pass in a pointer to a default DqnLock struct. -// In Win32, you may optionally change the win32Spincount. -DQN_FILE_SCOPE bool DqnLock_Init (DqnLock *const lock); -DQN_FILE_SCOPE void DqnLock_Acquire(DqnLock *const lock); -DQN_FILE_SCOPE void DqnLock_Release(DqnLock *const lock); -DQN_FILE_SCOPE void DqnLock_Delete (DqnLock *const lock); - // XPlatform > #DqnJobQueue API // ================================================================================================= // DqnJobQueue is a platform abstracted "lockless" multithreaded work queue. It will create threads @@ -6449,13 +6357,6 @@ DQN_FILE_SCOPE inline u32 Dqn_BitToggle(u32 bits, u32 flag) return result; } - -DQN_FILE_SCOPE inline bool Dqn_QuickSortDqnStringSorter(struct DqnString const *a, struct DqnString const *b, void *) -{ - bool result = DqnString::Cmp(a, b); - return result; -} - // #DqnString Impleemntation // ================================================================================================= // TODO(doyle): SSO requires handling assign/copy op when copying strings, we need to reassign the @@ -6924,56 +6825,6 @@ i32 Dqn_SplitString(char const *src, i32 srcLen, char splitChar, DqnSlice return arrayIndex; } -DQN_FILE_SCOPE i64 Dqn_BSearch(i64 *array, i64 size, i64 find, - Dqn_BSearchBound bound) -{ - if (size == 0 || !array) return -1; - - i64 start = 0; - i64 end = size - 1; - i64 mid = (i64)((start + end) * 0.5f); - - while (start <= end) - { - if (array[mid] == find) - { - if (bound == Dqn_BSearchBound_Normal || - bound == Dqn_BSearchBound_NormalLower || - bound == Dqn_BSearchBound_NormalHigher) - { - return mid; - } - else if (bound == Dqn_BSearchBound_Lower) - { - return mid - 1; - } - else - { - if ((mid + 1) >= size) return -1; - return mid + 1; - } - } - else if (array[mid] < find) start = mid + 1; - else end = mid - 1; - mid = (i64)((start + end) * 0.5f); - } - - if (bound == Dqn_BSearchBound_Normal) - { - return -1; - } - if (bound == Dqn_BSearchBound_Lower || bound == Dqn_BSearchBound_NormalLower) - { - if (find < array[mid]) return -1; - return mid; - } - else - { - if (find > array[mid]) return -1; - return mid; - } -} - // TODO(doyle): This should maybe be a tokenizer ... DQN_FILE_SCOPE DqnJson DqnJson_Get(char const *buf, i32 bufLen, char const *findProperty, i32 findPropertyLen) { @@ -9609,16 +9460,17 @@ DQN_FILE_SCOPE f64 DqnTimer_NowInS() { return DqnTimer_NowInMs() / 1000.0f; } // XPlatform > #DqnLock // ================================================================================================= -bool DqnLock_Init(DqnLock *lock) +bool DqnLock::Init() { - if (!lock) return false; - #if defined(DQN_IS_WIN32) - if (InitializeCriticalSectionEx(&lock->win32Handle, lock->win32SpinCount, 0)) + if (InitializeCriticalSectionEx(&this->win32Handle, this->win32SpinCount, 0)) + { return true; + } + #else // NOTE: Static initialise, pre-empt a lock so that it gets initialised as per documentation - lock->unixHandle = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; + this->unixHandle = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; DqnLock_Acquire(lock); DqnLock_Release(lock); return true; @@ -9627,75 +9479,38 @@ bool DqnLock_Init(DqnLock *lock) return false; } -void DqnLock_Acquire(DqnLock *lock) +void DqnLock::Acquire() { - if (!lock) return; - #if defined(DQN_IS_WIN32) - EnterCriticalSection(&lock->win32Handle); - + EnterCriticalSection(&this->win32Handle); #else // TODO(doyle): Better error handling - i32 error = pthread_mutex_lock(&lock->unixHandle); + i32 error = pthread_mutex_lock(&this->unixHandle); DQN_ASSERT(error == 0); #endif } -void DqnLock_Release(DqnLock *lock) +void DqnLock::Release() { - if (!lock) return; - #if defined(DQN_IS_WIN32) - LeaveCriticalSection(&lock->win32Handle); - + LeaveCriticalSection(&this->win32Handle); #else // TODO(doyle): better error handling - i32 error = pthread_mutex_unlock(&lock->unixHandle); + i32 error = pthread_mutex_unlock(&this->unixHandle); DQN_ASSERT(error == 0); #endif } -void DqnLock_Delete(DqnLock *lock) +void DqnLock::Delete() { - if (!lock) return; - #if defined(DQN_IS_WIN32) - DeleteCriticalSection(&lock->win32Handle); + DeleteCriticalSection(&this->win32Handle); #else - i32 error = pthread_mutex_destroy(&lock->unixHandle); + i32 error = pthread_mutex_destroy(&this->unixHandle); DQN_ASSERT(error == 0); #endif } -bool DqnLock::Init() { return DqnLock_Init (this); } -void DqnLock::Acquire() { DqnLock_Acquire(this); } -void DqnLock::Release() { DqnLock_Release(this); } -void DqnLock::Delete() { DqnLock_Delete (this); } - -DqnLockGuard DqnLock::LockGuard() -{ - return DqnLockGuard(this, nullptr); -} - -DqnLockGuard::DqnLockGuard(DqnLock *lock_, bool *succeeded) -{ - if (lock_) - { - this->lock = lock_; - this->lock->Acquire(); - if (succeeded) *succeeded = true; - } - else - { - if (succeeded) *succeeded = false; - } -} - -DqnLockGuard::~DqnLockGuard() -{ - if (this->lock) this->lock->Release(); -} - // XPlatform > #DqnJobQueue // ================================================================================================= typedef void *DqnThreadCallbackInternal(void *threadParam);