From: Olivier Galibert <galibert@pobox.com>
To: musl@lists.openwall.com
Cc: Olivier Galibert <galibert@pobox.com>
Subject: [musl] [PATCH] stdlib: implement qsort_r
Date: Sun, 8 Aug 2021 13:26:13 +0200 [thread overview]
Message-ID: <20210808112612.44230-1-galibert@pobox.com> (raw)
The extension qsort_r allows calling qsort on a list of indices
without having a global variable to hold the data to sort.
---
COPYRIGHT | 4 +-
include/stdlib.h | 4 +
src/stdlib/qsort.c | 200 +---------------------------------
src/stdlib/qsort_common.ic | 218 +++++++++++++++++++++++++++++++++++++
src/stdlib/qsort_r.c | 28 +++++
5 files changed, 257 insertions(+), 197 deletions(-)
create mode 100644 src/stdlib/qsort_common.ic
create mode 100644 src/stdlib/qsort_r.c
diff --git a/COPYRIGHT b/COPYRIGHT
index c1628e9a..f5199541 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -142,8 +142,8 @@ originally written by Solar Designer and placed into the public
domain. The code also comes with a fallback permissive license for use
in jurisdictions that may not recognize the public domain.
-The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011
-Valentin Ochs and is licensed under an MIT-style license.
+The smoothsort implementation (src/stdlib/qsort_common.ic) is
+Copyright © 2011 Valentin Ochs and is licensed under an MIT-style license.
The x86_64 port was written by Nicholas J. Kain and is licensed under
the standard MIT terms.
diff --git a/include/stdlib.h b/include/stdlib.h
index b54a051f..cacb61bf 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -55,6 +55,10 @@ int system (const char *);
void *bsearch (const void *, const void *, size_t, size_t, int (*)(const void *, const void *));
void qsort (void *, size_t, size_t, int (*)(const void *, const void *));
+#if defined(_GNU_SOURCE)
+ void qsort_r (void *, size_t, size_t, int (*)(const void *, const void *, void *), void *);
+#endif
+
int abs (int);
long labs (long);
long long llabs (long long);
diff --git a/src/stdlib/qsort.c b/src/stdlib/qsort.c
index da58fd31..b0ade53c 100644
--- a/src/stdlib/qsort.c
+++ b/src/stdlib/qsort.c
@@ -19,200 +19,10 @@
* IN THE SOFTWARE.
*/
-/* Minor changes by Rich Felker for integration in musl, 2011-04-27. */
+/* Instanciate qsort */
-/* Smoothsort, an adaptive variant of Heapsort. Memory usage: O(1).
- Run time: Worst case O(n log n), close to O(n) in the mostly-sorted case. */
+#define FN(function) function
+#define ARG_PROTO
+#define ARG
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "atomic.h"
-#define ntz(x) a_ctz_l((x))
-
-typedef int (*cmpfun)(const void *, const void *);
-
-static inline int pntz(size_t p[2]) {
- int r = ntz(p[0] - 1);
- if(r != 0 || (r = 8*sizeof(size_t) + ntz(p[1])) != 8*sizeof(size_t)) {
- return r;
- }
- return 0;
-}
-
-static void cycle(size_t width, unsigned char* ar[], int n)
-{
- unsigned char tmp[256];
- size_t l;
- int i;
-
- if(n < 2) {
- return;
- }
-
- ar[n] = tmp;
- while(width) {
- l = sizeof(tmp) < width ? sizeof(tmp) : width;
- memcpy(ar[n], ar[0], l);
- for(i = 0; i < n; i++) {
- memcpy(ar[i], ar[i + 1], l);
- ar[i] += l;
- }
- width -= l;
- }
-}
-
-/* shl() and shr() need n > 0 */
-static inline void shl(size_t p[2], int n)
-{
- if(n >= 8 * sizeof(size_t)) {
- n -= 8 * sizeof(size_t);
- p[1] = p[0];
- p[0] = 0;
- }
- p[1] <<= n;
- p[1] |= p[0] >> (sizeof(size_t) * 8 - n);
- p[0] <<= n;
-}
-
-static inline void shr(size_t p[2], int n)
-{
- if(n >= 8 * sizeof(size_t)) {
- n -= 8 * sizeof(size_t);
- p[0] = p[1];
- p[1] = 0;
- }
- p[0] >>= n;
- p[0] |= p[1] << (sizeof(size_t) * 8 - n);
- p[1] >>= n;
-}
-
-static void sift(unsigned char *head, size_t width, cmpfun cmp, int pshift, size_t lp[])
-{
- unsigned char *rt, *lf;
- unsigned char *ar[14 * sizeof(size_t) + 1];
- int i = 1;
-
- ar[0] = head;
- while(pshift > 1) {
- rt = head - width;
- lf = head - width - lp[pshift - 2];
-
- if((*cmp)(ar[0], lf) >= 0 && (*cmp)(ar[0], rt) >= 0) {
- break;
- }
- if((*cmp)(lf, rt) >= 0) {
- ar[i++] = lf;
- head = lf;
- pshift -= 1;
- } else {
- ar[i++] = rt;
- head = rt;
- pshift -= 2;
- }
- }
- cycle(width, ar, i);
-}
-
-static void trinkle(unsigned char *head, size_t width, cmpfun cmp, size_t pp[2], int pshift, int trusty, size_t lp[])
-{
- unsigned char *stepson,
- *rt, *lf;
- size_t p[2];
- unsigned char *ar[14 * sizeof(size_t) + 1];
- int i = 1;
- int trail;
-
- p[0] = pp[0];
- p[1] = pp[1];
-
- ar[0] = head;
- while(p[0] != 1 || p[1] != 0) {
- stepson = head - lp[pshift];
- if((*cmp)(stepson, ar[0]) <= 0) {
- break;
- }
- if(!trusty && pshift > 1) {
- rt = head - width;
- lf = head - width - lp[pshift - 2];
- if((*cmp)(rt, stepson) >= 0 || (*cmp)(lf, stepson) >= 0) {
- break;
- }
- }
-
- ar[i++] = stepson;
- head = stepson;
- trail = pntz(p);
- shr(p, trail);
- pshift += trail;
- trusty = 0;
- }
- if(!trusty) {
- cycle(width, ar, i);
- sift(head, width, cmp, pshift, lp);
- }
-}
-
-void qsort(void *base, size_t nel, size_t width, cmpfun cmp)
-{
- size_t lp[12*sizeof(size_t)];
- size_t i, size = width * nel;
- unsigned char *head, *high;
- size_t p[2] = {1, 0};
- int pshift = 1;
- int trail;
-
- if (!size) return;
-
- head = base;
- high = head + size - width;
-
- /* Precompute Leonardo numbers, scaled by element width */
- for(lp[0]=lp[1]=width, i=2; (lp[i]=lp[i-2]+lp[i-1]+width) < size; i++);
-
- while(head < high) {
- if((p[0] & 3) == 3) {
- sift(head, width, cmp, pshift, lp);
- shr(p, 2);
- pshift += 2;
- } else {
- if(lp[pshift - 1] >= high - head) {
- trinkle(head, width, cmp, p, pshift, 0, lp);
- } else {
- sift(head, width, cmp, pshift, lp);
- }
-
- if(pshift == 1) {
- shl(p, 1);
- pshift = 0;
- } else {
- shl(p, pshift - 1);
- pshift = 1;
- }
- }
-
- p[0] |= 1;
- head += width;
- }
-
- trinkle(head, width, cmp, p, pshift, 0, lp);
-
- while(pshift != 1 || p[0] != 1 || p[1] != 0) {
- if(pshift <= 1) {
- trail = pntz(p);
- shr(p, trail);
- pshift += trail;
- } else {
- shl(p, 2);
- pshift -= 2;
- p[0] ^= 7;
- shr(p, 1);
- trinkle(head - lp[pshift] - width, width, cmp, p, pshift + 1, 1, lp);
- shl(p, 1);
- p[0] |= 1;
- trinkle(head - width, width, cmp, p, pshift, 1, lp);
- }
- head -= width;
- }
-}
+#include "qsort_common.ic"
diff --git a/src/stdlib/qsort_common.ic b/src/stdlib/qsort_common.ic
new file mode 100644
index 00000000..099b1bf4
--- /dev/null
+++ b/src/stdlib/qsort_common.ic
@@ -0,0 +1,218 @@
+/* Copyright (C) 2011 by Valentin Ochs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* Minor changes by Rich Felker for integration in musl, 2011-04-27. */
+
+/* Smoothsort, an adaptive variant of Heapsort. Memory usage: O(1).
+ Run time: Worst case O(n log n), close to O(n) in the mostly-sorted case. */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "atomic.h"
+#define ntz(x) a_ctz_l((x))
+
+typedef int (*cmpfun)(const void *, const void * ARG_PROTO);
+
+static inline int pntz(size_t p[2]) {
+ int r = ntz(p[0] - 1);
+ if(r != 0 || (r = 8*sizeof(size_t) + ntz(p[1])) != 8*sizeof(size_t)) {
+ return r;
+ }
+ return 0;
+}
+
+static void FN(cycle)(size_t width, unsigned char* ar[], int n)
+{
+ unsigned char tmp[256];
+ size_t l;
+ int i;
+
+ if(n < 2) {
+ return;
+ }
+
+ ar[n] = tmp;
+ while(width) {
+ l = sizeof(tmp) < width ? sizeof(tmp) : width;
+ memcpy(ar[n], ar[0], l);
+ for(i = 0; i < n; i++) {
+ memcpy(ar[i], ar[i + 1], l);
+ ar[i] += l;
+ }
+ width -= l;
+ }
+}
+
+/* shl() and shr() need n > 0 */
+static inline void FN(shl)(size_t p[2], int n)
+{
+ if(n >= 8 * sizeof(size_t)) {
+ n -= 8 * sizeof(size_t);
+ p[1] = p[0];
+ p[0] = 0;
+ }
+ p[1] <<= n;
+ p[1] |= p[0] >> (sizeof(size_t) * 8 - n);
+ p[0] <<= n;
+}
+
+static inline void shr(size_t p[2], int n)
+{
+ if(n >= 8 * sizeof(size_t)) {
+ n -= 8 * sizeof(size_t);
+ p[0] = p[1];
+ p[1] = 0;
+ }
+ p[0] >>= n;
+ p[0] |= p[1] << (sizeof(size_t) * 8 - n);
+ p[1] >>= n;
+}
+
+static void FN(sift)(unsigned char *head, size_t width, cmpfun cmp, int pshift, size_t lp[] ARG_PROTO)
+{
+ unsigned char *rt, *lf;
+ unsigned char *ar[14 * sizeof(size_t) + 1];
+ int i = 1;
+
+ ar[0] = head;
+ while(pshift > 1) {
+ rt = head - width;
+ lf = head - width - lp[pshift - 2];
+
+ if((*cmp)(ar[0], lf ARG) >= 0 && (*cmp)(ar[0], rt ARG) >= 0) {
+ break;
+ }
+ if((*cmp)(lf, rt ARG) >= 0) {
+ ar[i++] = lf;
+ head = lf;
+ pshift -= 1;
+ } else {
+ ar[i++] = rt;
+ head = rt;
+ pshift -= 2;
+ }
+ }
+ FN(cycle)(width, ar, i);
+}
+
+static void FN(trinkle)(unsigned char *head, size_t width, cmpfun cmp, size_t pp[2], int pshift, int trusty, size_t lp[] ARG_PROTO)
+{
+ unsigned char *stepson,
+ *rt, *lf;
+ size_t p[2];
+ unsigned char *ar[14 * sizeof(size_t) + 1];
+ int i = 1;
+ int trail;
+
+ p[0] = pp[0];
+ p[1] = pp[1];
+
+ ar[0] = head;
+ while(p[0] != 1 || p[1] != 0) {
+ stepson = head - lp[pshift];
+ if((*cmp)(stepson, ar[0] ARG) <= 0) {
+ break;
+ }
+ if(!trusty && pshift > 1) {
+ rt = head - width;
+ lf = head - width - lp[pshift - 2];
+ if((*cmp)(rt, stepson ARG) >= 0 || (*cmp)(lf, stepson ARG) >= 0) {
+ break;
+ }
+ }
+
+ ar[i++] = stepson;
+ head = stepson;
+ trail = pntz(p);
+ shr(p, trail);
+ pshift += trail;
+ trusty = 0;
+ }
+ if(!trusty) {
+ FN(cycle)(width, ar, i);
+ FN(sift)(head, width, cmp, pshift, lp ARG);
+ }
+}
+
+void FN(qsort)(void *base, size_t nel, size_t width, cmpfun cmp ARG_PROTO)
+{
+ size_t lp[12*sizeof(size_t)];
+ size_t i, size = width * nel;
+ unsigned char *head, *high;
+ size_t p[2] = {1, 0};
+ int pshift = 1;
+ int trail;
+
+ if (!size) return;
+
+ head = base;
+ high = head + size - width;
+
+ /* Precompute Leonardo numbers, scaled by element width */
+ for(lp[0]=lp[1]=width, i=2; (lp[i]=lp[i-2]+lp[i-1]+width) < size; i++);
+
+ while(head < high) {
+ if((p[0] & 3) == 3) {
+ FN(sift)(head, width, cmp, pshift, lp ARG);
+ shr(p, 2);
+ pshift += 2;
+ } else {
+ if(lp[pshift - 1] >= high - head) {
+ FN(trinkle)(head, width, cmp, p, pshift, 0, lp ARG);
+ } else {
+ FN(sift)(head, width, cmp, pshift, lp ARG);
+ }
+
+ if(pshift == 1) {
+ FN(shl)(p, 1);
+ pshift = 0;
+ } else {
+ FN(shl)(p, pshift - 1);
+ pshift = 1;
+ }
+ }
+
+ p[0] |= 1;
+ head += width;
+ }
+
+ FN(trinkle)(head, width, cmp, p, pshift, 0, lp ARG);
+
+ while(pshift != 1 || p[0] != 1 || p[1] != 0) {
+ if(pshift <= 1) {
+ trail = pntz(p);
+ shr(p, trail);
+ pshift += trail;
+ } else {
+ FN(shl)(p, 2);
+ pshift -= 2;
+ p[0] ^= 7;
+ shr(p, 1);
+ FN(trinkle)(head - lp[pshift] - width, width, cmp, p, pshift + 1, 1, lp ARG);
+ FN(shl)(p, 1);
+ p[0] |= 1;
+ FN(trinkle)(head - width, width, cmp, p, pshift, 1, lp ARG);
+ }
+ head -= width;
+ }
+}
diff --git a/src/stdlib/qsort_r.c b/src/stdlib/qsort_r.c
new file mode 100644
index 00000000..d57e4a1e
--- /dev/null
+++ b/src/stdlib/qsort_r.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 2011 by Valentin Ochs
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* Instanciate qsort_r */
+
+#define FN(function) function##_r
+#define ARG_PROTO , void *arg
+#define ARG , arg
+
+#include "qsort_common.ic"
--
2.32.0
next reply other threads:[~2021-08-08 11:29 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-08-08 11:26 Olivier Galibert [this message]
2021-08-08 12:42 ` Jon Chesterfield
2021-08-08 19:39 ` Olivier Galibert
2021-08-08 20:35 ` Érico Nogueira
2021-08-08 21:21 ` Jon Chesterfield
2021-08-08 23:05 ` Rich Felker
2021-08-09 6:03 ` Olivier Galibert
2021-08-09 17:27 ` Florian Weimer
2021-08-08 20:28 ` Érico Nogueira
2021-08-18 17:36 Olivier Galibert
2021-08-18 20:18 ` Rich Felker
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210808112612.44230-1-galibert@pobox.com \
--to=galibert@pobox.com \
--cc=musl@lists.openwall.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.vuxu.org/mirror/musl/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).