diff --git a/libc/stdlib/qsort_r.cpp b/libc/stdlib/qsort_r.cpp index a3ee4f97..14400bdb 100644 --- a/libc/stdlib/qsort_r.cpp +++ b/libc/stdlib/qsort_r.cpp @@ -22,13 +22,12 @@ *******************************************************************************/ -#include #include #include -static void memswap(uint8_t* a, uint8_t* b, size_t size) +static void memswap(unsigned char* a, unsigned char* b, size_t size) { - uint8_t tmp; + unsigned char tmp; for ( size_t i = 0; i < size; i++ ) { tmp = a[i]; @@ -37,8 +36,51 @@ static void memswap(uint8_t* a, uint8_t* b, size_t size) } } -// TODO: This is just a quick and dirty insertion sort. It'd be nice to have a -// good old quick sort here soon. +static +unsigned char* array_index(unsigned char* base, + size_t element_size, + size_t index) +{ + return base + element_size * index; +} + +static +size_t partition(unsigned char* base, + size_t element_size, + size_t num_elements, + size_t pivot_index, + int (*compare)(const void*, const void*, void*), + void* arg) +{ + if ( pivot_index != num_elements - 1 ) + { + unsigned char* pivot = array_index(base, element_size, pivot_index); + unsigned char* other = array_index(base, element_size, num_elements - 1); + memswap(pivot, other, element_size); + pivot_index = num_elements - 1; + } + + size_t store_index = 0; + for ( size_t i = 0; i < num_elements - 1; i++ ) + { + unsigned char* pivot = array_index(base, element_size, pivot_index); + unsigned char* value = array_index(base, element_size, i); + if ( compare(value, pivot, arg) <= 0 ) + { + unsigned char* other = array_index(base, element_size, store_index); + if ( value != other ) + memswap(value, other, element_size); + store_index++; + } + } + + unsigned char* pivot = array_index(base, element_size, pivot_index); + unsigned char* value = array_index(base, element_size, store_index); + memswap(pivot, value, element_size); + + return store_index; +} + extern "C" void qsort_r(void* base_ptr, size_t num_elements, @@ -46,19 +88,18 @@ void qsort_r(void* base_ptr, int (*compare)(const void*, const void*, void*), void* arg) { - uint8_t* base = (uint8_t*) base_ptr; - for ( size_t i = 0; i < num_elements; i++ ) - { - for ( size_t c = i; c; c-- ) - { - size_t currentoff = c * element_size; - uint8_t* current = base + currentoff; - size_t p = c-1; - size_t prevoff = p * element_size; - uint8_t* prev = base + prevoff; - int cmp = compare(prev, current, arg); - if ( cmp <= 0 ) { break; } - memswap(prev, current, element_size); - } - } + unsigned char* base = (unsigned char*) base_ptr; + + if ( !element_size || num_elements < 2 ) + return; + + size_t pivot_index = num_elements / 2; + pivot_index = partition(base, element_size, num_elements, pivot_index, compare, arg); + + if ( 2 <= pivot_index ) + qsort_r(base, pivot_index, element_size, compare, arg); + + if ( 2 <= num_elements - (pivot_index + 1) ) + qsort_r(array_index(base, element_size, pivot_index + 1), + num_elements - (pivot_index + 1), element_size, compare, arg); }