Module Name:    src
Committed By:   riastradh
Date:           Sun Mar  2 20:00:32 UTC 2025

Modified Files:
        src/tests/lib/libc/stdlib: h_sort.c t_sort.sh

Log Message:
t_sort: Test mergesort for stability too.

These test cases are trivial, but they're enough to trigger unstable
heapsort and qsort.

Fix some error branches while here.

PR lib/58931: qsort_r() missing


To generate a diff of this commit:
cvs rdiff -u -r1.1 -r1.2 src/tests/lib/libc/stdlib/h_sort.c \
    src/tests/lib/libc/stdlib/t_sort.sh

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/tests/lib/libc/stdlib/h_sort.c
diff -u src/tests/lib/libc/stdlib/h_sort.c:1.1 src/tests/lib/libc/stdlib/h_sort.c:1.2
--- src/tests/lib/libc/stdlib/h_sort.c:1.1	Sun Mar  2 16:35:41 2025
+++ src/tests/lib/libc/stdlib/h_sort.c	Sun Mar  2 20:00:32 2025
@@ -1,4 +1,4 @@
-/*	$NetBSD: h_sort.c,v 1.1 2025/03/02 16:35:41 riastradh Exp $	*/
+/*	$NetBSD: h_sort.c,v 1.2 2025/03/02 20:00:32 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2025 The NetBSD Foundation, Inc.
@@ -27,9 +27,11 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: h_sort.c,v 1.1 2025/03/02 16:35:41 riastradh Exp $");
+__RCSID("$NetBSD: h_sort.c,v 1.2 2025/03/02 20:00:32 riastradh Exp $");
 
+#include <assert.h>
 #include <err.h>
+#include <getopt.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -53,25 +55,41 @@ mergesort_r_wrapper(void *a, size_t n, s
 		err(1, "mergesort_r");
 }
 
+struct context {
+	const char	*buf;
+	const size_t	*linepos;
+};
+
 static int
 cmp(const void *va, const void *vb, void *cookie)
 {
-	const char *buf = cookie;
+	const struct context *C = cookie;
 	const size_t *a = va;
 	const size_t *b = vb;
 
-	return strcmp(buf + *a, buf + *b);
+	return strcmp(C->buf + C->linepos[*a], C->buf + C->linepos[*b]);
+}
+
+static void __dead
+usage(void)
+{
+
+	fprintf(stderr, "Usage: %s [-n] <sortfn>\n", getprogname());
+	exit(1);
 }
 
 int
 main(int argc, char **argv)
 {
+	int ch;
+	int nflag = 0;
 	void (*sortfn)(void *, size_t, size_t,
 	    int (*)(const void *, const void *, void *), void *);
 	char *buf = NULL;
 	size_t nbuf;
-	size_t *lines = NULL;
+	size_t *linepos = NULL;
 	size_t nlines;
+	size_t *permutation = NULL;
 	size_t off;
 	ssize_t nread;
 	char *p;
@@ -82,16 +100,29 @@ main(int argc, char **argv)
 	 * Parse arguments.
 	 */
 	setprogname(argv[0]);
