compat: add qsort_s()
The function qsort_s() was introduced with C11 Annex K; it provides the ability to pass a context pointer to the comparison function, supports the convention of using a NULL pointer for an empty array and performs a few safety checks. Add an implementation based on compat/qsort.c for platforms that lack a native standards-compliant qsort_s() (i.e. basically everyone). It doesn't perform the full range of possible checks: It uses size_t instead of rsize_t and doesn't check nmemb and size against RSIZE_MAX because we probably don't have the restricted size type defined. For the same reason it returns int instead of errno_t. Signed-off-by: Rene Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
René Scharfe committed
Jan 22, 2017 at 18:51 UTC
04ee8b875b96c096c479132adca02fa734c3fa96
3 files changed
+83
Makefile
+8
@@ -279,6 +279,9 @@ all::
279
# is a simplified version of the merge sort used in glibc. This is
280
# recommended if Git triggers O(n^2) behavior in your platform's qsort().
281
#
282
+# Define HAVE_ISO_QSORT_S if your platform provides a qsort_s() that's
283
+# compatible with the one described in C11 Annex K.
284
+#
285
# Define UNRELIABLE_FSTAT if your system's fstat does not return the same
286
# information on a not yet closed file that lstat would return for the same
287
# file after it was closed.
@@ -1423,6 +1426,11 @@ ifdef INTERNAL_QSORT
1426
COMPAT_CFLAGS += -DINTERNAL_QSORT
1427
COMPAT_OBJS += compat/qsort.o
1428
endif
1429
+ifdef HAVE_ISO_QSORT_S
1430
+ COMPAT_CFLAGS += -DHAVE_ISO_QSORT_S
1431
+else
1432
+ COMPAT_OBJS += compat/qsort_s.o
1433
+endif
1434
ifdef RUNTIME_PREFIX
1435
COMPAT_CFLAGS += -DRUNTIME_PREFIX
1436
endif
compat/qsort_s.c
new
+69
@@ -0,0 +1,69 @@
1
+#include "../git-compat-util.h"
2
+
3
+/*
4
+ * A merge sort implementation, simplified from the qsort implementation
5
+ * by Mike Haertel, which is a part of the GNU C Library.
6
+ * Added context pointer, safety checks and return value.
7
+ */
8
+
9
+static void msort_with_tmp(void *b, size_t n, size_t s,
10
+ int (*cmp)(const void *, const void *, void *),
11
+ char *t, void *ctx)
12
+{
13
+ char *tmp;
14
+ char *b1, *b2;
15
+ size_t n1, n2;
16
+
17
+ if (n <= 1)
18
+ return;
19
+
20
+ n1 = n / 2;
21
+ n2 = n - n1;
22
+ b1 = b;
23
+ b2 = (char *)b + (n1 * s);
24
+
25
+ msort_with_tmp(b1, n1, s, cmp, t, ctx);
26
+ msort_with_tmp(b2, n2, s, cmp, t, ctx);
27
+
28
+ tmp = t;
29
+
30
+ while (n1 > 0 && n2 > 0) {
31
+ if (cmp(b1, b2, ctx) <= 0) {
32
+ memcpy(tmp, b1, s);
33
+ tmp += s;
34
+ b1 += s;
35
+ --n1;
36
+ } else {
37
+ memcpy(tmp, b2, s);
38
+ tmp += s;
39
+ b2 += s;
40
+ --n2;
41
+ }
42
+ }
43
+ if (n1 > 0)
44
+ memcpy(tmp, b1, n1 * s);
45
+ memcpy(b, t, (n - n2) * s);
46
+}
47
+
48
+int git_qsort_s(void *b, size_t n, size_t s,
49
+ int (*cmp)(const void *, const void *, void *), void *ctx)
50
+{
51
+ const size_t size = st_mult(n, s);
52
+ char buf[1024];
53
+
54
+ if (!n)
55
+ return 0;
56
+ if (!b || !cmp)
57
+ return -1;
58
+
59
+ if (size < sizeof(buf)) {
60
+ /* The temporary array fits on the small on-stack buffer. */
61
+ msort_with_tmp(b, n, s, cmp, buf, ctx);
62
+ } else {
63
+ /* It's somewhat large, so malloc it. */
64
+ char *tmp = xmalloc(size);
65
+ msort_with_tmp(b, n, s, cmp, tmp, ctx);
66
+ free(tmp);
67
+ }
68
+ return 0;
69
+}
git-compat-util.h
+6
@@ -988,6 +988,12 @@ static inline void sane_qsort(void *base, size_t nmemb, size_t size,
988
qsort(base, nmemb, size, compar);
989
}
990
991
+#ifndef HAVE_ISO_QSORT_S
992
+int git_qsort_s(void *base, size_t nmemb, size_t size,
993
+ int (*compar)(const void *, const void *, void *), void *ctx);
994
+#define qsort_s git_qsort_s
995
+#endif
996
+
997
#ifndef REG_STARTEND
998
#error "Git requires REG_STARTEND support. Compile with NO_REGEX=NeedsStartEnd"
999
#endif