aboutsummaryrefslogtreecommitdiff
path: root/Src/nu/sort.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/nu/sort.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/nu/sort.cpp')
-rw-r--r--Src/nu/sort.cpp361
1 files changed, 361 insertions, 0 deletions
diff --git a/Src/nu/sort.cpp b/Src/nu/sort.cpp
new file mode 100644
index 00000000..dea3dada
--- /dev/null
+++ b/Src/nu/sort.cpp
@@ -0,0 +1,361 @@
+#include <bfc/platform/types.h>
+#include "sort.h"
+#include <assert.h>
+/***
+*qsort.c - quicksort algorithm; qsort() library function for sorting arrays
+*
+* Copyright (c) Microsoft Corporation. All rights reserved.
+*
+*Purpose:
+* To implement the qsort() routine for sorting arrays.
+*
+*******************************************************************************/
+
+/* Always compile this module for speed, not size */
+#pragma optimize("t", on)
+
+/* prototypes for local routines */
+static void shortsort(uint8_t *lo, uint8_t *hi, size_t width, const void *context,
+ int (__fastcall *comp)(const void *, const void *, const void *));
+static void swap(uint8_t *p, uint8_t *q, size_t width);
+
+/* this parameter defines the cutoff between using quick sort and
+ insertion sort for arrays; arrays with lengths shorter or equal to the
+ below value use insertion sort */
+
+#define CUTOFF 8 /* testing shows that this is good value */
+
+/***
+*qsort(base, num, wid, context, comp) - quicksort function for sorting arrays
+*
+*Purpose:
+* quicksort the array of elements
+* side effects: sorts in place
+* maximum array size is number of elements times size of elements,
+* but is limited by the virtual address space of the processor
+*
+*Entry:
+* char *base = pointer to base of array
+* size_t num = number of elements in the array
+* size_t width = width in bytes of each array element
+* int (*comp)() = pointer to function returning analog of strcmp for
+* strings, but supplied by user for comparing the array elements.
+* it accepts 2 pointers to elements and returns neg if 1<2, 0 if
+* 1=2, pos if 1>2.
+*
+*Exit:
+* returns void
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+/* sort the array between lo and hi (inclusive) */
+
+#define STKSIZ (8*sizeof(void*) - 2)
+
+void __cdecl nu::qsort (
+ void *base,
+ size_t num,
+ size_t width,
+ const void *context,
+ int (__fastcall *comp)(const void *, const void *, const void *)
+ )
+{
+ /* Note: the number of stack entries required is no more than
+ 1 + log2(num), so 30 is sufficient for any array */
+ uint8_t *lo, *hi; /* ends of sub-array currently sorting */
+ uint8_t *mid; /* points to middle of subarray */
+ uint8_t *loguy, *higuy; /* traveling pointers for partition step */
+ size_t size; /* size of the sub-array */
+ uint8_t *lostk[STKSIZ] = {0}, *histk[STKSIZ] = {0};
+ int stkptr; /* stack for saving sub-array to be processed */
+
+ assert((width % sizeof(void *)) == 0);
+ if (num < 2 || width == 0)
+ return; /* nothing to do */
+
+ stkptr = 0; /* initialize stack */
+
+ lo = static_cast<uint8_t *>(base);
+ hi = (uint8_t *)base + width * (num-1); /* initialize limits */
+
+ /* this entry point is for pseudo-recursion calling: setting
+ lo and hi and jumping to here is like recursion, but stkptr is
+ preserved, locals aren't, so we preserve stuff on the stack */
+recurse:
+
+ size = (hi - lo) / width + 1; /* number of el's to sort */
+
+ /* below a certain size, it is faster to use a O(n^2) sorting method */
+ if (size <= CUTOFF) {
+ shortsort(lo, hi, width, context, comp);
+ }
+ else {
+ /* First we pick a partitioning element. The efficiency of the
+ algorithm demands that we find one that is approximately the median
+ of the values, but also that we select one fast. We choose the
+ median of the first, middle, and last elements, to avoid bad
+ performance in the face of already sorted data, or data that is made
+ up of multiple sorted runs appended together. Testing shows that a
+ median-of-three algorithm provides better performance than simply
+ picking the middle element for the latter case. */
+
+ mid = lo + (size / 2) * width; /* find middle element */
+
+ /* Sort the first, middle, last elements into order */
+ if (comp(lo, mid, context) > 0) {
+ swap(lo, mid, width);
+ }
+ if (comp(lo, hi, context) > 0) {
+ swap(lo, hi, width);
+ }
+ if (comp(mid, hi, context) > 0) {
+ swap(mid, hi, width);
+ }
+
+ /* We now wish to partition the array into three pieces, one consisting
+ of elements <= partition element, one of elements equal to the
+ partition element, and one of elements > than it. This is done
+ below; comments indicate conditions established at every step. */
+
+ loguy = lo;
+ higuy = hi;
+
+ /* Note that higuy decreases and loguy increases on every iteration,
+ so loop must terminate. */
+ for (;;) {
+ /* lo <= loguy < hi, lo < higuy <= hi,
+ A[i] <= A[mid] for lo <= i <= loguy,
+ A[i] > A[mid] for higuy <= i < hi,
+ A[hi] >= A[mid] */
+
+ /* The doubled loop is to avoid calling comp(mid,mid), since some
+ existing comparison funcs don't work when passed the same
+ value for both pointers. */
+
+ if (mid > loguy) {
+ do {
+ loguy += width;
+ } while (loguy < mid && comp(loguy, mid, context) <= 0);
+ }
+ if (mid <= loguy) {
+ do {
+ loguy += width;
+ } while (loguy <= hi && comp(loguy, mid, context) <= 0);
+ }
+
+ /* lo < loguy <= hi+1, A[i] <= A[mid] for lo <= i < loguy,
+ either loguy > hi or A[loguy] > A[mid] */
+
+ do {
+ higuy -= width;
+ } while (higuy > mid && comp(higuy, mid, context) > 0);
+
+ /* lo <= higuy < hi, A[i] > A[mid] for higuy < i < hi,
+ either higuy == lo or A[higuy] <= A[mid] */
+
+ if (higuy < loguy)
+ break;
+
+ /* if loguy > hi or higuy == lo, then we would have exited, so
+ A[loguy] > A[mid], A[higuy] <= A[mid],
+ loguy <= hi, higuy > lo */
+
+ swap(loguy, higuy, width);
+
+ /* If the partition element was moved, follow it. Only need
+ to check for mid == higuy, since before the swap,
+ A[loguy] > A[mid] implies loguy != mid. */
+
+ if (mid == higuy)
+ mid = loguy;
+
+ /* A[loguy] <= A[mid], A[higuy] > A[mid]; so condition at top
+ of loop is re-established */
+ }
+
+ /* A[i] <= A[mid] for lo <= i < loguy,
+ A[i] > A[mid] for higuy < i < hi,
+ A[hi] >= A[mid]
+ higuy < loguy
+ implying:
+ higuy == loguy-1
+ or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */
+
+ /* Find adjacent elements equal to the partition element. The
+ doubled loop is to avoid calling comp(mid,mid), since some
+ existing comparison funcs don't work when passed the same value
+ for both pointers. */
+
+ higuy += width;
+ if (mid < higuy) {
+ do {
+ higuy -= width;
+ } while (higuy > mid && comp(higuy, mid, context) == 0);
+ }
+ if (mid >= higuy) {
+ do {
+ higuy -= width;
+ } while (higuy > lo && comp(higuy, mid, context) == 0);
+ }
+
+ /* OK, now we have the following:
+ higuy < loguy
+ lo <= higuy <= hi
+ A[i] <= A[mid] for lo <= i <= higuy
+ A[i] == A[mid] for higuy < i < loguy
+ A[i] > A[mid] for loguy <= i < hi
+ A[hi] >= A[mid] */
+
+ /* We've finished the partition, now we want to sort the subarrays
+ [lo, higuy] and [loguy, hi].
+ We do the smaller one first to minimize stack usage.
+ We only sort arrays of length 2 or more.*/
+
+ if ( higuy - lo >= hi - loguy ) {
+ if (lo < higuy) {
+ lostk[stkptr] = lo;
+ histk[stkptr] = higuy;
+ ++stkptr;
+ } /* save big recursion for later */
+
+ if (loguy < hi) {
+ lo = loguy;
+ goto recurse; /* do small recursion */
+ }
+ }
+ else {
+ if (loguy < hi) {
+ lostk[stkptr] = loguy;
+ histk[stkptr] = hi;
+ ++stkptr; /* save big recursion for later */
+ }
+
+ if (lo < higuy) {
+ hi = higuy;
+ goto recurse; /* do small recursion */
+ }
+ }
+ }
+
+ /* We have sorted the array, except for any pending sorts on the stack.
+ Check if there are any, and do them. */
+
+ --stkptr;
+ if (stkptr >= 0) {
+ lo = lostk[stkptr];
+ hi = histk[stkptr];
+ goto recurse; /* pop subarray from stack */
+ }
+ else
+ return; /* all subarrays done */
+}
+
+
+/***
+*shortsort(hi, lo, width, comp) - insertion sort for sorting short arrays
+*
+*Purpose:
+* sorts the sub-array of elements between lo and hi (inclusive)
+* side effects: sorts in place
+* assumes that lo < hi
+*
+*Entry:
+* char *lo = pointer to low element to sort
+* char *hi = pointer to high element to sort
+* size_t width = width in bytes of each array element
+* int (*comp)() = pointer to function returning analog of strcmp for
+* strings, but supplied by user for comparing the array elements.
+* it accepts 2 pointers to elements and returns neg if 1<2, 0 if
+* 1=2, pos if 1>2.
+*
+*Exit:
+* returns void
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+static void __cdecl shortsort (
+ uint8_t *lo,
+ uint8_t *hi,
+ size_t width,
+ const void *context,
+ int (__fastcall *comp)(const void *, const void *, const void *)
+ )
+{
+ uint8_t *p;
+
+ /* Note: in assertions below, i and j are alway inside original bound of
+ array to sort. */
+
+ while (hi > lo) {
+ /* A[i] <= A[j] for i <= j, j > hi */
+ uint8_t *max = lo;
+ for (p = lo+width; p <= hi; p += width) {
+ /* A[i] <= A[max] for lo <= i < p */
+ if (comp(p, max, context) > 0) {
+ max = p;
+ }
+ /* A[i] <= A[max] for lo <= i <= p */
+ }
+
+ /* A[i] <= A[max] for lo <= i <= hi */
+
+ swap(max, hi, width);
+
+ /* A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, j >= hi */
+
+ hi -= width;
+
+ /* A[i] <= A[j] for i <= j, j > hi, loop top condition established */
+ }
+ /* A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] for i < j,
+ so array is sorted */
+}
+
+
+/***
+*swap(a, b, width) - swap two elements
+*
+*Purpose:
+* swaps the two array elements of size width
+*
+*Entry:
+* char *a, *b = pointer to two elements to swap
+* size_t width = width in bytes of each array element
+*
+*Exit:
+* returns void
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+static void swap (
+ uint8_t *_a,
+ uint8_t *_b,
+ size_t width
+ )
+{
+#if 1
+ void *tmp;
+ void **a = (void **)_a;
+ void **b = (void **)_b;
+ if ( a != b )
+ /* Do the swap one character at a time to avoid potential alignment
+ problems. */
+ do {
+ tmp = *a;
+ *a++ = *b;
+ *b++ = tmp;
+ width-=sizeof(void *);
+ } while (width);
+#else
+ //void *temp = alloca(width);
+ memcpy(temp, a, width);
+ memcpy(a, b, width);
+ memcpy(b, temp, width);
+ #endif
+}