Update and use internal qsort everywhere
This commit is contained in:
parent
5074d665a8
commit
eff579f743
210
src/hb-algs.hh
210
src/hb-algs.hh
|
@ -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, b iff a>b */
|
||||
/* __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)
|
||||
/* 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;
|
||||
if(compar(a, b, arg) > 0) {
|
||||
for(; a < end; a++, b++) { tmp = *a; *a = *b; *b = tmp; }
|
||||
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 */
|
||||
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)
|
||||
{
|
||||
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]); */
|
||||
|
||||
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); }
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* 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 = b;
|
||||
pr = last;
|
||||
/*
|
||||
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 */
|
||||
break;
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort_r_simple(b, (pl-b)/w, w, compar, arg);
|
||||
sort_r_simple(pl+w, (end-(pl+w))/w, w, compar, arg);
|
||||
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...);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
hb_sort_r (void *base, size_t nel, size_t width,
|
||||
int (*compar)(const void *_a, const void *_b, void *_arg),
|
||||
void *arg)
|
||||
hb_qsort (void *base, size_t nel, size_t width,
|
||||
int (*compar)(const void *_a, const void *_b))
|
||||
{
|
||||
sort_r_simple(base, nel, width, compar, arg);
|
||||
#ifdef __OPTIMIZE_SIZE__
|
||||
qsort (base, nel, width, compar);
|
||||
#else
|
||||
sort_r_simple (base, nel, width, compar);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -141,13 +141,13 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
|
|||
hb_sorted_array_t<Type> qsort (int (*cmp_)(const void*, const void*))
|
||||
{
|
||||
if (likely (length))
|
||||
::qsort (arrayZ, length, this->item_size, cmp_);
|
||||
hb_qsort (arrayZ, length, this->item_size, cmp_);
|
||||
return hb_sorted_array_t<Type> (*this);
|
||||
}
|
||||
hb_sorted_array_t<Type> qsort ()
|
||||
{
|
||||
if (likely (length))
|
||||
::qsort (arrayZ, length, this->item_size, Type::cmp);
|
||||
hb_qsort (arrayZ, length, this->item_size, Type::cmp);
|
||||
return hb_sorted_array_t<Type> (*this);
|
||||
}
|
||||
void qsort (unsigned int start, unsigned int end)
|
||||
|
@ -155,7 +155,7 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
|
|||
end = hb_min (end, length);
|
||||
assert (start <= end);
|
||||
if (likely (start < end))
|
||||
::qsort (arrayZ + start, end - start, this->item_size, Type::cmp);
|
||||
hb_qsort (arrayZ + start, end - start, this->item_size, Type::cmp);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -157,7 +157,7 @@ struct post
|
|||
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
gids[i] = i;
|
||||
hb_sort_r (gids, count, sizeof (gids[0]), cmp_gids, (void *) this);
|
||||
hb_qsort (gids, count, sizeof (gids[0]), cmp_gids, (void *) this);
|
||||
|
||||
if (unlikely (!gids_sorted_by_name.cmpexch (nullptr, gids)))
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue