// these macros make the code more readable, and a bit faster than a sift_down
// function.
#define UPDATE_CHILD (child_ptr = base + ((root_ptr - base) * 2 + size))
#define HAS_CHILD (UPDATE_CHILD <= end_ptr)
#define HAS_SIBLING (child_ptr < end_ptr)
#define LESS_THAN(a, b) (cmp(a, b) < 0)
#define END (base + end * size)
#define ROOT (base + start * size)
#define SIBLING (child_ptr + size)
#define SIFT_DOWN \
{ \
root_ptr = ROOT; \
end_ptr = END; \
while(HAS_CHILD) { \
if(HAS_SIBLING && LESS_THAN(child_ptr, SIBLING)) \
child_ptr += size; \
if(LESS_THAN(root_ptr, child_ptr)) { \
swap_bytes(root_ptr, child_ptr, size); \
root_ptr = child_ptr; \
} \
else break; \
} \
}
void
hsort(void *base, size_t num, size_t size, int (*cmp)(const void*, const void*))
{
size_t end = num - 1;
void *root_ptr, *child_ptr, *end_ptr; // used by SIFT_DOWN only.
int start = end / 2;
if(base && num && size && cmp) {
// arrange the array into a heap.
while(start >= 0) {
SIFT_DOWN;
start--;
}
start = 0;
while(end) {
// swap the root of the heap with the last item.
// also decrease heap size (end) to keep previously placed values.
swap_bytes(base + end-- * size, base, size);
// put the next largest element at the top of the heap.
SIFT_DOWN;
}
}
}
Currently about half as fast as qsort() on my system; this sorts 1,048,576 random ints in about 0.36 seconds, qsort() in about 0.18 seconds.