// Buffer too small.
errno = 0;
memset(buf, 0, sizeof(buf));
ASSERT_EQ(buf, strerror_r(4567, buf, 2));
ASSERT_STREQ("U", buf); // The GNU strerror_r doesn't set errno (the POSIX one sets it to ERANGE).
ASSERT_ERRNO(0); #else
GTEST_SKIP() << "musl doesn't have GNU strerror_r"; #endif
}
TEST(STRING_TEST, strsignal) { // A regular signal.
ASSERT_STREQ("Hangup", strsignal(1));
// A real-time signal.
ASSERT_STREQ("Real-time signal 14", strsignal(SIGRTMIN + 14)); // One of the signals the C library keeps to itself.
ASSERT_STREQ("Unknown signal 32", strsignal(32)); // __SIGRTMIN
// Errors.
ASSERT_STREQ("Unknown signal -1", strsignal(-1)); // Too small.
ASSERT_STREQ("Unknown signal 0", strsignal(0)); // Still too small.
ASSERT_STREQ("Unknown signal 1234", strsignal(1234)); // Too large.
}
ASSERT_STREQ("Unknown signal 1001", strsignal1001);
}
// TODO: where did this number come from? #define ITER 500
// For every length we want to test, vary and change alignment // of allocated memory, fill it with some values, calculate // expected result and then run function and compare what we got. // These tests contributed by Intel Corporation. // TODO: make these tests more intention-revealing and less random. template<class Character> class StringTestState { public: explicit StringTestState(size_t MAX_LEN) : MAX_LEN(MAX_LEN), align1_index_(0), align2_index_(0) { int max_alignment = 64;
// TODO: fix the tests to not sometimes use twice their specified "MAX_LEN".
glob_ptr = reinterpret_cast<Character*>(memalign(sysconf(_SC_PAGESIZE), 2 * sizeof(Character) * MAX_LEN + max_alignment));
glob_ptr1 = reinterpret_cast<Character*>(memalign(sysconf(_SC_PAGESIZE), 2 * sizeof(Character) * MAX_LEN + max_alignment));
glob_ptr2 = reinterpret_cast<Character*>(memalign(sysconf(_SC_PAGESIZE), 2 * sizeof(Character) * MAX_LEN + max_alignment));
// Calculate input lengths and fill state.len with them. // Test small lengths with more density than big ones. Manually push // smallest (0) and biggest (MAX_LEN) lengths. Avoid repeats. // Return number of lengths to test. void InitLenArray() {
n = 0;
len[n++] = 0; for (size_t i = 1; i < ITER; ++i) {
size_t l = static_cast<size_t>(exp(log(static_cast<double>(MAX_LEN)) * i / ITER)); if (l != len[n - 1]) {
len[n++] = l;
}
}
len[n++] = MAX_LEN;
}
// Verify that strchr finds the first occurrence of 'a' in a string // filled with 'a' characters. Iterate over the string putting // non 'a' characters at the front of the string during each iteration // and continue to verify that strchr can find the first occurrence // properly. The idea is to cover all possible alignments of the location // of the first occurrence of the 'a' character and which includes // other 'a' characters close by. for (size_t i = 0; i < sizeof(str) - 1; i++) {
EXPECT_EQ(&str[i], strchr(str, 'a'));
str[i] = 'b';
}
}
TEST(STRING_TEST, stpncpy) {
StringTestState<char> state(SMALL); for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) {
memset(state.ptr1, 'J', state.MAX_LEN); // Choose a random size for our src buffer.
size_t ptr1_len = random() % state.MAX_LEN;
state.ptr1[ptr1_len] = '\0'; // Copy ptr1 into ptr, used to verify that ptr1 does not get modified.
memcpy(state.ptr, state.ptr1, state.MAX_LEN); // Init ptr2 to a set value.
memset(state.ptr2, '\1', state.MAX_LEN);
// Choose a random amount of data to copy.
size_t copy_len = random() % state.MAX_LEN;
// Set the second half of ptr to the expected pattern in ptr2.
memset(state.ptr + state.MAX_LEN, '\1', state.MAX_LEN);
memcpy(state.ptr + state.MAX_LEN, state.ptr1, copy_len);
size_t expected_end; if (copy_len > ptr1_len) {
memset(state.ptr + state.MAX_LEN + ptr1_len, '\0', copy_len - ptr1_len);
expected_end = ptr1_len;
} else {
expected_end = copy_len;
}
// Verify ptr1 was not modified.
ASSERT_EQ(0, memcmp(state.ptr1, state.ptr, state.MAX_LEN)); // Verify ptr2 contains the expected data.
ASSERT_EQ(0, memcmp(state.ptr2, state.ptr + state.MAX_LEN, state.MAX_LEN));
}
}
TEST(STRING_TEST, strncpy) {
StringTestState<char> state(SMALL); for (state.BeginIterations(); state.HasNextIteration(); state.NextIteration()) { // Choose a random value to fill the string, except \0 (string terminator), // or \1 (guarantees it's different from anything in ptr2).
memset(state.ptr1, 'K', state.MAX_LEN); // Choose a random size for our src buffer.
size_t ptr1_len = random() % state.MAX_LEN;
state.ptr1[ptr1_len] = '\0'; // Copy ptr1 into ptr, used to verify that ptr1 does not get modified.
memcpy(state.ptr, state.ptr1, state.MAX_LEN); // Init ptr2 to a set value.
memset(state.ptr2, '\1', state.MAX_LEN);
// Choose a random amount of data to copy.
size_t copy_len = random() % state.MAX_LEN;
// Set the second half of ptr to the expected pattern in ptr2.
memset(state.ptr + state.MAX_LEN, '\1', state.MAX_LEN);
memcpy(state.ptr + state.MAX_LEN, state.ptr1, copy_len); if (copy_len > ptr1_len) {
memset(state.ptr + state.MAX_LEN + ptr1_len, '\0', copy_len - ptr1_len);
}
char* src_data = reinterpret_cast<char*>(malloc(MEMMOVE_DATA_SIZE));
ASSERT_TRUE(src_data != nullptr); // Initialize to a known pattern to copy into src for each test and // to compare dst against. for (size_t i = 0; i < MEMMOVE_DATA_SIZE; i++) {
src_data[i] = (i + 1) % 255;
}
// Check all different dst offsets between 0 and 127 inclusive. char* src = buffer; for (size_t i = 0; i < 127; i++) { char* dst = buffer + 256 + i; // Small copy.
verify_memmove(src_data, dst, src, 1024);
// Medium copy.
verify_memmove(src_data, dst, src, 64 * 1024);
// Use our own incrementer to cut down on the total number of calls. static size_t LargeSetIncrement(size_t len) { if (len >= 4096) { return4096;
} elseif (len >= 1024) { return1024;
} elseif (len >= 256) { return256;
} return1;
}
#define STRCAT_DST_LEN 64
staticvoid DoStrcatTest(uint8_t* src, uint8_t* dst, size_t len) { if (len >= 1) { int value = 32 + (len % 96);
memset(src, value, len - 1);
src[len-1] = '\0';
if (len >= STRCAT_DST_LEN) { // Create a small buffer for doing quick compares in each loop.
uint8_t cmp_buf[STRCAT_DST_LEN]; // Make sure dst string contains a different value then the src string. int value2 = 32 + (value + 2) % 96;
memset(cmp_buf, value2, sizeof(cmp_buf));
for (size_t i = 1; i <= STRCAT_DST_LEN;) {
memset(dst, value2, i-1);
memset(dst+i-1, 0, len-i);
src[len-i] = '\0';
ASSERT_EQ(dst, reinterpret_cast<uint8_t*>(strcat(reinterpret_cast<char*>(dst), reinterpret_cast<char*>(src))));
ASSERT_TRUE(memcmp(dst, cmp_buf, i-1) == 0);
ASSERT_TRUE(memcmp(src, dst+i-1, len-i+1) == 0); // This is an expensive loop, so don't loop through every value, // get to a certain size and then start doubling. if (i < 16) {
i++;
} else {
i <<= 1;
}
}
} else {
dst[0] = '\0';
ASSERT_EQ(dst, reinterpret_cast<uint8_t*>(strcat(reinterpret_cast<char*>(dst), reinterpret_cast<char*>(src))));
ASSERT_TRUE(memcmp(src, dst, len) == 0);
}
}
}
#ifdefined(STRLCAT_SUPPORTED) staticvoid DoStrlcatTest(uint8_t* src, uint8_t* dst, size_t len) { if (len >= 1) { int value = 32 + (len % 96);
memset(src, value, len - 1);
src[len-1] = '\0';
if (len >= STRCAT_DST_LEN) { // Create a small buffer for doing quick compares in each loop.
uint8_t cmp_buf[STRCAT_DST_LEN]; // Make sure dst string contains a different value then the src string. int value2 = 32 + (value + 2) % 96;
memset(cmp_buf, value2, sizeof(cmp_buf));
for (size_t i = 1; i <= STRCAT_DST_LEN;) {
memset(dst, value2, i-1);
memset(dst+i-1, 0, len-i);
src[len-i] = '\0';
ASSERT_EQ(len-1, strlcat(reinterpret_cast<char*>(dst), reinterpret_cast<char*>(src), len));
ASSERT_TRUE(memcmp(dst, cmp_buf, i-1) == 0);
ASSERT_TRUE(memcmp(src, dst+i-1, len-i+1) == 0); // This is an expensive loop, so don't loop through every value, // get to a certain size and then start doubling. if (i < 16) {
i++;
} else {
i <<= 1;
}
}
} else {
dst[0] = '\0';
ASSERT_EQ(len-1, strlcat(reinterpret_cast<char*>(dst), reinterpret_cast<char*>(src), len));
ASSERT_TRUE(memcmp(src, dst, len) == 0);
}
}
} #endif
// Do single character differences.
size_t len; if (len1 > len2) {
len = len2;
} else {
len = len1;
} // Need at least a two character buffer to do this test. if (len > 1) {
buf1[len-1] = '\0';
buf2[len-1] = '\0'; int diff_c = (c + 1) % 96;
staticvoid DoMemchrTest(uint8_t* buf, size_t len) { if (len >= 1) { int value = len % 128; int search_value = (len % 128) + 1;
memset(buf, value, len); // The buffer does not contain the search value.
ASSERT_EQ(nullptr, memchr(buf, search_value, len)); if (len >= 2) {
buf[0] = search_value; // The search value is the first element in the buffer.
ASSERT_EQ(&buf[0], memchr(buf, search_value, len));
buf[0] = value;
}
// The search value is the last element in the buffer.
buf[len - 1] = search_value;
ASSERT_EQ(&buf[len - 1], memchr(buf, search_value, len));
// The search value is the last element in the buffer, and the length // spans well beyond the buffer's end. C11 explicitly allows this.
ASSERT_EQ(&buf[len - 1], memchr(buf, search_value, len + 4096));
}
}
template <typename Fn>
requires std::invocable<Fn, constchar*, int> staticvoid DoStrchrTestImpl(uint8_t* buf, size_t len, Fn strchr_fn) { if (len >= 1) { char value = 32 + (len % 96); char search_value = 33 + (len % 96);
memset(buf, value, len - 1);
buf[len - 1] = '\0'; // The buffer does not contain the search value.
ASSERT_EQ(nullptr, strchr_fn(reinterpret_cast<char*>(buf), search_value)); // Search for the special '\0' character.
ASSERT_EQ(reinterpret_cast<char*>(&buf[len - 1]),
strchr_fn(reinterpret_cast<char*>(buf), '\0')); if (len >= 2) {
buf[0] = search_value; // The search value is the first element in the buffer.
ASSERT_EQ(reinterpret_cast<char*>(&buf[0]),
strchr_fn(reinterpret_cast<char*>(buf), search_value));
buf[0] = value;
buf[len - 2] = search_value; // The search value is the second to last element in the buffer. // The last element is the '\0' character.
ASSERT_EQ(reinterpret_cast<char*>(&buf[len - 2]),
strchr_fn(reinterpret_cast<char*>(buf), search_value));
}
}
}
staticvoid DoStrchrTest(uint8_t* buf, size_t len) {
DoStrchrTestImpl(buf, len, [](constchar* s, int c) { return strchr(s, c); });
}
template <typename Fn>
requires std::invocable<Fn, constchar*, int> staticvoid DoStrrchrTestImpl(uint8_t* buf, size_t len, Fn strrchr_fn) { if (len >= 1) { char value = 32 + (len % 96); char search_value = 33 + (len % 96);
memset(buf, value, len - 1);
buf[len - 1] = '\0'; // The buffer does not contain the search value.
ASSERT_EQ(nullptr, strrchr_fn(reinterpret_cast<char*>(buf), search_value)); // Search for the special '\0' character.
ASSERT_EQ(reinterpret_cast<char*>(&buf[len - 1]),
strrchr_fn(reinterpret_cast<char*>(buf), '\0')); if (len >= 2) {
buf[0] = search_value; // The search value is the first element in the buffer.
ASSERT_EQ(reinterpret_cast<char*>(&buf[0]),
strrchr_fn(reinterpret_cast<char*>(buf), search_value));
buf[0] = value;
buf[len - 2] = search_value; // The search value is the second to last element in the buffer. // The last element is the '\0' character.
ASSERT_EQ(reinterpret_cast<char*>(&buf[len - 2]),
strrchr_fn(reinterpret_cast<char*>(buf), search_value));
}
}
}
staticvoid DoStrrchrTest(uint8_t* buf, size_t len) {
DoStrrchrTestImpl(buf, len, [](constchar* s, int c) { return strrchr(s, c); });
}
// clang depends on the fact that a memcpy where src and dst is the same // still operates correctly. This test verifies that this assumption // holds true. // See https://llvm.org/bugs/show_bug.cgi?id=11763 for more information. static std::vector<uint8_t> g_memcpy_same_buffer;
// The current memmem() implementation has special cases for needles of // lengths 0, 1, 2, 3, and 4, plus a long needle case. We test matches at the // beginning, middle, and end of the haystack.
// The current strstr() implementation has special cases for needles of // lengths 0, 1, 2, 3, and 4, plus a long needle case. We test matches at the // beginning, middle, and end of the haystack.
// The "transform" of two different strings should cause different outputs.
ASSERT_TRUE(strcmp(dst1, dst2) < 0);
}
TEST(STRING_TEST, strxfrm_l_smoke) { // bionic just forwards to strxfrm(3), so this is a subset of the // strxfrm test. constchar* src1 = "aab"; char dst1[16] = {};
ASSERT_EQ(strxfrm_l(dst1, src1, 0, LC_GLOBAL_LOCALE), 3U);
ASSERT_STREQ(dst1, "");
ASSERT_EQ(strxfrm_l(dst1, src1, sizeof(dst1), LC_GLOBAL_LOCALE), 3U);
}
TEST(STRING_TEST, memccpy_smoke) { char dst[32];
memset(dst, 0, sizeof(dst)); char* p = static_cast<char*>(memccpy(dst, "hello world", ' ', 32));
EXPECT_STREQ("hello ", dst);
EXPECT_EQ(ptrdiff_t(6), p - dst);
TEST(STRING_TEST, memset_explicit_smoke) { #ifdefined(__BIONIC__) // We can't reliably test that the compiler won't optimize out calls to // memset_explicit(), but we can at least check that it behaves like memset. char buf[32];
memset_explicit(buf, 'x', sizeof(buf));
ASSERT_TRUE(memcmp(buf, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", sizeof(buf)) == 0); #else
GTEST_SKIP() << "memset_explicit not available"; #endif
}
// Split the buffer into two equal parts with different contents, and then // run both configurations on strcspn. This should provide sufficient variety // in sizes. const size_t split_point = len / 2; char* buf_1_start = buf; char* buf_1_nul = buf + split_point; char* buf_2_start = buf_1_nul + 1; char* buf_2_nul = buf + len - 1;
// Haystack size classes for large needle test functions; see comments in those // for how these were determined.
constexpr auto STRSPN_HAYSTACK_SIZE_CLASSES = {16, 64, 128, 192, 256};
static std::string MustReplaceChar(std::string_view haystack, char replace_what, char replace_with) {
std::string s{haystack}; auto i = s.find(replace_what); if (i == std::string::npos) {
fprintf(stderr, "Fatal: no '%c' in given string.\n", replace_what);
abort();
}
s[i] = replace_with; return s;
}
TEST(STRING_TEST, strspn_large_needle_many_haystack_sizes) { // This test was built with psimd's strspn impl in mind, which is written: // - with one path for keep/reject sets of <= 4 chars, falling back to // - one path for haystacks <= 128B, falling back to // - one general path for all other cases // // The 'general path for all other cases' also branches based on whether the // keep/reject set has any chars >= 0x80. // // Most existing handwritten tests fall into the "<= 4 chars keep/reject set" // path, so this ignores that. const std::string all_chars = StringOfAllChars(); for (size_t size_class : STRSPN_HAYSTACK_SIZE_CLASSES) {
std::string haystack(size_class, 'a');
EXPECT_EQ(strspn(haystack.c_str(), all_chars.c_str()), size_class);
TEST(STRING_TEST, strcspn_large_needle_many_haystack_sizes) { // This test was built with psimd's strcspn impl in mind, which is written: // strspn, so the same special cases apply: // - with one path for keep/reject sets of <= 4 chars, falling back to // - one path for haystacks <= 128B, falling back to // - one general path for all other cases // // The 'general path for all other cases' also branches based on whether the // keep/reject set has any chars >= 0x80. // // Most existing handwritten tests fall into the "<= 4 chars keep/reject set" // path, so this ignores that. const std::string all_chars = StringOfAllChars(); for (size_t size_class : STRSPN_HAYSTACK_SIZE_CLASSES) {
std::string haystack(size_class, 'a');
TEST(STRING_TEST, strsep) { char* p = nullptr;
EXPECT_EQ(nullptr, strsep(&p, ":"));
// Unlike strtok(), strsep() _does_ return empty strings. char str[] = ":hello:world:::foo:";
p = str;
EXPECT_STREQ("", strsep(&p, ":"));
EXPECT_STREQ("hello", strsep(&p, ":"));
EXPECT_STREQ("world", strsep(&p, ":"));
EXPECT_STREQ("", strsep(&p, ":"));
EXPECT_STREQ("", strsep(&p, ":"));
EXPECT_STREQ("foo", strsep(&p, ":"));
EXPECT_STREQ("", strsep(&p, ":"));
EXPECT_EQ(nullptr, strsep(&p, ":")); // Repeated calls after the first nullptr keep returning nullptr. for (size_t i = 0; i < 1024; ++i) {
EXPECT_EQ(nullptr, strsep(&p, ":"));
}
// Some existing implementations have a separate return path for this. char non_empty_at_end[] = "hello:world";
p = non_empty_at_end;
EXPECT_STREQ("hello", strsep(&p, ":"));
EXPECT_STREQ("world", strsep(&p, ":")); for (size_t i = 0; i < 1024; ++i) {
EXPECT_EQ(nullptr, strsep(&p, ":"));
}
// Check that the implementation copes with top bit set characters. char top_bit_set_str[] = "hello\x80world";
p = top_bit_set_str;
EXPECT_STREQ("hello", strsep(&p, "\x80"));
EXPECT_STREQ("world", strsep(&p, "\x80"));
EXPECT_EQ(nullptr, strsep(&p, "\x80"));
}
// Unlike strsep(), strtok() doesn't return empty strings. char str[] = ":hello:world:::foo:";
EXPECT_STREQ("hello", strtok(str, ":"));
EXPECT_STREQ("world", strtok(nullptr, ":"));
EXPECT_STREQ("foo", strtok(nullptr, ":"));
EXPECT_EQ(nullptr, strtok(nullptr, ":")); // Repeated calls after the first nullptr keep returning nullptr. for (size_t i = 0; i < 1024; ++i) {
EXPECT_EQ(nullptr, strtok(nullptr, ":"));
}
// Some existing implementations have a separate return path for this. char non_empty_at_end[] = "hello:world";
EXPECT_STREQ("hello", strtok(non_empty_at_end, ":"));
EXPECT_STREQ("world", strtok(nullptr, ":")); for (size_t i = 0; i < 1024; ++i) {
EXPECT_EQ(nullptr, strtok(nullptr, ":"));
}
// Check that the implementation copes with top bit set characters. char top_bit_set_str[] = "hello\x80world";
EXPECT_STREQ("hello", strtok(top_bit_set_str, "\x80"));
EXPECT_STREQ("world", strtok(nullptr, "\x80"));
EXPECT_EQ(nullptr, strtok(nullptr, "\x80"));
}
// Unlike strsep(), strtok_r() doesn't return empty strings. char str[] = ":hello:world:::foo:";
EXPECT_STREQ("hello", strtok_r(str, ":", &p));
EXPECT_STREQ("world", strtok_r(nullptr, ":", &p));
EXPECT_STREQ("foo", strtok_r(nullptr, ":", &p));
EXPECT_EQ(nullptr, strtok_r(nullptr, ":", &p)); // Repeated calls after the first nullptr keep returning nullptr. for (size_t i = 0; i < 1024; ++i) {
EXPECT_EQ(nullptr, p);
EXPECT_EQ(nullptr, strtok_r(nullptr, ":", &p));
}
// Some existing implementations have a separate return path for this. char non_empty_at_end[] = "hello:world";
EXPECT_STREQ("hello", strtok_r(non_empty_at_end, ":", &p));
EXPECT_STREQ("world", strtok_r(nullptr, ":", &p)); for (size_t i = 0; i < 1024; ++i) {
EXPECT_EQ(nullptr, p);
EXPECT_EQ(nullptr, strtok_r(nullptr, ":", &p));
}
// Check that the implementation copes with top bit set characters. char top_bit_set_str[] = "hello\x80world";
EXPECT_STREQ("hello", strtok_r(top_bit_set_str, "\x80", &p));
EXPECT_STREQ("world", strtok_r(nullptr, "\x80", &p));
EXPECT_EQ(nullptr, strtok_r(nullptr, "\x80", &p));
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.56 Sekunden
(vorverarbeitet am 2026-06-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.