|
|
|
@ -653,115 +653,213 @@ hb_bsearch (const void *key, const void *base, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* From https://github.com/noporpoise/sort_r
|
|
|
|
|
* With following modifications: |
|
|
|
|
* |
|
|
|
|
* 10 November 2018: |
|
|
|
|
* https://github.com/noporpoise/sort_r/issues/7
|
|
|
|
|
*/ |
|
|
|
|
Feb 5, 2019 (c8c65c1e) |
|
|
|
|
Modified to support optional argument using templates */ |
|
|
|
|
|
|
|
|
|
/* Isaac Turner 29 April 2014 Public Domain */ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
hb_sort_r function to be exported. |
|
|
|
|
|
|
|
|
|
hb_qsort function to be exported. |
|
|
|
|
Parameters: |
|
|
|
|
base is the array to be sorted |
|
|
|
|
nel is the number of elements in the array |
|
|
|
|
width is the size in bytes of each element of the array |
|
|
|
|
compar is the comparison function |
|
|
|
|
arg is a pointer to be passed to the comparison function |
|
|
|
|
arg (optional) is a pointer to be passed to the comparison function |
|
|
|
|
|
|
|
|
|
void hb_sort_r(void *base, size_t nel, size_t width, |
|
|
|
|
int (*compar)(const void *_a, const void *_b, void *_arg), |
|
|
|
|
void *arg); |
|
|
|
|
void hb_sort(void *base, size_t nel, size_t width, |
|
|
|
|
int (*compar)(const void *_a, const void *_b, [void *_arg]), |
|
|
|
|
[void *arg]); |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
#define SORT_R_SWAP(a,b,tmp) ((tmp) = (a), (a) = (b), (b) = (tmp)) |
|
|
|
|
|
|
|
|
|
/* swap a and b */ |
|
|
|
|
/* a and b must not be equal! */ |
|
|
|
|
static inline void sort_r_swap(char *__restrict a, char *__restrict b, |
|
|
|
|
size_t w) |
|
|
|
|
{ |
|
|
|
|
char tmp, *end = a+w; |
|
|
|
|
for(; a < end; a++, b++) { SORT_R_SWAP(*a, *b, tmp); } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* swap a, b iff a>b */ |
|
|
|
|
/* a and b must not be equal! */ |
|
|
|
|
/* __restrict is same as restrict but better support on old machines */ |
|
|
|
|
static int sort_r_cmpswap(char *__restrict a, char *__restrict b, size_t w, |
|
|
|
|
int (*compar)(const void *_a, const void *_b, |
|
|
|
|
void *_arg), |
|
|
|
|
void *arg) |
|
|
|
|
template <typename ...Ts> |
|
|
|
|
static inline int sort_r_cmpswap(char *__restrict a, |
|
|
|
|
char *__restrict b, size_t w, |
|
|
|
|
int (*compar)(const void *_a, |
|
|
|
|
const void *_b, |
|
|
|
|
Ts... _ds), |
|
|
|
|
Ts... ds) |
|
|
|
|
{ |
|
|
|
|
char tmp, *end = a+w; |
|
|
|
|
if(compar(a, b, arg) > 0) { |
|
|
|
|
for(; a < end; a++, b++) { tmp = *a; *a = *b; *b = tmp; } |
|
|
|
|
if(compar(a, b, ds...) > 0) { |
|
|
|
|
sort_r_swap(a, b, w); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Swap consecutive blocks of bytes of size na and nb starting at memory addr ptr, |
|
|
|
|
with the smallest swap so that the blocks are in the opposite order. Blocks may |
|
|
|
|
be internally re-ordered e.g. |
|
|
|
|
12345ab -> ab34512 |
|
|
|
|
123abc -> abc123 |
|
|
|
|
12abcde -> deabc12 |
|
|
|
|
*/ |
|
|
|
|
static inline void sort_r_swap_blocks(char *ptr, size_t na, size_t nb) |
|
|
|
|
{ |
|
|
|
|
if(na > 0 && nb > 0) { |
|
|
|
|
if(na > nb) { sort_r_swap(ptr, ptr+na, nb); } |
|
|
|
|
else { sort_r_swap(ptr, ptr+nb, na); } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Implement recursive quicksort ourselves */ |
|
|
|
|
/* Note: quicksort is not stable, equivalent values may be swapped */ |
|
|
|
|
template <typename ...Ts> |
|
|
|
|
static inline void sort_r_simple(void *base, size_t nel, size_t w, |
|
|
|
|
int (*compar)(const void *_a, const void *_b, |
|
|
|
|
void *_arg), |
|
|
|
|
void *arg) |
|
|
|
|
int (*compar)(const void *_a, |
|
|
|
|
const void *_b, |
|
|
|
|
Ts... _ds), |
|
|
|
|
Ts... ds) |
|
|
|
|
{ |
|
|
|
|
char *b = (char *)base, *end = b + nel*w; |
|
|
|
|
if(nel < 7) { |
|
|
|
|
|
|
|
|
|
/* for(size_t i=0; i<nel; i++) {printf("%4i", *(int*)(b + i*sizeof(int)));}
|
|
|
|
|
printf("\n"); */ |
|
|
|
|
|
|
|
|
|
if(nel < 10) { |
|
|
|
|
/* Insertion sort for arbitrarily small inputs */ |
|
|
|
|
char *pi, *pj; |
|
|
|
|
for(pi = b+w; pi < end; pi += w) { |
|
|
|
|
for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar,arg); pj -= w) {} |
|
|
|
|
for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar,ds...); pj -= w) {} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
/* nel > 6; Quicksort */ |
|
|
|
|
/* nel > 9; Quicksort */ |
|
|
|
|
|
|
|
|
|
/* Use median of first, middle and last items as pivot */ |
|
|
|
|
char *x, *y, *xend, ch; |
|
|
|
|
char *pl, *pm, *pr; |
|
|
|
|
int cmp; |
|
|
|
|
char *pl, *ple, *pr, *pre, *pivot; |
|
|
|
|
char *last = b+w*(nel-1), *tmp; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Use median of second, middle and second-last items as pivot. |
|
|
|
|
First and last may have been swapped with pivot and therefore be extreme |
|
|
|
|
*/ |
|
|
|
|
char *l[3]; |
|
|
|
|
l[0] = b; |
|
|
|
|
l[0] = b + w; |
|
|
|
|
l[1] = b+w*(nel/2); |
|
|
|
|
l[2] = last; |
|
|
|
|
l[2] = last - w; |
|
|
|
|
|
|
|
|
|
if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; } |
|
|
|
|
if(compar(l[1],l[2],arg) > 0) { |
|
|
|
|
tmp=l[1]; l[1]=l[2]; l[2]=tmp; /* swap(l[1],l[2]) */ |
|
|
|
|
if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; } |
|
|
|
|
} |
|
|
|
|
/* printf("pivots: %i, %i, %i\n", *(int*)l[0], *(int*)l[1], *(int*)l[2]); */ |
|
|
|
|
|
|
|
|
|
/* swap l[id], l[2] to put pivot as last element */ |
|
|
|
|
for(x = l[1], y = last, xend = x+w; x<xend; x++, y++) { |
|
|
|
|
ch = *x; *x = *y; *y = ch; |
|
|
|
|
if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); } |
|
|
|
|
if(compar(l[1],l[2],ds...) > 0) { |
|
|
|
|
SORT_R_SWAP(l[1], l[2], tmp); |
|
|
|
|
if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pl = b; |
|
|
|
|
pr = last; |
|
|
|
|
/* swap mid value (l[1]), and last element to put pivot as last element */ |
|
|
|
|
if(l[1] != last) { sort_r_swap(l[1], last, w); } |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
pl is the next item on the left to be compared to the pivot |
|
|
|
|
pr is the last item on the right that was compared to the pivot |
|
|
|
|
ple is the left position to put the next item that equals the pivot |
|
|
|
|
ple is the last right position where we put an item that equals the pivot |
|
|
|
|
v- end (beyond the array) |
|
|
|
|
EEEEEELLLLLLLLuuuuuuuuGGGGGGGEEEEEEEE. |
|
|
|
|
^- b ^- ple ^- pl ^- pr ^- pre ^- last (where the pivot is) |
|
|
|
|
Pivot comparison key: |
|
|
|
|
E = equal, L = less than, u = unknown, G = greater than, E = equal |
|
|
|
|
*/ |
|
|
|
|
pivot = last; |
|
|
|
|
ple = pl = b; |
|
|
|
|
pre = pr = last; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Strategy: |
|
|
|
|
Loop into the list from the left and right at the same time to find: |
|
|
|
|
- an item on the left that is greater than the pivot |
|
|
|
|
- an item on the right that is less than the pivot |
|
|
|
|
Once found, they are swapped and the loop continues. |
|
|
|
|
Meanwhile items that are equal to the pivot are moved to the edges of the |
|
|
|
|
array. |
|
|
|
|
*/ |
|
|
|
|
while(pl < pr) { |
|
|
|
|
pm = pl+((pr-pl+1)>>1); |
|
|
|
|
for(; pl < pm; pl += w) { |
|
|
|
|
if(sort_r_cmpswap(pl, pr, w, compar, arg)) { |
|
|
|
|
pr -= w; /* pivot now at pl */ |
|
|
|
|
/* Move left hand items which are equal to the pivot to the far left.
|
|
|
|
|
break when we find an item that is greater than the pivot */ |
|
|
|
|
for(; pl < pr; pl += w) { |
|
|
|
|
cmp = compar(pl, pivot, ds...); |
|
|
|
|
if(cmp > 0) { break; } |
|
|
|
|
else if(cmp == 0) { |
|
|
|
|
if(ple < pl) { sort_r_swap(ple, pl, w); } |
|
|
|
|
ple += w; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/* break if last batch of left hand items were equal to pivot */ |
|
|
|
|
if(pl >= pr) { break; } |
|
|
|
|
/* Move right hand items which are equal to the pivot to the far right.
|
|
|
|
|
break when we find an item that is less than the pivot */ |
|
|
|
|
for(; pl < pr; ) { |
|
|
|
|
pr -= w; /* Move right pointer onto an unprocessed item */ |
|
|
|
|
cmp = compar(pr, pivot, ds...); |
|
|
|
|
if(cmp == 0) { |
|
|
|
|
pre -= w; |
|
|
|
|
if(pr < pre) { sort_r_swap(pr, pre, w); } |
|
|
|
|
} |
|
|
|
|
else if(cmp < 0) { |
|
|
|
|
if(pl < pr) { sort_r_swap(pl, pr, w); } |
|
|
|
|
pl += w; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
pm = pl+((pr-pl)>>1); |
|
|
|
|
for(; pm < pr; pr -= w) { |
|
|
|
|
if(sort_r_cmpswap(pl, pr, w, compar, arg)) { |
|
|
|
|
pl += w; /* pivot now at pr */ |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pl = pr; /* pr may have gone below pl */ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Now we need to go from: EEELLLGGGGEEEE |
|
|
|
|
to: LLLEEEEEEEGGGG |
|
|
|
|
Pivot comparison key: |
|
|
|
|
E = equal, L = less than, u = unknown, G = greater than, E = equal |
|
|
|
|
*/ |
|
|
|
|
sort_r_swap_blocks(b, ple-b, pl-ple); |
|
|
|
|
sort_r_swap_blocks(pr, pre-pr, end-pre); |
|
|
|
|
|
|
|
|
|
/*for(size_t i=0; i<nel; i++) {printf("%4i", *(int*)(b + i*sizeof(int)));}
|
|
|
|
|
printf("\n");*/ |
|
|
|
|
|
|
|
|
|
sort_r_simple(b, (pl-ple)/w, w, compar, ds...); |
|
|
|
|
sort_r_simple(end-(pre-pr), (pre-pr)/w, w, compar, ds...); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sort_r_simple(b, (pl-b)/w, w, compar, arg); |
|
|
|
|
sort_r_simple(pl+w, (end-(pl+w))/w, w, compar, arg); |
|
|
|
|
} |
|
|
|
|
static inline void |
|
|
|
|
hb_qsort (void *base, size_t nel, size_t width, |
|
|
|
|
int (*compar)(const void *_a, const void *_b)) |
|
|
|
|
{ |
|
|
|
|
#ifdef __OPTIMIZE_SIZE__ |
|
|
|
|
qsort (base, nel, width, compar); |
|
|
|
|
#else |
|
|
|
|
sort_r_simple (base, nel, width, compar); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static inline void |
|
|
|
|
hb_sort_r (void *base, size_t nel, size_t width, |
|
|
|
|
hb_qsort (void *base, size_t nel, size_t width, |
|
|
|
|
int (*compar)(const void *_a, const void *_b, void *_arg), |
|
|
|
|
void *arg) |
|
|
|
|
{ |
|
|
|
|
#ifdef HAVE_GNU_QSORT_R |
|
|
|
|
qsort_r (base, nel, width, compar, arg); |
|
|
|
|
#else |
|
|
|
|
sort_r_simple (base, nel, width, compar, arg); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|