Changeset View
Standalone View
lib/libc/tests/string/strrchr_test.c
- This file was added.
| /* | |||||
| * SPDX-License-Identifier: BSD-2-Clause | |||||
| * | |||||
| * Copyright (c) 2023, 2026 Robert Clausecker <fuz@FreeBSD.org> | |||||
| * | |||||
| * Adapted from memrchr_test.c. | |||||
| */ | |||||
| #include <sys/cdefs.h> | |||||
| #include <dlfcn.h> | |||||
| #include <limits.h> | |||||
| #include <stdio.h> | |||||
| #include <string.h> | |||||
| #include <atf-c.h> | |||||
| static char *(*strrchr_fn)(const char *, int); | |||||
| /* | |||||
| * Check that when looking for the character NUL, we find the | |||||
| * string terminator, and not some NUL character after it. | |||||
| */ | |||||
| ATF_TC_WITHOUT_HEAD(nul); | |||||
| ATF_TC_BODY(nul, tc) | |||||
| { | |||||
| size_t i, j, k; | |||||
| char buf[1+15+64]; /* offset [0+15] + 64 buffer bytes + sentinels */ | |||||
ngie: Could these please be made into constants? | |||||
Done Inline ActionsI'm not sure if this would be helpful here. These numbers correspond to the possible choices in the SSE implementation, which processes 16 bytes per vector (thus 16 possibly misalignments) and 32 bytes per iteration (thus 64 bytes total length to complete the main loop at least once). If I change the code such that these values change, I plan to bump the unit test to cover the extended range. The same applies to the other cases and in fact pretty much all the string unit tests I have written so far. I can add some arbitrary symbolic names for these numbers, but I don't see how that improves things, it just adds more clutter. If you insist, I can try and do some systematic refactoring to add symbolic constants at a later point, but it makes the unit tests less useful as a development tool for me, as it becomes harder to modify the numbers when the code changes. My main priority is to get the fix for 293915 in right now. fuz: I'm not sure if this would be helpful here. These numbers correspond to the possible choices… | |||||
Not Done Inline Actionsnit: I would specify alignas(16) for better repeatability. I'm not 100% convinced it won't fail on i = 4 for one version of the compiler, but 8 for another, if the stack layout is changed strajabot: nit: I would specify `alignas(16) ` for better repeatability. I'm not 100% convinced it won't… | |||||
Done Inline Actions
Indeed, but it'll cycle through all alignments anyway and the specific iteration it fails on doesn't really matter. fuz: > nit: I would specify `alignas(16) ` for better repeatability. I'm not 100% convinced it won't… | |||||
| buf[0] = '\0'; | |||||
| memset(buf + 1, '-', sizeof(buf) - 1); | |||||
| for (i = 0; i < 16; i++) | |||||
| for (j = 0; j < 64; j++) | |||||
| for (k = j; k < 64; k++) { | |||||
| buf[i + j + 1] = '\0'; | |||||
| buf[i + k + 1] = '\0'; | |||||
| ATF_CHECK_EQ(strrchr_fn(buf + i + 1, '\0'), buf + i + j + 1); | |||||
| buf[i + j + 1] = '-'; | |||||
| buf[i + k + 1] = '-'; | |||||
| } | |||||
| } | |||||
| /* | |||||
| * Check that if the character 'X' does not occur in the string | |||||
| * (but occurs before and after it), we correctly return NULL. | |||||
| */ | |||||
| ATF_TC_WITHOUT_HEAD(not_found); | |||||
| ATF_TC_BODY(not_found, tc) | |||||
Not Done Inline ActionsCould these please be made into constants? ngie: Could these please be made into constants? | |||||
| { | |||||
| size_t i, j; | |||||
| char buf[1+15+64+2]; /* offset [0..15] + 64 buffer bytes + sentinels */ | |||||
| buf[0] = 'X'; | |||||
| memset(buf + 1, '-', sizeof(buf) - 1); | |||||
| for (i = 0; i < 16; i++) | |||||
| for (j = 0; j < 64; j++) { | |||||
| buf[i + j + 1] = '\0'; | |||||
| buf[i + j + 2] = 'X'; | |||||
| ATF_CHECK_EQ(strrchr_fn(buf + i + 1, 'X'), NULL); | |||||
| buf[i + j + 1] = '-'; | |||||
| buf[i + j + 2] = '-'; | |||||
| } | |||||
| } | |||||
| static void | |||||
| do_found_test(char buf[], size_t first, size_t second) | |||||
| { | |||||
| /* invariant: first <= second */ | |||||
| buf[first] = 'X'; | |||||
| buf[second] = 'X'; | |||||
| ATF_CHECK_EQ(strrchr_fn(buf, 'X'), buf + second); | |||||
| buf[first] = '-'; | |||||
| buf[second] = '-'; | |||||
| } | |||||
Not Done Inline ActionsAlso, why X/-? ngie: Also, why X/-? | |||||
Done Inline ActionsThe choice is arbitrary. X is the character we are looking for, - is some other character. They were chosen to be easy to distinguish visually when inspecting a test suite failure in the debugger. Unit test values goes through all possible characters to try to catch arithmetic errors in the scalar kernel (cf. 3d8ef25). fuz: The choice is arbitrary. `X` is the character we are looking for, `-` is some other character. | |||||
| /* | |||||
| * Check that if the character 'X' occurs in the string multiple | |||||
Not Done Inline ActionsCould these please be made into constants? ngie: Could these please be made into constants? | |||||
| * times (i. e. twice), its last encounter is returned. | |||||
| */ | |||||
| ATF_TC_WITHOUT_HEAD(found); | |||||
| ATF_TC_BODY(found, tc) | |||||
| { | |||||
| size_t i, j, k, l; | |||||
| char buf[1+15+64+2]; | |||||
| buf[0] = 'X'; | |||||
| memset(buf + 1, '-', sizeof(buf) - 1); | |||||
| for (i = 0; i < 16; i++) | |||||
| for (j = 0; j < 64; j++) | |||||
| for (k = 0; k < j; k++) | |||||
| for (l = 0; l <= k; l++) { | |||||
| buf[i + j + 1] = '\0'; | |||||
| buf[i + j + 2] = 'X'; | |||||
| do_found_test(buf + i + 1, l, k); | |||||
| buf[i + j + 1] = '-'; | |||||
| buf[i + j + 2] = '-'; | |||||
| } | |||||
| } | |||||
| static void | |||||
| do_values_test(char buf[], size_t len, size_t i, int c) | |||||
| { | |||||
| /* sentinels */ | |||||
| buf[-1] = c; | |||||
| buf[len] = '\0'; | |||||
| buf[len + 1] = 'c'; | |||||
| /* fill the string with some other character, but not with NUL */ | |||||
| memset(buf, c == UCHAR_MAX ? c - 1 : c + 1, len); | |||||
| if (i < len) { | |||||
| buf[i] = c; | |||||
| ATF_CHECK_EQ(strrchr_fn(buf, c), buf + i); | |||||
| } else | |||||
| ATF_CHECK_EQ(strrchr_fn(buf, c), c == 0 ? buf + len : NULL); | |||||
| } | |||||
| /* | |||||
| * Check that the character is found regardless of its value. | |||||
| * This catches arithmetic (overflow) errors in incorrect SWAR | |||||
| * implementations of byte-parallel character matching. | |||||
| */ | |||||
| ATF_TC_WITHOUT_HEAD(values); | |||||
| ATF_TC_BODY(values, tc) | |||||
| { | |||||
| size_t i, j, k; | |||||
| int c; | |||||
| char buf[1+15+64+2]; | |||||
| for (i = 0; i < 16; i++) | |||||
| for (j = 0; j < 64; j++) | |||||
| for (k = 0; k <= j; k++) | |||||
| for (c = 0; c <= UCHAR_MAX; c++) | |||||
| do_values_test(buf + i + 1, j, k, c); | |||||
| } | |||||
| ATF_TP_ADD_TCS(tp) | |||||
| { | |||||
| void *dl_handle; | |||||
| dl_handle = dlopen(NULL, RTLD_LAZY); | |||||
| strrchr_fn = dlsym(dl_handle, "test_strrchr"); | |||||
| if (strrchr_fn == NULL) | |||||
| strrchr_fn = strrchr; | |||||
| ATF_TP_ADD_TC(tp, nul); | |||||
| ATF_TP_ADD_TC(tp, not_found); | |||||
| ATF_TP_ADD_TC(tp, found); | |||||
| ATF_TP_ADD_TC(tp, values); | |||||
| return (atf_no_error()); | |||||
| } | |||||
Could these please be made into constants?