From cbf7b4d6065c7f7e49c1f6dfcf4a43bc213db193 Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Wed, 28 Jun 2017 22:47:27 +1000 Subject: [PATCH] Add quicksort generic to lib --- build.bat | 9 +-- dqn.h | 189 +++++++++++++++++++++++++++++++++++++++++++--- dqn_unit_test.cpp | 155 ++++++++++++++++++++++++++++++++++++- 3 files changed, 335 insertions(+), 18 deletions(-) diff --git a/build.bat b/build.bat index 6c100dd..4b69675 100644 --- a/build.bat +++ b/build.bat @@ -17,7 +17,7 @@ REM Drop compilation files into build folder IF NOT EXIST bin mkdir bin pushd bin -REM EHa- disable exception handling (we don't use) +REM EHa- disable exception handling (but we use for algorithms so /EHsc) REM GR- disable c runtime type information (we don't use) REM MD use dynamic runtime library @@ -30,11 +30,8 @@ REM Zi enables debug data, Z7 combines the debug files into one. REM W4 warning level 4 REM WX treat warnings as errors -REM wd4100 ignore: unused argument parameters REM wd4201 ignore: nonstandard extension used: nameless struct/union -REM wd4189 ignore: local variable is initialised but not referenced - -set CompileFlags=-EHa- -GR- -Oi -MT -Z7 -W4 -WX -wd4100 -wd4201 -wd4189 -FC -Od +set CompileFlags=-EHsc -GR- -Oi -MT -Z7 -W4 -WX -wd4201 -FC -O2 REM Include directories set IncludeFlags= @@ -44,7 +41,7 @@ set LinkLibraries=user32.lib gdi32.lib msimg32.lib REM incrmenetal:no, turn incremental builds off REM opt:ref, try to remove functions from libs that are referenced at all -set LinkFlags=-incremental:no -opt:ref +set LinkFlags=-incremental:no -opt:ref -machine:x64 cl %CompileFlags% %CompileEntryPoint% %IncludeFlags% /link %LinkLibraries% %LinkFlags% /nologo /OUT:"%ProjectName%.exe" REM cl /P /c %CompileFlags% %CompileEntryPoint% diff --git a/dqn.h b/dqn.h index b7ba1c0..4b9c681 100644 --- a/dqn.h +++ b/dqn.h @@ -42,6 +42,7 @@ // #DqnWChar WChar Operations (IsDigit(), IsAlpha() etc) // #DqnWStr WStr Operations (WStr_Len() etc) // #DqnRnd Random Number Generator (ints and floats) +// #Dqn_* Dqn_QuickSort // #XPlatform (Win32 & Unix) // #DqnFile File I/O (Read, Write, Delete) @@ -61,8 +62,14 @@ // #DqnSprintf Cross-platform Sprintf Implementation (Public Domain lib stb_sprintf) // TODO +// - DqnMemStack +// - Allow 0 size memblock stack initialisation/block-less stack for situations where you don't +// care about specifying a size upfront +// // - Win32 // - Get rid of reliance on MAX_PATH +// +// - Make lib compile and run on Linux with GCC using -03 //////////////////////////////////////////////////////////////////////////////// // Preprocessor Checks @@ -972,6 +979,90 @@ DQN_FILE_SCOPE u32 DqnRnd_PCGNext (DqnRandPCGState *pcg); DQN_FILE_SCOPE f32 DqnRnd_PCGNextf(DqnRandPCGState *pcg); // return: A random integer N between [min, max] DQN_FILE_SCOPE i32 DqnRnd_PCGRange(DqnRandPCGState *pcg, i32 min, i32 max); + +//////////////////////////////////////////////////////////////////////////////// +// #Dqn_* Public API +//////////////////////////////////////////////////////////////////////////////// +typedef bool Dqn_QuickSortLessThanCallback(const void *const val1, const void *const val2); +typedef void Dqn_QuickSortSwapCallback (void *const val1, void *const val2); +DQN_FILE_SCOPE void Dqn_QuickSortC(void *const array, const u32 itemSize, const u32 size, + Dqn_QuickSortLessThanCallback *const IsLessThan, + Dqn_QuickSortSwapCallback *const Swap); + +template +DQN_FILE_SCOPE void Dqn_QuickSort(T *const array, const u32 size, + Dqn_QuickSortLessThanCallback *const IsLessThan) +{ + if (!array || size == 0 || size == 1 || !IsLessThan) return; + + // Insertion Sort, under 24->32 is an optimal amount + const u32 QUICK_SORT_THRESHOLD = 24; + if (size < QUICK_SORT_THRESHOLD) + { + u32 itemToInsertIndex = 1; + while (itemToInsertIndex < size) + { + for (u32 checkIndex = 0; checkIndex < itemToInsertIndex; checkIndex++) + { + if (!IsLessThan(&array[checkIndex], &array[itemToInsertIndex])) + { + T itemToInsert = array[itemToInsertIndex]; + for (u32 i = itemToInsertIndex; i > checkIndex; i--) + array[i] = array[i - 1]; + + array[checkIndex] = itemToInsert; + break; + } + } + itemToInsertIndex++; + } + + return; + } + + DqnRandPCGState state = {}; + DqnRnd_PCGInit(&state); + + u32 pivotIndex = DqnRnd_PCGRange(&state, 0, size - 1); + u32 partitionIndex = 0; + u32 startIndex = 0; + + // Swap pivot with last index, so pivot is always at the end of the array. + // This makes logic much simpler. + { + u32 lastIndex = size - 1; + DQN_SWAP(T, array[lastIndex], array[pivotIndex]); + pivotIndex = lastIndex; + } + + // 4^, 8, 7, 5, 2, 3, 6 + if (IsLessThan(&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 (u32 checkIndex = startIndex; checkIndex < size; checkIndex++) + { + if (IsLessThan(&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, IsLessThan); + + // Skip the value at partion index since that is guaranteed to be sorted. + // 4, 5, 2, 3, (x), 8, 7 + u32 oneAfterPartitionIndex = partitionIndex + 1; + Dqn_QuickSort(array + oneAfterPartitionIndex, (size - oneAfterPartitionIndex), IsLessThan); +} + #endif /* DQN_H */ //////////////////////////////////////////////////////////////////////////////// @@ -1899,9 +1990,10 @@ DqnMemStackInternal_AllocateBlock(u32 byteAlign, size_t size, const bool zeroCle if (!result) return NULL; - result->memory = (u8 *)DQN_ALIGN_POW_N((u8 *)result + sizeof(*result), byteAlign); - result->size = alignedSize; - result->used = 0; + result->memory = (u8 *)DQN_ALIGN_POW_N((u8 *)result + sizeof(*result), byteAlign); + result->size = alignedSize; + result->used = 0; + result->prevBlock = NULL; return result; } @@ -1966,6 +2058,8 @@ DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem(DqnMemStack *const stack, u8 *c const u32 DEFAULT_ALIGNMENT = 4; stack->tempRegionCount = 0; stack->byteAlign = (byteAlign == 0) ? DEFAULT_ALIGNMENT : byteAlign; + + DQN_ASSERT(!stack->block->prevBlock); return true; } @@ -1976,6 +2070,7 @@ DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedSize(DqnMemStack *const stack, size if (result) { stack->flags |= DqnMemStackFlag_IsNotExpandable; + DQN_ASSERT(!stack->block->prevBlock); return true; } @@ -1996,6 +2091,7 @@ DQN_FILE_SCOPE bool DqnMemStack_Init(DqnMemStack *const stack, size_t size, cons stack->tempRegionCount = 0; stack->byteAlign = byteAlign; stack->flags = 0; + DQN_ASSERT(!stack->block->prevBlock); return true; } @@ -2067,6 +2163,10 @@ DQN_FILE_SCOPE bool DqnMemStack_Pop(DqnMemStack *const stack, void *ptr, size_t if (DQN_ASSERT_MSG(calcSize == sizeAligned, "'ptr' was not the last item allocated to memStack")) { stack->block->used -= sizeAligned; + if (stack->block->used == 0 && stack->block->prevBlock) + { + return DQN_ASSERT(DqnMemStack_FreeLastBlock(stack)); + } return true; } } @@ -3927,6 +4027,65 @@ DQN_FILE_SCOPE i32 DqnRnd_PCGRange(DqnRandPCGState *pcg, i32 min, i32 max) return min + value; } +//////////////////////////////////////////////////////////////////////////////// +// #Dqn_* Implementation +//////////////////////////////////////////////////////////////////////////////// +FILE_SCOPE void *DqnInternal_QuickSortGetArrayItemPtr(void *const array, const u32 index, + const u32 size) +{ + u8 *byteArray = (u8 *)array; + void *result = (void *)(byteArray + (index * size)); + return result; +} + +DQN_FILE_SCOPE void Dqn_QuickSortC(void *const array, const u32 itemSize, const u32 size, + Dqn_QuickSortLessThanCallback *const IsLessThan, + Dqn_QuickSortSwapCallback *const Swap) +{ + // NOTE: See