commit fa625e878f3f8f8b2dbc1d8ca0b761fb65326d02 (tree)
parent 6412aebc9025fef3ae191101abd266a2ea5079b1
Author: GasInfinity <me@gasinfinity.dev>
Date: Thu, 1 Jan 2026 13:17:26 +0100
feat(libzigc): add `qsort`
* also remove musl implementation
Diffstat:
5 files changed, 44 insertions(+), 239 deletions(-)
diff --git a/lib/c/stdlib.zig b/lib/c/stdlib.zig
@@ -8,6 +8,9 @@ comptime {
@export(&abs, .{ .name = "abs", .linkage = common.linkage, .visibility = common.visibility });
@export(&labs, .{ .name = "labs", .linkage = common.linkage, .visibility = common.visibility });
@export(&llabs, .{ .name = "llabs", .linkage = common.linkage, .visibility = common.visibility });
+
+ @export(&qsort_r, .{ .name = "qsort_r", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&qsort, .{ .name = "qsort", .linkage = common.linkage, .visibility = common.visibility });
}
}
@@ -23,6 +26,47 @@ fn llabs(a: c_longlong) callconv(.c) c_longlong {
return @intCast(@abs(a));
}
+// NOTE: Despite its name, `qsort` doesn't have to use quicksort or make any complexity or stability guarantee.
+fn qsort_r(base: *anyopaque, n: usize, size: usize, compare: *const fn (a: *const anyopaque, b: *const anyopaque, arg: ?*anyopaque) callconv(.c) c_int, arg: ?*anyopaque) callconv(.c) void {
+ const Context = struct {
+ base: [*]u8,
+ size: usize,
+ compare: *const fn (a: *const anyopaque, b: *const anyopaque, arg: ?*anyopaque) callconv(.c) c_int,
+ arg: ?*anyopaque,
+
+ pub fn lessThan(ctx: @This(), a: usize, b: usize) bool {
+ return ctx.compare(&ctx.base[a * ctx.size], &ctx.base[b * ctx.size], ctx.arg) < 0;
+ }
+
+ pub fn swap(ctx: @This(), a: usize, b: usize) void {
+ const a_bytes: []u8 = ctx.base[a * ctx.size ..][0..ctx.size];
+ const b_bytes: []u8 = ctx.base[b * ctx.size ..][0..ctx.size];
+
+ for (a_bytes, b_bytes) |*ab, *bb| {
+ const tmp = ab.*;
+ ab.* = bb.*;
+ bb.* = tmp;
+ }
+ }
+ };
+
+ std.mem.sortUnstableContext(0, n, Context{
+ .base = @ptrCast(base),
+ .size = size,
+ .compare = compare,
+ .arg = arg,
+ });
+}
+
+fn qsort(base: *anyopaque, n: usize, size: usize, compare: *const fn (a: *const anyopaque, b: *const anyopaque) callconv(.c) c_int) callconv(.c) void {
+ return qsort_r(base, n, size, (struct {
+ fn wrap(a: *const anyopaque, b: *const anyopaque, arg: ?*anyopaque) callconv(.c) c_int {
+ const cmp: *const fn (a: *const anyopaque, b: *const anyopaque) callconv(.c) c_int = @ptrCast(@alignCast(arg.?));
+ return cmp(a, b);
+ }
+ }).wrap, @constCast(compare));
+}
+
test abs {
const val: c_int = -10;
try std.testing.expectEqual(10, abs(val));
diff --git a/lib/libc/musl/src/stdlib/qsort.c b/lib/libc/musl/src/stdlib/qsort.c
@@ -1,221 +0,0 @@
-/* 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. */
-
-#define _BSD_SOURCE
-#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 *, 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, void *arg, 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, 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;
- }
- }
- cycle(width, ar, i);
-}
-
-static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, 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], 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) {
- cycle(width, ar, i);
- sift(head, width, cmp, arg, pshift, lp);
- }
-}
-
-void __qsort_r(void *base, size_t nel, size_t width, cmpfun cmp, void *arg)
-{
- 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, arg, pshift, lp);
- shr(p, 2);
- pshift += 2;
- } else {
- if(lp[pshift - 1] >= high - head) {
- trinkle(head, width, cmp, arg, p, pshift, 0, lp);
- } else {
- sift(head, width, cmp, arg, 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, arg, 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, arg, p, pshift + 1, 1, lp);
- shl(p, 1);
- p[0] |= 1;
- trinkle(head - width, width, cmp, arg, p, pshift, 1, lp);
- }
- head -= width;
- }
-}
-
-weak_alias(__qsort_r, qsort_r);
diff --git a/lib/libc/musl/src/stdlib/qsort_nr.c b/lib/libc/musl/src/stdlib/qsort_nr.c
@@ -1,14 +0,0 @@
-#define _BSD_SOURCE
-#include <stdlib.h>
-
-typedef int (*cmpfun)(const void *, const void *);
-
-static int wrapper_cmp(const void *v1, const void *v2, void *cmp)
-{
- return ((cmpfun)cmp)(v1, v2);
-}
-
-void qsort(void *base, size_t nel, size_t width, cmpfun cmp)
-{
- __qsort_r(base, nel, width, wrapper_cmp, (void *)cmp);
-}
diff --git a/src/libs/musl.zig b/src/libs/musl.zig
@@ -1725,8 +1725,6 @@ const src_files = [_][]const u8{
"musl/src/stdlib/imaxdiv.c",
"musl/src/stdlib/ldiv.c",
"musl/src/stdlib/lldiv.c",
- "musl/src/stdlib/qsort.c",
- "musl/src/stdlib/qsort_nr.c",
"musl/src/stdlib/strtod.c",
"musl/src/stdlib/strtol.c",
"musl/src/stdlib/wcstod.c",
diff --git a/src/libs/wasi_libc.zig b/src/libs/wasi_libc.zig
@@ -1015,8 +1015,6 @@ const libc_top_half_src_files = [_][]const u8{
"musl/src/stdlib/imaxdiv.c",
"musl/src/stdlib/ldiv.c",
"musl/src/stdlib/lldiv.c",
- "musl/src/stdlib/qsort.c",
- "musl/src/stdlib/qsort_nr.c",
"musl/src/stdlib/strtol.c",
"musl/src/string/bcopy.c",
"musl/src/string/explicit_bzero.c",