Browse Source
A few codepaths had to rely on a global variable when sorting elements of an array because sort(3) API does not allow extra data to be passed to the comparison function. Use qsort_s() when natively available, and a fallback implementation of it when not, to eliminate the need, which is a prerequisite for making the codepath reentrant. * rs/qsort-s: ref-filter: use QSORT_S in ref_array_sort() string-list: use QSORT_S in string_list_sort() perf: add basic sort performance test add QSORT_S compat: add qsort_s()maint
![gitster@pobox.com](/assets/img/avatar_default.png)
7 changed files with 146 additions and 12 deletions
@ -0,0 +1,69 @@ |
|||||||
|
#include "../git-compat-util.h" |
||||||
|
|
||||||
|
/* |
||||||
|
* A merge sort implementation, simplified from the qsort implementation |
||||||
|
* by Mike Haertel, which is a part of the GNU C Library. |
||||||
|
* Added context pointer, safety checks and return value. |
||||||
|
*/ |
||||||
|
|
||||||
|
static void msort_with_tmp(void *b, size_t n, size_t s, |
||||||
|
int (*cmp)(const void *, const void *, void *), |
||||||
|
char *t, void *ctx) |
||||||
|
{ |
||||||
|
char *tmp; |
||||||
|
char *b1, *b2; |
||||||
|
size_t n1, n2; |
||||||
|
|
||||||
|
if (n <= 1) |
||||||
|
return; |
||||||
|
|
||||||
|
n1 = n / 2; |
||||||
|
n2 = n - n1; |
||||||
|
b1 = b; |
||||||
|
b2 = (char *)b + (n1 * s); |
||||||
|
|
||||||
|
msort_with_tmp(b1, n1, s, cmp, t, ctx); |
||||||
|
msort_with_tmp(b2, n2, s, cmp, t, ctx); |
||||||
|
|
||||||
|
tmp = t; |
||||||
|
|
||||||
|
while (n1 > 0 && n2 > 0) { |
||||||
|
if (cmp(b1, b2, ctx) <= 0) { |
||||||
|
memcpy(tmp, b1, s); |
||||||
|
tmp += s; |
||||||
|
b1 += s; |
||||||
|
--n1; |
||||||
|
} else { |
||||||
|
memcpy(tmp, b2, s); |
||||||
|
tmp += s; |
||||||
|
b2 += s; |
||||||
|
--n2; |
||||||
|
} |
||||||
|
} |
||||||
|
if (n1 > 0) |
||||||
|
memcpy(tmp, b1, n1 * s); |
||||||
|
memcpy(b, t, (n - n2) * s); |
||||||
|
} |
||||||
|
|
||||||
|
int git_qsort_s(void *b, size_t n, size_t s, |
||||||
|
int (*cmp)(const void *, const void *, void *), void *ctx) |
||||||
|
{ |
||||||
|
const size_t size = st_mult(n, s); |
||||||
|
char buf[1024]; |
||||||
|
|
||||||
|
if (!n) |
||||||
|
return 0; |
||||||
|
if (!b || !cmp) |
||||||
|
return -1; |
||||||
|
|
||||||
|
if (size < sizeof(buf)) { |
||||||
|
/* The temporary array fits on the small on-stack buffer. */ |
||||||
|
msort_with_tmp(b, n, s, cmp, buf, ctx); |
||||||
|
} else { |
||||||
|
/* It's somewhat large, so malloc it. */ |
||||||
|
char *tmp = xmalloc(size); |
||||||
|
msort_with_tmp(b, n, s, cmp, tmp, ctx); |
||||||
|
free(tmp); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
#!/bin/sh |
||||||
|
|
||||||
|
test_description='Basic sort performance tests' |
||||||
|
. ./perf-lib.sh |
||||||
|
|
||||||
|
test_perf_default_repo |
||||||
|
|
||||||
|
test_expect_success 'setup' ' |
||||||
|
git ls-files --stage "*.[ch]" "*.sh" | |
||||||
|
cut -f2 -d" " | |
||||||
|
git cat-file --batch >unsorted |
||||||
|
' |
||||||
|
|
||||||
|
test_perf 'sort(1)' ' |
||||||
|
sort <unsorted >expect |
||||||
|
' |
||||||
|
|
||||||
|
test_perf 'string_list_sort()' ' |
||||||
|
test-string-list sort <unsorted >actual |
||||||
|
' |
||||||
|
|
||||||
|
test_expect_success 'string_list_sort() sorts like sort(1)' ' |
||||||
|
test_cmp_bin expect actual |
||||||
|
' |
||||||
|
|
||||||
|
test_done |
Loading…
Reference in new issue