-	if (argc < 2)
-		errx(1, "Usage: %s <sortfn>", getprogname());
-	if (strcmp(argv[1], "heapsort_r") == 0)
+	while ((ch = getopt(argc, argv, "hn")) != -1) {
+		switch (ch) {
+		case 'n':
+			nflag = 1;
+			break;
+		case '?':
+		case 'h':
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	if (argc != 1)
+		usage();
+	if (strcmp(argv[0], "heapsort_r") == 0)
 		sortfn = &heapsort_r_wrapper;
-	else if (strcmp(argv[1], "mergesort_r") == 0)
+	else if (strcmp(argv[0], "mergesort_r") == 0)
 		sortfn = &mergesort_r_wrapper;
-	else if (strcmp(argv[1], "qsort_r") == 0)
+	else if (strcmp(argv[0], "qsort_r") == 0)
 		sortfn = &qsort_r;
 	else
-		errx(1, "unknown sort: %s", argv[1]);
+		errx(1, "unknown sort: %s", argv[0]);
 
 	/*
 	 * Allocate an initial 4K buffer.
@@ -99,7 +130,7 @@ main(int argc, char **argv)
 	nbuf = 0x1000;
 	error = reallocarr(&buf, nbuf, 1);
 	if (error)
-		err(1, "reallocarr");
+		errc(1, error, "reallocarr");
 
 	/*
 	 * Read the input into a contiguous buffer.  Reject input with
@@ -125,7 +156,7 @@ main(int argc, char **argv)
 			nbuf *= 2;
 			error = reallocarr(&buf, nbuf, 1);
 			if (error)
-				err(1, "reallocarr");
+				errc(1, error, "reallocarr");
 		}
 	}
 
@@ -148,30 +179,47 @@ main(int argc, char **argv)
 	}
 
 	/*
-	 * Create an array of line offsets to sort.  NUL-terminate each
-	 * line so we can use strcmp(3).
+	 * Create an array of line positions to sort.  NUL-terminate
+	 * each line so we can use strcmp(3).
 	 */
-	error = reallocarr(&lines, nlines, sizeof(lines[0]));
-	if (lines == NULL)
-		err(1, "reallocarr");
+	error = reallocarr(&linepos, nlines, sizeof(linepos[0]));
+	if (error)
+		errc(1, error, "reallocarr");
 	i = 0;
-	for (p = buf; lines[i++] = p - buf, (p = strchr(p, '\n')) != NULL;) {
+	for (p = buf; linepos[i++] = p - buf, (p = strchr(p, '\n')) != NULL;) {
 		*p = '\0';
 		if (*++p == '\0')
 			break;
 	}
+	assert(i == nlines);
 
 	/*
-	 * Sort the array of line offsets via comparison function that
-	 * consults the buffer as a cookie.
+	 * Create an array of permuted line numbers.
 	 */
-	(*sortfn)(lines, nlines, sizeof(lines[0]), &cmp, buf);
+	error = reallocarr(&permutation, nlines, sizeof(permutation[0]));
+	if (error)
+		errc(1, error, "reallocarr");
+	for (i = 0; i < nlines; i++)
+		permutation[i] = i;
 
 	/*
-	 * Print the lines in sorted order.
+	 * Sort the lines via comparison function that consults the
+	 * buffer as a cookie.
 	 */
-	for (i = 0; i < nlines; i++)
-		printf("%s\n", buf + lines[i]);
+	(*sortfn)(permutation, nlines, sizeof(permutation[0]), &cmp,
+	    &(struct context) { .buf = buf, .linepos = linepos });
+
+	/*
+	 * Print the lines in sorted order with the original line
+	 * numbers.
+	 */
+	for (i = 0; i < nlines; i++) {
+		const size_t j = permutation[i];
+		if (nflag)
+			printf("%zu %s\n", j, buf + linepos[j]);
+		else
+			printf("%s\n", buf + linepos[j]);
+	}
 	fflush(stdout);
 	return ferror(stdout);
 }
Index: src/tests/lib/libc/stdlib/t_sort.sh
diff -u src/tests/lib/libc/stdlib/t_sort.sh:1.1 src/tests/lib/libc/stdlib/t_sort.sh:1.2
--- src/tests/lib/libc/stdlib/t_sort.sh:1.1	Sun Mar  2 16:35:41 2025
+++ src/tests/lib/libc/stdlib/t_sort.sh	Sun Mar  2 20:00:32 2025
@@ -1,4 +1,4 @@
-#	$NetBSD: t_sort.sh,v 1.1 2025/03/02 16:35:41 riastradh Exp $
+#	$NetBSD: t_sort.sh,v 1.2 2025/03/02 20:00:32 riastradh Exp $
 #
 # Copyright (c) 2025 The NetBSD Foundation, Inc.
 # All rights reserved.
@@ -28,19 +28,61 @@ check_sort()
 {
 	local sortfn
 
-	set -Ceu
+	set -eu
 
 	sortfn="$1"
 
 	printf 'foo\nbar\nbaz\nquux' >in1
-	printf 'bar\nbaz\nfoo\nquux\n' >out
-	atf_check -s exit:0 -o file:out \
-		"$(atf_get_srcdir)"/h_sort "$sortfn" <in1
+	printf '1 bar\n2 baz\n0 foo\n3 quux\n' >out1
+	atf_check -s exit:0 -o file:out1 \
+		"$(atf_get_srcdir)"/h_sort -n "$sortfn" <in1
+
 	atf_check -s exit:0 -o empty sh -c 'exec shuffle -f - <in1 >in2'
-	atf_check -s exit:0 -o file:out \
+	printf 'bar\nbaz\nfoo\nquux\n' >out2
+	atf_check -s exit:0 -o file:out2 \
 		"$(atf_get_srcdir)"/h_sort "$sortfn" <in2
 }
 
+check_stablesort()
+{
+	local sortfn
+
+	set -eu
+
+	sortfn="$1"
+
+	printf 'foo\nfoo\nfoo\nfoo\nfoo' >in1
+	printf '0 foo\n1 foo\n2 foo\n3 foo\n4 foo\n' >out1
+	atf_check -s exit:0 -o file:out1 \
+		"$(atf_get_srcdir)"/h_sort -n "$sortfn" <in1
+
+	printf 'foo\nfoo\nfoo\nfoo\nfoo\nbar\nbar\nbar\nbar\nbar' >in2
+	printf '5 bar\n6 bar\n7 bar\n8 bar\n9 bar\n' >out2
+	printf '0 foo\n1 foo\n2 foo\n3 foo\n4 foo\n' >>out2
+	atf_check -s exit:0 -o file:out2 \
+		"$(atf_get_srcdir)"/h_sort -n "$sortfn" <in2
+
+	printf 'foo\nfoo\nbar\nbaz\nquux' >in3
+	printf '2 bar\n3 baz\n0 foo\n1 foo\n4 quux\n' >out3
+	atf_check -s exit:0 -o file:out3 \
+		"$(atf_get_srcdir)"/h_sort -n "$sortfn" <in3
+
+	printf 'foo\nbar\nbar\nbaz\nquux' >in4
+	printf '1 bar\n2 bar\n3 baz\n0 foo\n4 quux\n' >out4
+	atf_check -s exit:0 -o file:out4 \
+		"$(atf_get_srcdir)"/h_sort -n "$sortfn" <in4
+
+	printf 'foo\nbar\nbaz\nbaz\nquux' >in5
+	printf '1 bar\n2 baz\n3 baz\n0 foo\n4 quux\n' >out5
+	atf_check -s exit:0 -o file:out5 \
+		"$(atf_get_srcdir)"/h_sort -n "$sortfn" <in5
+
+	printf 'foo\nbar\nbaz\nquux\nquux' >in6
+	printf '1 bar\n2 baz\n0 foo\n3 quux\n4 quux\n' >out6
+	atf_check -s exit:0 -o file:out6 \
+		"$(atf_get_srcdir)"/h_sort -n "$sortfn" <in6
+}
+
 sortfn_case()
 {
 	local sortfn
@@ -52,10 +94,22 @@ sortfn_case()
 	atf_add_test_case "$sortfn"
 }
 
+stablesortfn_case()
+{
+	local sortfn
+
+	sortfn="$1"
+
+	eval "${sortfn}_stable_head() { atf_set descr \"Test ${sortfn}\"; }"
+	eval "${sortfn}_stable_body() { check_stablesort $sortfn; }"
+	atf_add_test_case "${sortfn}_stable"
+}
+
 atf_init_test_cases()
 {
 
 	sortfn_case heapsort_r
 	sortfn_case mergesort_r
 	sortfn_case qsort_r
+	stablesortfn_case mergesort_r
 }

Reply via email to