The branch main has been updated by kfv:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=d59c7ea2701fe7b73b32eef49a7c712ef38de5a0

commit d59c7ea2701fe7b73b32eef49a7c712ef38de5a0
Author:     Faraz Vahedi <[email protected]>
AuthorDate: 2026-08-22 15:01:27 +0000
Commit:     Faraz Vahedi <[email protected]>
CommitDate: 2026-08-22 15:01:27 +0000

    libc: Implement bsearch_s(), document bsearch_b(), and add unit tests
    
    - Implement bsearch_s() as per §K.3.6.3.2 in C23, first specified
      in C11.  It behaves identically to bsearch(), except the callback
      is called with a third argument, context, which is passed through
      from the caller, and it also performs runtime constraint checking
      on its arguments.
    - Document bsearch_b(), bsearch_s(), and add history section
    - Add rudimentary unit tests for bsearch(), bsearch_b(), and bsearch_s()
    
    Reviewed by:    dteske, fuz
    Approved by:    dteske (mentor), fuz (mentor)
    MFC after:      1 month
    Differential Revision:  https://reviews.freebsd.org/D58876
---
 include/stdlib.h                       |  10 +++
 lib/libc/stdlib/Makefile.inc           |   3 +
 lib/libc/stdlib/Symbol.map             |   1 +
 lib/libc/stdlib/bsearch.3              | 108 ++++++++++++++++++++++++--
 lib/libc/stdlib/bsearch.c              |  38 ++++++++++
 lib/libc/stdlib/bsearch_s.c            |   7 ++
 lib/libc/tests/stdlib/Makefile         |   7 +-
 lib/libc/tests/stdlib/bsearch_b_test.c |  58 ++++++++++++++
 lib/libc/tests/stdlib/bsearch_s_test.c | 134 +++++++++++++++++++++++++++++++++
 lib/libc/tests/stdlib/bsearch_test.c   |  92 ++++++++++++++++++++++
 lib/libc/tests/stdlib/test-search.h    |  51 +++++++++++++
 11 files changed, 503 insertions(+), 6 deletions(-)

diff --git a/include/stdlib.h b/include/stdlib.h
index d61e2de99693..a384956eb435 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -411,6 +411,9 @@ _Noreturn void abort_handler_s(const char * __restrict, 
void * __restrict,
 /* K3.6.1.3 */
 void ignore_handler_s(const char * __restrict, void * __restrict, errno_t);
 /* K.3.6.3.2 */
+void   *bsearch_s(const void *, const void *, rsize_t, rsize_t,
+    int (*)(const void *, const void *, void *), void *);
+/* K.3.6.3.3 */
 errno_t         qsort_s(void *, rsize_t, rsize_t,
     int (*)(const void *, const void *, void *), void *);
 #endif /* __EXT1_VISIBLE */
@@ -428,6 +431,13 @@ __NULLABILITY_PRAGMA_POP
        (const void *)(bsearch_b)((key), (base), (nmemb), (size), (compar)), \
        (bsearch_b)((key), (base), (nmemb), (size), (compar)))
 #endif
+#if __EXT1_VISIBLE
+#define        bsearch_s(key, base, nmemb, size, compar, context)              
\
+       __qualsel((base),                                               \
+       (const void *)(bsearch_s)((key), (base), (nmemb), (size),       \
+           (compar), (context)),                                       \
+       (bsearch_s)((key), (base), (nmemb), (size), (compar), (context)))
+#endif
 #endif
 
 #endif /* !_STDLIB_H_ */
diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc
index 7ff6a6c46ea5..497f163acf96 100644
--- a/lib/libc/stdlib/Makefile.inc
+++ b/lib/libc/stdlib/Makefile.inc
@@ -13,6 +13,7 @@ MISRCS+= \
        atoll.c \
        bsearch.c \
        bsearch_b.c \
+       bsearch_s.c \
        cxa_thread_atexit.c \
        cxa_thread_atexit_impl.c \
        div.c \
@@ -109,6 +110,8 @@ MLINKS+=abs.3 labs.3 \
        abs.3 llabs.3 \
        abs.3 imaxabs.3
 MLINKS+=atol.3 atoll.3
