xref: /src/lib/libc/tests/string/strrchr_test.c (revision 8b5d77bbcbd98e684226950be1c779e108059d8d)
1 /*
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023, 2026 Robert Clausecker <fuz@FreeBSD.org>
5  *
6  * Adapted from memrchr_test.c.
7  */
8 
9 #include <sys/cdefs.h>
10 
11 #include <dlfcn.h>
12 #include <limits.h>
13 #include <stdio.h>
14 #include <string.h>
15 
16 #include <atf-c.h>
17 
18 static char *(*strrchr_fn)(const char *, int);
19 
20 /*
21  * Check that when looking for the character NUL, we find the
22  * string terminator, and not some NUL character after it.
23  */
24 ATF_TC_WITHOUT_HEAD(nul);
ATF_TC_BODY(nul,tc)25 ATF_TC_BODY(nul, tc)
26 {
27 	size_t i, j, k;
28 	char buf[1+15+64]; /* offset [0+15] + 64 buffer bytes + sentinels */
29 
30 	buf[0] = '\0';
31 	memset(buf + 1, '-', sizeof(buf) - 1);
32 
33 	for (i = 0; i < 16; i++)
34 		for (j = 0; j < 64; j++)
35 			for (k = j; k < 64; k++) {
36 				buf[i + j + 1] = '\0';
37 				buf[i + k + 1] = '\0';
38 				ATF_CHECK_EQ(strrchr_fn(buf + i + 1, '\0'), buf + i + j + 1);
39 				buf[i + j + 1] = '-';
40 				buf[i + k + 1] = '-';
41 			}
42 }
43 
44 /*
45  * Check that if the character 'X' does not occur in the string
46  * (but occurs before and after it), we correctly return NULL.
47  */
48 ATF_TC_WITHOUT_HEAD(not_found);
ATF_TC_BODY(not_found,tc)49 ATF_TC_BODY(not_found, tc)
50 {
51 	size_t i, j;
52 	char buf[1+15+64+2]; /* offset [0..15] + 64 buffer bytes + sentinels */
53 
54 	buf[0] = 'X';
55 	memset(buf + 1, '-', sizeof(buf) - 1);
56 
57 	for (i = 0; i < 16; i++)
58 		for (j = 0; j < 64; j++) {
59 			buf[i + j + 1] = '\0';
60 			buf[i + j + 2] = 'X';
61 			ATF_CHECK_EQ(strrchr_fn(buf + i + 1, 'X'), NULL);
62 			buf[i + j + 1] = '-';
63 			buf[i + j + 2] = '-';
64 		}
65 }
66 
67 static void
do_found_test(char buf[],size_t first,size_t second)68 do_found_test(char buf[], size_t first, size_t second)
69 {
70 	/* invariant: first <= second */
71 
72 	buf[first] = 'X';
73 	buf[second] = 'X';
74 	ATF_CHECK_EQ(strrchr_fn(buf, 'X'), buf + second);
75 	buf[first] = '-';
76 	buf[second] = '-';
77 }
78 
79 /*
80  * Check that if the character 'X' occurs in the string multiple
81  * times (i. e. twice), its last encounter is returned.
82  */
83 ATF_TC_WITHOUT_HEAD(found);
ATF_TC_BODY(found,tc)84 ATF_TC_BODY(found, tc)
85 {
86 	size_t i, j, k, l;
87 	char buf[1+15+64+2];
88 
89 	buf[0] = 'X';
90 	memset(buf + 1, '-', sizeof(buf) - 1);
91 
92 	for (i = 0; i < 16; i++)
93 		for (j = 0; j < 64; j++)
94 			for (k = 0; k < j; k++)
95 				for (l = 0; l <= k; l++) {
96 					buf[i + j + 1] = '\0';
97 					buf[i + j + 2] = 'X';
98 					do_found_test(buf + i + 1, l, k);
99 					buf[i + j + 1] = '-';
100 					buf[i + j + 2] = '-';
101 				}
102 }
103 
104 static void
do_values_test(char buf[],size_t len,size_t i,int c)105 do_values_test(char buf[], size_t len, size_t i, int c)
106 {
107 	/* sentinels */
108 	buf[-1] = c;
109 	buf[len] = '\0';
110 	buf[len + 1] = 'c';
111 
112 	/* fill the string with some other character, but not with NUL */
113 	memset(buf, c == UCHAR_MAX ? c - 1 : c + 1, len);
114 
115 	if (i < len) {
116 		buf[i] = c;
117 		ATF_CHECK_EQ(strrchr_fn(buf, c), buf + i);
118 	} else
119 		ATF_CHECK_EQ(strrchr_fn(buf, c), c == 0 ? buf + len : NULL);
120 }
121 
122 /*
123  * Check that the character is found regardless of its value.
124  * This catches arithmetic (overflow) errors in incorrect SWAR
125  * implementations of byte-parallel character matching.
126  */
127 ATF_TC_WITHOUT_HEAD(values);
ATF_TC_BODY(values,tc)128 ATF_TC_BODY(values, tc)
129 {
130 	size_t i, j, k;
131 	int c;
132 	char buf[1+15+64+2];
133 
134 	for (i = 0; i < 16; i++)
135 		for (j = 0; j < 64; j++)
136 			for (k = 0; k <= j; k++)
137 				for (c = 0; c <= UCHAR_MAX; c++)
138 					do_values_test(buf + i + 1, j, k, c);
139 }
140 
ATF_TP_ADD_TCS(tp)141 ATF_TP_ADD_TCS(tp)
142 {
143 	void *dl_handle;
144 
145 	dl_handle = dlopen(NULL, RTLD_LAZY);
146 	strrchr_fn = dlsym(dl_handle, "test_strrchr");
147 	if (strrchr_fn == NULL)
148 		strrchr_fn = strrchr;
149 
150 	ATF_TP_ADD_TC(tp, nul);
151 	ATF_TP_ADD_TC(tp, not_found);
152 	ATF_TP_ADD_TC(tp, found);
153 	ATF_TP_ADD_TC(tp, values);
154 
155 	return (atf_no_error());
156 }
157