#include "qsort.inl"
#include "cassert.h"
#include "nowarn.hxx"
typedef void (*i_SWAP)(char *a, char *b, uint32_t size);
static ___INLINE void i_SWAP_ALIGN(char *a, char *b, uint32_t size)
{
uint32_t n1 = size / (uint32_t)sizeofptr;
uint32_t i;
void **_a = dcast(a, void);
void **_b = dcast(b, void);
void *swap;
for (i = 0; i < n1; ++i, ++_a, ++_b)
{
swap = *_a;
*_a = *_b;
*_b = swap;
}
}
static ___INLINE void i_SWAP_PTR(char *a, char *b, uint32_t size)
{
void *swap = *dcast(a, void);
*dcast(a, void) = *dcast(b, void);
*dcast(b, void) = swap;
unref(size);
}
static ___INLINE void i_SWAP_GENERAL(char *a, char *b, uint32_t size)
{
uint32_t n1 = size / (uint32_t)sizeofptr;
uint32_t i;
void **_a = dcast(a, void);
void **_b = dcast(b, void);
void *swap;
char swapc;
for (i = 0; i < n1; ++i, ++_a, ++_b)
{
swap = *_a;
*_a = *_b;
*_b = swap;
}
a += n1 * sizeofptr;
b += n1 * sizeofptr;
n1 = size % (uint32_t)sizeofptr;
for (i = 0; i < n1; ++i, ++a, ++b)
{
swapc = *a;
*a = *b;
*b = swapc;
}
}
#define MAX_THRESH 4
typedef struct
{
char *lo;
char *hi;
} stack_node;
#define CHAR_BIT 8
#define STACK_SIZE (CHAR_BIT * sizeof(uint32_t))
#define PUSH(low, high) ((void)((top->lo = (low)), (top->hi = (high)), ++top))
#define POP(low, high) ((void)(--top, (low = top->lo), (high = top->hi)))
#define STACK_NOT_EMPTY (stack < top)
void _qsort_ex(const void *data, const uint32_t total_elems, const uint32_t sizeof_elem, FPtr_compare_ex func_compare, const void *user_data)
{
i_SWAP SWAP_FUNC;
char *base_ptr = (char *)data;
uint32_t max_thresh = MAX_THRESH * sizeof_elem;
cassert_no_null(base_ptr);
cassert(sizeof_elem > 0);
cassert_no_nullf(func_compare);
if (total_elems == 0)
return;
if (sizeof_elem == sizeofptr)
SWAP_FUNC = i_SWAP_PTR;
else if (sizeof_elem % (uint32_t)sizeofptr == 0)
SWAP_FUNC = i_SWAP_ALIGN;
else
SWAP_FUNC = i_SWAP_GENERAL;
if (total_elems > MAX_THRESH)
{
char *lo = base_ptr;
char *hi = &lo[sizeof_elem * (total_elems - 1)];
stack_node stack[STACK_SIZE];
stack_node *top = stack;
PUSH(NULL, NULL);
while (STACK_NOT_EMPTY)
{
char *left_ptr;
char *right_ptr;
char *mid = lo + sizeof_elem * ((uint32_t)(hi - lo) / sizeof_elem >> 1);
if (func_compare(cast(mid, void), cast(lo, void), user_data) < 0)
SWAP_FUNC(mid, lo, sizeof_elem);
if ((func_compare)(cast(hi, void), cast(mid, void), user_data) < 0)
SWAP_FUNC(mid, hi, sizeof_elem);
else
goto jump_over;
if (func_compare(cast(mid, void), cast(lo, void), user_data) < 0)
SWAP_FUNC(mid, lo, sizeof_elem);
jump_over:;
left_ptr = lo + sizeof_elem;
right_ptr = hi - sizeof_elem;
do
{
while (func_compare(cast(left_ptr, void), cast(mid, void), user_data) < 0)
left_ptr += sizeof_elem;
while (func_compare(cast(mid, void), cast(right_ptr, void), user_data) < 0)
right_ptr -= sizeof_elem;
if (left_ptr < right_ptr)
{
SWAP_FUNC(left_ptr, right_ptr, sizeof_elem);
if (mid == left_ptr)
mid = right_ptr;
else if (mid == right_ptr)
mid = left_ptr;
left_ptr += sizeof_elem;
right_ptr -= sizeof_elem;
}
else if (left_ptr == right_ptr)
{
left_ptr += sizeof_elem;
right_ptr -= sizeof_elem;
break;
}
} while (left_ptr <= right_ptr);
if ((uint32_t)(right_ptr - lo) <= max_thresh)
{
if ((uint32_t)(hi - left_ptr) <= max_thresh)
POP(lo, hi);
else
lo = left_ptr;
}
else if ((uint32_t)(hi - left_ptr) <= max_thresh)
hi = right_ptr;
else if ((right_ptr - lo) > (hi - left_ptr))
{
PUSH(lo, right_ptr);
lo = left_ptr;
}
else
{
PUSH(left_ptr, hi);
hi = right_ptr;
}
}
}
#define min(x, y) ((x) < (y) ? (x) : (y))
{
char *const end_ptr = &base_ptr[sizeof_elem * (total_elems - 1)];
char *tmp_ptr = base_ptr;
char *thresh = min(end_ptr, base_ptr + max_thresh);
char *run_ptr;
for (run_ptr = tmp_ptr + sizeof_elem; run_ptr <= thresh; run_ptr += sizeof_elem)
if (func_compare(cast(run_ptr, void), cast(tmp_ptr, void), user_data) < 0)
tmp_ptr = run_ptr;
if (tmp_ptr != base_ptr)
SWAP_FUNC(tmp_ptr, base_ptr, sizeof_elem);
run_ptr = base_ptr + sizeof_elem;
while ((run_ptr += sizeof_elem) <= end_ptr)
{
tmp_ptr = run_ptr - sizeof_elem;
while (func_compare(cast(run_ptr, void), cast(tmp_ptr, void), user_data) < 0)
tmp_ptr -= sizeof_elem;
tmp_ptr += sizeof_elem;
if (tmp_ptr != run_ptr)
{
char *trav;
trav = run_ptr + sizeof_elem;
while (--trav >= run_ptr)
{
char c = *trav;
char *hi, *lo;
for (hi = lo = trav; (lo -= sizeof_elem) >= tmp_ptr; hi = lo)
*hi = *lo;
*hi = c;
}
}
}
}
}