+MLINKS+=bsearch.3 bsearch_b.3 \
+       bsearch.3 bsearch_s.3
 MLINKS+=div.3 ldiv.3 \
        div.3 lldiv.3 \
        div.3 imaxdiv.3
diff --git a/lib/libc/stdlib/Symbol.map b/lib/libc/stdlib/Symbol.map
index df4922f1df60..be063fea8707 100644
--- a/lib/libc/stdlib/Symbol.map
+++ b/lib/libc/stdlib/Symbol.map
@@ -132,6 +132,7 @@ FBSD_1.8 {
 };
 
 FBSD_1.9 {
+       bsearch_s;
        memalignment;
        recallocarray;
        strfromd;
diff --git a/lib/libc/stdlib/bsearch.3 b/lib/libc/stdlib/bsearch.3
index 4d1e7b1171b9..814b71028af0 100644
--- a/lib/libc/stdlib/bsearch.3
+++ b/lib/libc/stdlib/bsearch.3
@@ -29,11 +29,13 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd June 21, 2026
+.Dd August 15, 2026
 .Dt BSEARCH 3
 .Os
 .Sh NAME
-.Nm bsearch
+.Nm bsearch ,
+.Nm bsearch_b ,
+.Nm bsearch_s
 .Nd binary search of a sorted table
 .Sh LIBRARY
 .Lb libc
@@ -41,6 +43,11 @@
 .In stdlib.h
 .Ft "QVoid *"
 .Fn bsearch "const void *key" "QVoid *base" "size_t nmemb" "size_t size" "int 
(*compar) (const void *, const void *)"
+.Ft "QVoid *"
+.Fn bsearch_b "const void *key" "QVoid *base" "size_t nmemb" "size_t size" 
"int (^compar) (const void *, const void *)"
+.Fd #define __STDC_WANT_LIB_EXT1__ 1
+.Ft "QVoid *"
+.Fn bsearch_s "const void *key" "QVoid *base" "rsize_t nmemb" "rsize_t size" 
"int (*compar) (const void *, const void *, void *)" "void *context"
 .Sh DESCRIPTION
 The
 .Fn bsearch
@@ -74,11 +81,68 @@ sample function in
 .Xr qsort 3
 for a comparison function that is also compatible with
 .Fn bsearch .
+.Pp
+The
+.Fn bsearch_b
+function behaves identically to
+.Fn bsearch ,
+except the callback
+.Fa compar
+takes a block pointer instead of a function pointer.
+.Pp
+The
+.Fn bsearch_s
+function behaves identically to
+.Fn bsearch ,
+except the callback
+.Fa compar
+is called with a third argument,
+.Fa context ,
+which is passed through from the caller.
+Runtime-constraint violation occurs if:
+.Bl -bullet
+.It
+.Fa nmemb
+or
+.Fa size
+is greater than
+.Dv RSIZE_MAX
+.It
+.Fa nmemb
+is not zero and any of
+.Fa key ,
+.Fa base ,
+or
+.Fa compar
+is a null pointer
+.El
+.Pp
+On a runtime-constraint violation, the runtime-constraint handler is
+invoked,
+.Fn bsearch_s
+does not search the array, and a null pointer is returned.
+Note that the handler is called before
+.Fn bsearch_s
+returns, and the handler function might not return.
+If
+.Fa nmemb
+is zero, the comparison function is not called, no match is found,
+and
+.Fa key ,
+.Fa base ,
+and
+.Fa compar
+may be null pointers.
 .Sh RETURN VALUES
 The
-.Fn bsearch
-function returns a pointer to a matching member of the array, or a null
+.Fn bsearch ,
+.Fn bsearch_b ,
+and
+.Fn bsearch_s
+functions return a pointer to a matching member of the array, or a null
 pointer if no match is found.
+.Fn bsearch_s
+also returns a null pointer if there is a runtime-constraint violation.
 If two members compare as equal, which member is matched is unspecified.
 .Sh EXAMPLES
 A sample program that searches people by age in a sorted array:
@@ -147,7 +211,8 @@ main(void)
 .Sh SEE ALSO
 .Xr db 3 ,
 .Xr lsearch 3 ,
-.Xr qsort 3
+.Xr qsort 3 ,
+.Xr set_constraint_handler_s 3
 .\" .Xr tsearch 3
 .Sh STANDARDS
 The
@@ -155,3 +220,36 @@ The
 function conforms to
 .St -isoC-2023 ,
 where it is specified as a qualifier-preserving function.
+.Pp
+The
+.Fn bsearch_s
+function conforms to
+.St -isoC-2023 ,
+section K.3.6.3.2.
+Like
+.Fn bsearch ,
+it is specified as a qualifier-preserving function.
+.Sh HISTORY
+The
+.Fn bsearch ,
+initially specified in the
+.St -svid1 ,
+first appeared in
+.At V.2
+and later in
+.Bx 4.3 .
+It was first standardized in
+.St -isoC .
+.Pp
+The
+.Fn bsearch_b
+function first appeared in Mac OS X and was later added to
+.Fx 11.0
+as an extension.
+.Pp
+The
+.Fn bsearch_s
+function first appeared in
+.Fx 16.0 .
+It was first standardized in
+.St -isoC-2011 .
diff --git a/lib/libc/stdlib/bsearch.c b/lib/libc/stdlib/bsearch.c
index 96c728e1c997..6bfd44d4df6d 100644
--- a/lib/libc/stdlib/bsearch.c
+++ b/lib/libc/stdlib/bsearch.c
@@ -32,10 +32,18 @@
 #include <stddef.h>
 #include <stdlib.h>
 
+#ifdef I_AM_BSEARCH_S
+#include <errno.h>
+#include <stdint.h>
+#include "libc_private.h"
+#endif
+
 #ifdef I_AM_BSEARCH_B
 #include "block_abi.h"
 #define        COMPAR(x,y)     CALL_BLOCK(compar, x, y)
 typedef DECLARE_BLOCK(int, compar_block, const void *, const void *);
+#elif defined(I_AM_BSEARCH_S)
+#define        COMPAR(x,y)     compar((x), (y), context)
 #else
 #define        COMPAR(x,y)     compar(x, y)
 #endif
@@ -60,6 +68,10 @@ typedef DECLARE_BLOCK(int, compar_block, const void *, const 
void *);
 void *
 bsearch_b(const void *key, const void *base0, size_t nmemb, size_t size,
     compar_block compar)
+#elif defined(I_AM_BSEARCH_S)
+void *
+bsearch_s(const void *key, const void *base0, rsize_t nmemb, rsize_t size,
+    int (*compar)(const void *, const void *, void *), void *context)
 #else
 void *
 bsearch(const void *key, const void *base0, size_t nmemb, size_t size,
@@ -71,6 +83,32 @@ bsearch(const void *key, const void *base0, size_t nmemb, 
size_t size,
        int cmp;
        const void *p;
 
+#ifdef I_AM_BSEARCH_S
+       if (nmemb > RSIZE_MAX) {
+               __throw_constraint_handler_s("bsearch_s : nmemb > RSIZE_MAX",
+                   EINVAL);
+               return (NULL);
+       } else if (size > RSIZE_MAX) {
+               __throw_constraint_handler_s("bsearch_s : size > RSIZE_MAX",
+                   EINVAL);
+               return (NULL);
+       } else if (nmemb != 0) {
+               if (key == NULL) {
+                       __throw_constraint_handler_s("bsearch_s : key == NULL",
+                           EINVAL);
+                       return (NULL);
+               } else if (base0 == NULL) {
+                       __throw_constraint_handler_s("bsearch_s : base == NULL",
+                           EINVAL);
+                       return (NULL);
+               } else if (compar == NULL) {
+                       __throw_constraint_handler_s("bsearch_s : compar == 
NULL",
+                           EINVAL);
+                       return (NULL);
+               }
+       }
+#endif
+
        for (lim = nmemb; lim != 0; lim >>= 1) {
                p = base + (lim >> 1) * size;
                cmp = COMPAR(key, p);
diff --git a/lib/libc/stdlib/bsearch_s.c b/lib/libc/stdlib/bsearch_s.c
new file mode 100644
index 000000000000..03722ffe6370
--- /dev/null
+++ b/lib/libc/stdlib/bsearch_s.c
@@ -0,0 +1,7 @@
+/*
+ * Copyright (c) 2026 Faraz Vahedi <[email protected]>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#define I_AM_BSEARCH_S
+#include "bsearch.c"
diff --git a/lib/libc/tests/stdlib/Makefile b/lib/libc/tests/stdlib/Makefile
index 1a0206bcdab9..00836dd0259c 100644
--- a/lib/libc/tests/stdlib/Makefile
+++ b/lib/libc/tests/stdlib/Makefile
@@ -1,5 +1,10 @@
 .include <src.opts.mk>
 
+ATF_TESTS_C+=          bsearch_test
+.if ${COMPILER_FEATURES:Mblocks}
+ATF_TESTS_C+=          bsearch_b_test
+.endif
+ATF_TESTS_C+=          bsearch_s_test
 ATF_TESTS_C+=          clearenv_test
 ATF_TESTS_C+=          cxa_atexit_test
 ATF_TESTS_C+=          dynthr_test
@@ -66,7 +71,7 @@ CFLAGS+=      -I${.CURDIR}
 LIBADD.cxa_thread_atexit_test+=                pthread
 
 # Tests that require blocks support
-.for t in qsort_b_test
+.for t in bsearch_b_test qsort_b_test
 CFLAGS.${t}.c+=                -fblocks
 LIBADD.${t}+=          BlocksRuntime
 .endfor
diff --git a/lib/libc/tests/stdlib/bsearch_b_test.c 
b/lib/libc/tests/stdlib/bsearch_b_test.c
new file mode 100644
index 000000000000..4c04a723f639
--- /dev/null
+++ b/lib/libc/tests/stdlib/bsearch_b_test.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2026 Faraz Vahedi <[email protected]>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * Test for bsearch_b() routine.
+ */
+
+#include <stdlib.h>
+
+#include "test-search.h"
+
+#define        THUNK 42
+
+static void *
+do_bsearch_b(const int *key, const int *base, size_t n, void *ctx)
+{
+       int thunk = *(int *)ctx;
+
+       return (bsearch_b(key, base, n, sizeof(int),
+           ^(const void *a, const void *b) {
+               ATF_REQUIRE_EQ(thunk, THUNK);
+               return (searchhelp(a, b));
+           }));
+}
+
+ATF_TC_WITHOUT_HEAD(bsearch_b_test);
+ATF_TC_BODY(bsearch_b_test, tc)
+{
+       int testvector[SVEC_LEN];
+       int thunk = THUNK;
+       int key, j;
+
+       for (j = 0; j <= SVEC_LEN; j++) {
+               if (j == 0) {
+                       key = 0;
+                       ATF_CHECK(bsearch_b(&key, testvector, 0,
+                           sizeof(testvector[0]),
+                           ^(const void *a __unused, const void *b __unused) {
+                               atf_tc_fail(
+                                   "comparison block invoked unexpectedly");
+                               return (0);
+                           }) == NULL);
+                       continue;
+               }
+               check_sorted_search(do_bsearch_b, &thunk, testvector,
+                   (size_t)j);
+       }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, bsearch_b_test);
+
+       return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/bsearch_s_test.c 
b/lib/libc/tests/stdlib/bsearch_s_test.c
new file mode 100644
index 000000000000..b41343400ba3
--- /dev/null
+++ b/lib/libc/tests/stdlib/bsearch_s_test.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2026 Faraz Vahedi <[email protected]>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * Test for bsearch_s() routine.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define        THUNK 42
+
+#include "test-search.h"
+
+static errno_t error_code;
+static int compar_calls;
+
+static int
+searchhelp_s(const void *a, const void *b, void *thunk)
+{
+       compar_calls++;
+       if (thunk != NULL)
+               ATF_REQUIRE_EQ(*(int *)thunk, THUNK);
+       return (searchhelp(a, b));
+}
+
+static void
+constraint_handler(const char * restrict msg __unused,
+    void * restrict ptr __unused, errno_t error)
+{
+       error_code = error;
+}
+
+static void
+expect_viol(const void *key, const void *base, rsize_t nmemb, rsize_t size,
+    int (*compar)(const void *, const void *, void *), void *thunk)
+{
+       error_code = 0;
+       compar_calls = 0;
+       ATF_CHECK(bsearch_s(key, base, nmemb, size, compar, thunk) == NULL);
+       ATF_CHECK(error_code > 0);
+       ATF_CHECK_EQ(compar_calls, 0);
+}
+
+static void *
+do_bsearch_s(const int *key, const int *base, size_t n, void *ctx)
+{
+       return (bsearch_s(key, base, n, sizeof(int), searchhelp_s, ctx));
+}
+
+ATF_TC_WITHOUT_HEAD(bsearch_s_constraints);
+ATF_TC_BODY(bsearch_s_constraints, tc)
+{
+       int thunk = THUNK;
+       int key = 4;
+       int b[] = { 4, 7, 81 };
+
+       set_constraint_handler_s(constraint_handler);
+       expect_viol(&key, b, -1, sizeof(int), searchhelp_s, &thunk);
+       expect_viol(&key, b, RSIZE_MAX + 1, sizeof(int), searchhelp_s, &thunk);
+       expect_viol(&key, b, nitems(b), -1, searchhelp_s, &thunk);
+       expect_viol(&key, b, nitems(b), RSIZE_MAX + 1, searchhelp_s, &thunk);
+       expect_viol(NULL, b, nitems(b), sizeof(int), searchhelp_s, &thunk);
+       expect_viol(&key, NULL, 1, sizeof(int), searchhelp_s, &thunk);
+       expect_viol(&key, b, nitems(b), sizeof(int), NULL, &thunk);
+       /* size > RSIZE_MAX is a violation even when nmemb is zero. */
+       expect_viol(&key, b, 0, RSIZE_MAX + 1, searchhelp_s, &thunk);
+}
+
+ATF_TC_WITHOUT_HEAD(bsearch_s_nmemb_zero);
+ATF_TC_BODY(bsearch_s_nmemb_zero, tc)
+{
+       int thunk = THUNK;
+       int key = 4;
+       int b[] = { 4, 7, 81 };
+
+       error_code = 0;
+       compar_calls = 0;
+       set_constraint_handler_s(constraint_handler);
+       ATF_CHECK(bsearch_s(&key, b, 0, sizeof(int), searchhelp_s,
+           &thunk) == NULL);
+       ATF_CHECK(error_code == 0);
+       ATF_CHECK_EQ(compar_calls, 0);
+       ATF_CHECK(bsearch_s(NULL, NULL, 0, 0, NULL, NULL) == NULL);
+       ATF_CHECK(error_code == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(bsearch_s_h);
+ATF_TC_BODY(bsearch_s_h, tc)
+{
+       int thunk = THUNK;
+       int b[] = { 4, 7, 81 };
+       int key = 7;
+       int *found;
+
+       error_code = 0;
+       compar_calls = 0;
+       set_constraint_handler_s(constraint_handler);
+       found = bsearch_s(&key, b, nitems(b), sizeof(int), searchhelp_s,
+           &thunk);
+       ATF_CHECK(error_code == 0);
+       ATF_CHECK(compar_calls > 0);
+       ATF_CHECK(found == &b[1]);
+
+       compar_calls = 0;
+       found = bsearch_s(&key, b, nitems(b), sizeof(int), searchhelp_s, NULL);
+       ATF_CHECK(found == &b[1]);
+       ATF_CHECK(compar_calls > 0);
+}
+
+ATF_TC_WITHOUT_HEAD(bsearch_s_test);
+ATF_TC_BODY(bsearch_s_test, tc)
+{
+       int testvector[SVEC_LEN];
+       int thunk = THUNK;
+       int j;
+
+       for (j = 1; j <= SVEC_LEN; j++)
+               check_sorted_search(do_bsearch_s, &thunk, testvector,
+                   (size_t)j);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, bsearch_s_constraints);
+       ATF_TP_ADD_TC(tp, bsearch_s_nmemb_zero);
+       ATF_TP_ADD_TC(tp, bsearch_s_h);
+       ATF_TP_ADD_TC(tp, bsearch_s_test);
+
+       return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/bsearch_test.c 
b/lib/libc/tests/stdlib/bsearch_test.c
new file mode 100644
index 000000000000..6da8096b85a9
--- /dev/null
+++ b/lib/libc/tests/stdlib/bsearch_test.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2026 Faraz Vahedi <[email protected]>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * Test for bsearch() routine.
+ */
+
+#include <stdlib.h>
+
+#include "test-search.h"
+
+static const void *expected_key;
+static const void *expected_base;
+static size_t expected_nmemb;
+
+static int
+searchhelp_check(const void *a, const void *b)
+{
+       const char *p, *base;
+
+       ATF_CHECK(a == expected_key);
+       p = b;
+       base = expected_base;
+       ATF_CHECK(((size_t)(p - base) % sizeof(int)) == 0);
+       ATF_CHECK(p >= base);
+       ATF_CHECK(p < base + expected_nmemb * sizeof(int));
+       return (searchhelp(a, b));
+}
+
+static int
+searchhelp_never(const void *a __unused, const void *b __unused)
+{
+       atf_tc_fail("comparison function invoked unexpectedly");
+       return (0);
+}
+
+static void *
+do_bsearch(const int *key, const int *base, size_t n, void *ctx __unused)
+{
+       expected_key = key;
+       return (bsearch(key, base, n, sizeof(int), searchhelp_check));
+}
+
+ATF_TC_WITHOUT_HEAD(bsearch_test);
+ATF_TC_BODY(bsearch_test, tc)
+{
+       int testvector[SVEC_LEN];
+       int key, j;
+
+       for (j = 0; j <= SVEC_LEN; j++) {
+               if (j == 0) {
+                       key = 0;
+                       ATF_CHECK(bsearch(&key, testvector, 0,
+                           sizeof(testvector[0]), searchhelp_never) == NULL);
+                       continue;
+               }
+               expected_base = testvector;
+               expected_nmemb = (size_t)j;
+               check_sorted_search(do_bsearch, NULL, testvector, (size_t)j);
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(bsearch_duplicates);
+ATF_TC_BODY(bsearch_duplicates, tc)
+{
+       int d[] = { 1, 2, 2, 2, 3 };
+       int e[] = { 7, 7, 7 };
+       int key, *found;
+
+       key = 2;
+       found = bsearch(&key, d, nitems(d), sizeof(d[0]), searchhelp);
+       ATF_REQUIRE(found != NULL);
+       ATF_CHECK(found >= &d[1] && found <= &d[3]);
+       ATF_CHECK_EQ(*found, 2);
+
+       key = 7;
+       found = bsearch(&key, e, nitems(e), sizeof(e[0]), searchhelp);
+       ATF_REQUIRE(found != NULL);
+       ATF_CHECK(found >= &e[0] && found <= &e[2]);
+       ATF_CHECK_EQ(*found, 7);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, bsearch_test);
+       ATF_TP_ADD_TC(tp, bsearch_duplicates);
+
+       return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/test-search.h 
b/lib/libc/tests/stdlib/test-search.h
new file mode 100644
index 000000000000..3d805585eaae
--- /dev/null
+++ b/lib/libc/tests/stdlib/test-search.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2026 Faraz Vahedi <[email protected]>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _TEST_SEARCH_H
+#define _TEST_SEARCH_H
+
+#include <sys/param.h>
+
+#include <stddef.h>
+
+#include <atf-c.h>
+
+#define        SVEC_LEN        1024
+
+typedef void *search_int_t(const int *, const int *, size_t, void *);
+
+static int
+searchhelp(const void *a, const void *b)
+{
+       const int *oa = a, *ob = b;
+
+       return ((*oa > *ob) - (*oa < *ob));
+}
+
+/*
+ * Fill v[i] = i, then confirm every element is found and -1 and n are not.
+ */
+static void
+check_sorted_search(search_int_t *search, void *ctx, int *v, size_t n)
+{
+       size_t i;
+       int key;
+
+       for (i = 0; i < n; i++)
+               v[i] = (int)i;
+       for (i = 0; i < n; i++) {
+               key = v[i];
+               ATF_CHECK(search(&key, v, n, ctx) == &v[i]);
+       }
+       if (n != 0) {
+               key = -1;
+               ATF_CHECK(search(&key, v, n, ctx) == NULL);
+               key = (int)n;
+               ATF_CHECK(search(&key, v, n, ctx) == NULL);
+       }
+}
+
+#endif /* !_TEST_SEARCH_H */

Reply via email to