#ifdef __GLIBC__ // glibc immediately dereferences the locale passed to all wcsto*_l functions, // even if it won't be used, and even if it's LC_GLOBAL_LOCALE, which isn't a // pointer to valid memory. static locale_t SAFE_LC_GLOBAL_LOCALE = duplocale(LC_GLOBAL_LOCALE); #else static locale_t SAFE_LC_GLOBAL_LOCALE = LC_GLOBAL_LOCALE; #endif
// Modern versions of UTF-8 (https://datatracker.ietf.org/doc/html/rfc3629 and // newer) explicitly disallow code points beyond U+10FFFF, which exclude all 5- // and 6-byte sequences. Earlier versions of UTF-8 allowed the wider range: // https://datatracker.ietf.org/doc/html/rfc2279. // // Bionic's unicode implementation was written after the high values were // excluded, so it has never supported them. Other implementations (at least // as of glibc 2.36), do support those sequences. #ifdefined(__ANDROID__) || defined(ANDROID_HOST_MUSL)
constexpr bool kLibcRejectsOverLongUtf8Sequences = true; #elifdefined(__GLIBC__)
constexpr bool kLibcRejectsOverLongUtf8Sequences = false; #else #error kLibcRejectsOverLongUtf8Sequences must be configured forthis platform #endif
// Any non-initial state is invalid when calling wcrtomb.
EXPECT_EQ(static_cast<size_t>(-2), mbrtowc(nullptr, "\xc2", 1, &ps));
EXPECT_ERRNO_FAILURE(EILSEQ, static_cast<size_t>(-1), wcrtomb(out, 0x00a2, &ps));
// If the first argument to wcrtomb is NULL or the second is L'\0' the shift // state should be reset.
ps = {};
EXPECT_EQ(static_cast<size_t>(-2), mbrtowc(nullptr, "\xc2", 1, &ps));
EXPECT_EQ(1U, wcrtomb(nullptr, 0x00a2, &ps));
EXPECT_TRUE(mbsinit(&ps));
// An unrepresentable char just returns an error from wcstombs...
EXPECT_ERRNO_FAILURE(EILSEQ, static_cast<size_t>(-1), wcstombs(nullptr, bad_chars, 0));
EXPECT_ERRNO_FAILURE(EILSEQ, static_cast<size_t>(-1), wcstombs(nullptr, bad_chars, 256));
// And wcsrtombs doesn't tell us where it got stuck because we didn't ask it // to actually convert anything...
src = bad_chars;
EXPECT_ERRNO_FAILURE(EILSEQ, static_cast<size_t>(-1), wcsrtombs(nullptr, &src, 0, nullptr));
EXPECT_EQ(&bad_chars[0], src);
src = bad_chars;
EXPECT_ERRNO_FAILURE(EILSEQ, static_cast<size_t>(-1), wcsrtombs(nullptr, &src, 256, nullptr));
EXPECT_EQ(&bad_chars[0], src);
// wcsrtombs is a bit more informative...
memset(bytes, 'x', sizeof(bytes));
src = chars;
EXPECT_EQ(0U, wcsrtombs(bytes, &src, 0, nullptr));
EXPECT_EQ(&chars[0], src); // No input consumed.
// mbtowc and all the mbrto* APIs behave slightly differently when n is 0: // // mbrtowc returns 0 "if the next n or fewer bytes complete the multibyte // character that corresponds to the null wide character" // // mbrtoc (C23 7.24.7.2.4) says: // // If s is not a null pointer, the mbtowc function either returns 0 (if s // points to the null character), or returns the number of bytes that are // contained in the converted multibyte character (if the next n or fewer // bytes form a valid multibyte character), or returns -1 (if they do not // form a valid multibyte character). // // glibc's interpretation differs from all the BSDs (including macOS) and // bionic (by way of openbsd). glibc returns 0 since s does point to the null // character, whereas the BSDs return -1 because the next 0 bytes do not form // a valid multibyte chatacter. glibc's interpretation is probably more // correct from a strict interpretation of the spec, but considering the other // APIs behave more like the BSD interpretation that may be a bug in the spec. #ifdef __GLIBC__ int expected_result_for_zero_length_empty_string = 0; #else int expected_result_for_zero_length_empty_string = -1; #endif
constchar* valid = VALID;
ASSERT_EQ(4U, mbsrtowcs(out, &valid, 4, ps));
ASSERT_EQ(L'A', out[0]);
ASSERT_EQ(static_cast<wchar_t>(0x00a2), out[1]);
ASSERT_EQ(static_cast<wchar_t>(0x20ac), out[2]);
ASSERT_EQ(static_cast<wchar_t>(0x24b62), out[3]); // Check that valid has advanced to the next unread character.
ASSERT_EQ('e', *valid);
wmemset(out, L'x', NUM_WCHARS(sizeof(out)));
ASSERT_EQ(2U, mbsrtowcs(out, &valid, 4, ps));
ASSERT_EQ(L'e', out[0]);
ASSERT_EQ(L'f', out[1]);
ASSERT_EQ(L'\0', out[2]); // Check that we didn't clobber the rest of out.
ASSERT_EQ(L'x', out[3]); // Check that valid has advanced to the end of the string.
ASSERT_EQ(nullptr, valid);
TEST(wchar, wcwidth_non_spacing_special_cases) { // U+00AD is a soft hyphen, which normally shouldn't be rendered at all. // I think the assumption here is that you elide the soft hyphen character // completely in that case, and never call wcwidth() if you don't want to // render it as an actual hyphen. Whereas if you do want to render it, // you call wcwidth(), and 1 is the right answer. This is what Markus Kuhn's // original https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c did, // and glibc and iOS do the same. // See also: https://en.wikipedia.org/wiki/Soft_hyphen#Text_to_be_formatted_by_the_recipient
EXPECT_EQ(1, wcwidth(0x00ad)); // Soft hyphen (SHY).
// U+115F is the Hangeul choseong filler (for a degenerate composed // character missing an initial consonant (as opposed to one with a // leading ieung). Since the code points for combining jungseong (medial // vowels) and jongseong (trailing consonants) have width 0, the choseong // (initial consonant) has width 2 to cover the entire syllable. So unless // U+115f has width 2, a degenerate composed "syllable" without an initial // consonant or ieung would have a total width of 0, which is silly. // The following sequence is effectively "약" without the leading ieung...
EXPECT_EQ(2, wcwidth(0x115f)); // Hangeul choseong filler.
EXPECT_EQ(0, wcwidth(0x1163)); // Hangeul jungseong "ya".
EXPECT_EQ(0, wcwidth(0x11a8)); // Hangeul jongseong "kiyeok".
// U+1160, the jungseong filler, has width 0 because it must have been // preceded by either a choseong or choseong filler.
EXPECT_EQ(0, wcwidth(0x1160));
}
TEST(wchar, wcwidth_cjk) {
EXPECT_EQ(2, wcwidth(0x4e00)); // Start of CJK unified block.
EXPECT_EQ(2, wcwidth(0x9fff)); // End of CJK unified block.
EXPECT_EQ(2, wcwidth(0x3400)); // Start of CJK extension A block.
EXPECT_EQ(2, wcwidth(0x4dbf)); // End of CJK extension A block.
EXPECT_EQ(2, wcwidth(0x20000)); // Start of CJK extension B block.
EXPECT_EQ(2, wcwidth(0x2a6df)); // End of CJK extension B block.
}
TEST(wchar, wcwidth_korean_jeongeul_syllables) {
EXPECT_EQ(2, wcwidth(0xac00)); // Start of block.
EXPECT_EQ(2, wcwidth(0xd7a3)); // End of defined code points as of Unicode 15.
// Undefined characters at the end of the block currently have width 1, // but since they're undefined, we don't test that.
}
TEST(wchar, wcwidth_hangeul_compatibility_jamo) { // These are actually the *compatibility* jamo code points, *not* the regular // jamo code points (U+1100-U+11FF) using a jungseong filler. If you use the // Android IME to type any of these, you get these code points.
// (Half of) the Korean "crying" emoticon "ㅠㅠ". // Actually U+3160 "Hangeul Letter Yu" from Hangeul Compatibility Jamo.
EXPECT_EQ(2, wcwidth(L'ㅠ')); // The two halves of the Korean internet shorthand "ㄱㅅ" (short for 감사). // Actually U+3131 "Hangeul Letter Kiyeok" and U+3145 "Hangeul Letter Sios" // from Hangeul Compatibility Jamo.
EXPECT_EQ(2, wcwidth(L'ㄱ'));
EXPECT_EQ(2, wcwidth(L'ㅅ'));
}
TEST(wchar, wcslen) {
constexpr size_t array_len = 256 / sizeof(wchar_t); wchar_t wide_str[array_len]; for (size_t i = 0; i < array_len; ++i) {
wide_str[i] = i + 1;
}
for (size_t i = 0; i < array_len - 1; ++i) { constwchar_t old = wide_str[i];
wide_str[i] = 0;
EXPECT_EQ(wcslen(wide_str), i);
wide_str[i] = old;
}
}
staticvoid DoWcslenTest(uint8_t* byte_buf, size_t byte_len) { // NOTE: This is intentionally left potentially-misaligned, because // historically bionic (and other BSD-derived libcs including iOS) allowed // it, though glibc does not.
size_t len = byte_len / sizeof(wchar_t); if (len >= 1) {
size_t pre_nul_len = len * sizeof(wchar_t) - sizeof(wchar_t);
memset(byte_buf, (32 + (byte_len % 96)), pre_nul_len); // Because this is unaligned, use `memset` to set the 0, rather than // casting to `wchar_t *` and doing an unaligned store. The `memset` is // preferred by the standard & sanitizers.
memset(byte_buf + pre_nul_len, 0, sizeof(wchar_t));
EXPECT_EQ(len - 1, wcslen(reinterpret_cast<constwchar_t*>(byte_buf)));
}
}
staticvoid DoWmemchrTest(uint8_t* byte_buf, size_t byte_len) { // Much like DoWcslenTest, this intentionally supports unaligned buffers, for // compatibility with other libcs. const size_t len = byte_len / sizeof(wchar_t); if (!len) { return;
}
// We special-case wmemset() to 0, so we need to test that explicitly.
TEST(wchar, wmemset_0) { wchar_t dst[4] = { 0x12345678, 0x12345678, 0x12345678, 0x12345678 };
ASSERT_EQ(dst, wmemset(dst, 0, 3));
ASSERT_EQ(dst[0], wchar_t(0));
ASSERT_EQ(dst[1], wchar_t(0));
ASSERT_EQ(dst[2], wchar_t(0));
ASSERT_EQ(dst[3], wchar_t(0x12345678)); // A zero length touches nothing.
dst[0] = wchar_t(0x12345678);
ASSERT_EQ(dst, wmemset(dst, 0, 0));
ASSERT_EQ(dst[0], wchar_t(0x12345678));
}
TEST(wchar, btowc) { // This function only works for single-byte "wide" characters.
ASSERT_EQ(wint_t('a'), btowc('a')); // It _truncates_ the input to unsigned char.
ASSERT_EQ(wint_t(0x66), btowc(0x666)); // And rejects anything with the top bit set.
ASSERT_EQ(WEOF, btowc(0xa0));
}
TEST(wchar, wctob) { // This function only works for single-byte "wide" characters.
ASSERT_EQ('a', wctob(L'a')); // And rejects anything that would have the top bit set.
ASSERT_EQ(EOF, wctob(0xa0)); // There's no truncation here (unlike btowc()), // so this is rejected rather than seen as 0x66 ('f').
ASSERT_EQ(EOF, wctob(0x666));
}
// The "transform" of two different strings should cause different outputs.
ASSERT_TRUE(wcscmp(dst1, dst2) < 0);
}
TEST(wchar, strxfrm_l_smoke) { // bionic just forwards to wcsxfrm(3), so this is a subset of the // strxfrm test. constwchar_t* src1 = L"aab"; wchar_t dst1[16] = {};
ASSERT_EQ(wcsxfrm_l(dst1, src1, 0, LC_GLOBAL_LOCALE), 3U);
ASSERT_STREQ(dst1, L"");
ASSERT_EQ(wcsxfrm_l(dst1, src1, sizeof(dst1)/sizeof(wchar_t), LC_GLOBAL_LOCALE), 3U);
}
// wcstok() doesn't return empty strings. wchar_t str[] = L":hello:world:::foo:";
EXPECT_STREQ(L"hello", wcstok(str, L":", &p));
EXPECT_STREQ(L"world", wcstok(nullptr, L":", &p));
EXPECT_STREQ(L"foo", wcstok(nullptr, L":", &p));
EXPECT_EQ(nullptr, wcstok(nullptr, L":", &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, wcstok(nullptr, L":", &p));
}
// Some existing implementations have a separate return path for this. wchar_t non_empty_at_end[] = L"hello:world";
EXPECT_STREQ(L"hello", wcstok(non_empty_at_end, L":", &p));
EXPECT_STREQ(L"world", wcstok(nullptr, L":", &p)); for (size_t i = 0; i < 1024; ++i) {
EXPECT_EQ(nullptr, p);
EXPECT_EQ(nullptr, wcstok(nullptr, L":", &p));
}
}
TEST(wchar, fwide_byte) {
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("/proc/version", "re"), fclose); // Unknown orientation.
EXPECT_EQ(0, fwide(fp.get(), 0)); // Getting a byte sets the orientation to bytes.
EXPECT_EQ('L', fgetc(fp.get()));
EXPECT_TRUE(fwide(fp.get(), 0) < 0); // We don't prevent you from mixing and matching...
EXPECT_EQ(wint_t(L'i'), fgetwc(fp.get())); // ...but fwide() remembers what you _first_ did.
EXPECT_TRUE(fwide(fp.get(), 0) < 0);
}
TEST(wchar, fwide_wide_char) {
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("/proc/version", "re"), fclose); // Unknown orientation.
EXPECT_EQ(0, fwide(fp.get(), 0)); // Getting a wide character sets the orientation to wide.
EXPECT_EQ(wint_t(L'L'), fgetwc(fp.get()));
EXPECT_TRUE(fwide(fp.get(), 0) > 0); // We don't prevent you from mixing and matching...
EXPECT_EQ('i', fgetc(fp.get())); // ...but fwide() remembers what you _first_ did.
EXPECT_TRUE(fwide(fp.get(), 0) > 0);
}
TEST(wchar, fwide_set_byte) {
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("/proc/version", "re"), fclose); // Unknown orientation.
EXPECT_EQ(0, fwide(fp.get(), 0)); // You can set it to what you want...
EXPECT_TRUE(fwide(fp.get(), -123) < 0); // But only once...
EXPECT_TRUE(fwide(fp.get(), 123) < 0); // And you can still use the stream however you like.
EXPECT_EQ('L', fgetc(fp.get()));
EXPECT_EQ(wint_t(L'i'), fgetwc(fp.get()));
}
TEST(wchar, fwide_set_wide_char) {
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("/proc/version", "re"), fclose); // Unknown orientation.
EXPECT_EQ(0, fwide(fp.get(), 0)); // You can set it to what you want...
EXPECT_TRUE(fwide(fp.get(), 123) > 0); // But only once...
EXPECT_TRUE(fwide(fp.get(), -123) > 0); // And you can still use the stream however you like.
EXPECT_EQ('L', fgetc(fp.get()));
EXPECT_EQ(wint_t(L'i'), fgetwc(fp.get()));
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.31 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.