mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-11-07 04:36:52 -10:00
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
This commit is contained in:
11 files changed
+503
-6
No files matched your search
@@ -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_ */
|
||||
@@ -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
|
||||
|
||||
@@ -132,6 +132,7 @@ FBSD_1.8 {
|
||||
};
|
||||
|
||||
FBSD_1.9 {
|
||||
bsearch_s;
|
||||
memalignment;
|
||||
recallocarray;
|
||||
strfromd;
|
||||
|
||||
+103
-5
@@ -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 .
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Faraz Vahedi <kfv@FreeBSD.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
#define I_AM_BSEARCH_S
|
||||
#include "bsearch.c"
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Faraz Vahedi <kfv@FreeBSD.org>
|
||||
*
|
||||
* 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());
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Faraz Vahedi <kfv@FreeBSD.org>
|
||||
*
|
||||
* 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());
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Faraz Vahedi <kfv@FreeBSD.org>
|
||||
*
|
||||
* 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());
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Faraz Vahedi <kfv@FreeBSD.org>
|
||||
*
|
||||
* 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 */
|
||||
Reference in new issue
Block a user