From fa625e878f3f8f8b2dbc1d8ca0b761fb65326d02 Mon Sep 17 00:00:00 2001 From: GasInfinity Date: Thu, 1 Jan 2026 13:17:26 +0100 Subject: [PATCH] feat(libzigc): add `qsort` * also remove musl implementation --- lib/c/stdlib.zig | 44 ++++++ lib/libc/musl/src/stdlib/qsort.c | 221 ---------------------------- lib/libc/musl/src/stdlib/qsort_nr.c | 14 -- src/libs/musl.zig | 2 - src/libs/wasi_libc.zig | 2 - 5 files changed, 44 insertions(+), 239 deletions(-) delete mode 100644 lib/libc/musl/src/stdlib/qsort.c delete mode 100644 lib/libc/musl/src/stdlib/qsort_nr.c diff --git a/lib/c/stdlib.zig b/lib/c/stdlib.zig index e427771640..f764f166cb 100644 --- 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 deleted file mode 100644 index 314ddc29da..0000000000 --- a/lib/libc/musl/src/stdlib/qsort.c +++ /dev/null @@ -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 -#include -#include - -#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 deleted file mode 100644 index 8ffe71d038..0000000000 --- a/lib/libc/musl/src/stdlib/qsort_nr.c +++ /dev/null @@ -1,14 +0,0 @@ -#define _BSD_SOURCE -#include - -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 index 744f1f28d2..e1a41aa02b 100644 --- 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 index 7edb2063a5..789dfa9c30 100644 --- 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",