130 lines
4.1 KiB
C
130 lines
4.1 KiB
C
//
|
|
// Created by rick on 06-03-21.
|
|
//
|
|
|
|
#include <libc/sort.h>
|
|
#include <libc/libc.h>
|
|
#include <types.h>
|
|
|
|
// taken from https://github.com/DevSolar/pdclib/blob/master/functions/stdlib/qsort.c
|
|
|
|
/* This implementation is taken from Paul Edward's PDPCLIB.
|
|
Original code is credited to Raymond Gardner, Englewood CO.
|
|
Minor mods are credited to Paul Edwards.
|
|
Some reformatting and simplification done by Martin Baute.
|
|
All code is still Public Domain.
|
|
*/
|
|
|
|
static inline void memswp(uint8_t *i, uint8_t *j, size_t size) {
|
|
uint8_t tmp[size];
|
|
memcpy(tmp, j, size);
|
|
memcpy(j, i, size);
|
|
memcpy(i, tmp, size);
|
|
}
|
|
|
|
/* For small sets, insertion sort is faster than quicksort.
|
|
T is the threshold below which insertion sort will be used.
|
|
Must be 3 or larger.
|
|
*/
|
|
#define T 7
|
|
|
|
/* Macros for handling the QSort stack */
|
|
#define PREPARE_STACK void * stack[STACKSIZE]; void ** stackptr = stack
|
|
#define PUSH(base, limit) stackptr[0] = base; stackptr[1] = limit; stackptr += 2
|
|
#define POP(base, limit) stackptr -= 2; (base) = stackptr[0]; (limit) = stackptr[1]
|
|
/* TODO: Stack usage is log2( nmemb ) (minus what T shaves off the worst case).
|
|
Worst-case nmemb is platform dependent and should probably be
|
|
configured through _PDCLIB_config.h.
|
|
*/
|
|
#define STACKSIZE 64
|
|
|
|
void qsort(void *base, size_t nmemb, size_t size, int ( *compar )(const void *, const void *)) {
|
|
void *i;
|
|
void *j;
|
|
size_t thresh = T * size;
|
|
void *base_ = base;
|
|
void *limit = base_ + nmemb * size;
|
|
PREPARE_STACK;
|
|
|
|
for (;;) {
|
|
if ((size_t) (limit - base_) > thresh) /* QSort for more than T elements. */
|
|
{
|
|
/* We work from second to last - first will be pivot element. */
|
|
i = base_ + size;
|
|
j = limit - size;
|
|
/* We swap first with middle element, then sort that with second
|
|
and last element so that eventually first element is the median
|
|
of the three - avoiding pathological pivots.
|
|
TODO: Instead of middle element, chose one randomly.
|
|
*/
|
|
memswp(((((size_t) (limit - base_)) / size) / 2) * size + base_, base_, size);
|
|
|
|
if (compar(i, j) > 0) {
|
|
memswp(i, j, size);
|
|
}
|
|
|
|
if (compar(base_, j) > 0) {
|
|
memswp(base_, j, size);
|
|
}
|
|
|
|
if (compar(i, base_) > 0) {
|
|
memswp(i, base_, size);
|
|
}
|
|
|
|
/* Now we have the median for pivot element, entering main Quicksort. */
|
|
for (;;) {
|
|
do {
|
|
/* move i right until *i >= pivot */
|
|
i += size;
|
|
} while (compar(i, base_) < 0);
|
|
|
|
do {
|
|
/* move j left until *j <= pivot */
|
|
j -= size;
|
|
} while (compar(j, base_) > 0);
|
|
|
|
if (i > j) {
|
|
/* break loop if pointers crossed */
|
|
break;
|
|
}
|
|
|
|
/* else swap elements, keep scanning */
|
|
memswp(i, j, size);
|
|
}
|
|
|
|
/* move pivot into correct place */
|
|
memswp(base_, j, size);
|
|
|
|
/* larger subfile base / limit to stack, sort smaller */
|
|
if (j - base_ > limit - i) {
|
|
/* left is larger */
|
|
PUSH(base_, j);
|
|
base_ = i;
|
|
} else {
|
|
/* right is larger */
|
|
PUSH(i, limit);
|
|
limit = j;
|
|
}
|
|
} else /* insertion sort for less than T elements */
|
|
{
|
|
for (j = base_, i = j + size; i < limit; j = i, i += size) {
|
|
for (; compar(j, j + size) > 0; j -= size) {
|
|
memswp(j, j + size, size);
|
|
|
|
if (j == base_) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (stackptr != stack) /* if any entries on stack */
|
|
{
|
|
POP(base_, limit);
|
|
} else /* else stack empty, done */
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|