[libcxx-commits] [libcxx] 8b9d8df - [libc++] Correctly implement __transform_primary on all platforms (#186130)
via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Aug 4 03:14:19 PDT 2026
Author: aokblast
Date: 2026-08-04T18:14:14+08:00
New Revision: 8b9d8dfe208f08ffa83321d8b8fbb3a06c98a6df
URL: https://github.com/llvm/llvm-project/commit/8b9d8dfe208f08ffa83321d8b8fbb3a06c98a6df
DIFF: https://github.com/llvm/llvm-project/commit/8b9d8dfe208f08ffa83321d8b8fbb3a06c98a6df.diff
LOG: [libc++] Correctly implement __transform_primary on all platforms (#186130)
Added:
Modified:
libcxx/include/__cxx03/regex
libcxx/include/regex
libcxx/test/std/re/re.traits/transform_primary.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/include/__cxx03/regex b/libcxx/include/__cxx03/regex
index bbd6eeee19ee9..a0564c91e26c0 100644
--- a/libcxx/include/__cxx03/regex
+++ b/libcxx/include/__cxx03/regex
@@ -1108,6 +1108,26 @@ typename regex_traits<_CharT>::string_type
regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, char) const {
const string_type __s(__f, __l);
string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
+#if defined(__FreeBSD__) || defined(__APPLE__)
+ // FreeBSD's (and Darwin's, which shares the same collation code) strxfrm()
+ // emits the weights one collation level at a time, separating the levels with
+ // a byte ('.') that is chosen to sort below every weight byte. Primary
+ // equivalence only depends on the first (primary) level, so drop everything
+ // from the first separator onwards. This keeps characters that share a
+ // primary weight equal (e.g. 'A' and 'A-acute' in cs_CZ) even when the lower
+ // levels expand to a
diff erent number of weights, which the fixed-size logic
+ // below could not handle.
+ typename string_type::size_type __sep = __d.find('.');
+ if (__sep != string_type::npos)
+ __d.erase(__sep, string_type::npos);
+#elif defined(__GLIBC__)
+ // glibc's strxfrm() likewise writes the whole primary level first, then a
+ // '\1' level separator (and a trailing '\0' after the last level); real
+ // weight bytes are always >= 2. Keep only the primary level, as above.
+ typename string_type::size_type __sep = __d.find('\1');
+ if (__sep != string_type::npos)
+ __d.erase(__sep, string_type::npos);
+#else
switch (__d.size()) {
case 1:
break;
@@ -1118,6 +1138,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
__d.clear();
break;
}
+#endif
return __d;
}
@@ -1128,6 +1149,16 @@ typename regex_traits<_CharT>::string_type
regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, wchar_t) const {
const string_type __s(__f, __l);
string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
+# if defined(__FreeBSD__) || defined(__APPLE__) || defined(__GLIBC__)
+ // As in the char overload above, FreeBSD's, Darwin's and glibc's wcsxfrm() all
+ // separate the collation levels with a weight (the value 1) that sorts below
+ // every real weight. Keep only the primary level so primary-equivalent
+ // characters compare equal regardless of how many weights their lower levels
+ // expand to.
+ typename string_type::size_type __sep = __d.find(static_cast<_CharT>(1));
+ if (__sep != string_type::npos)
+ __d.erase(__sep, string_type::npos);
+# else
switch (__d.size()) {
case 1:
break;
@@ -1138,6 +1169,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
__d.clear();
break;
}
+# endif
return __d;
}
#endif
diff --git a/libcxx/include/regex b/libcxx/include/regex
index 86ead9143a25f..ce908f651e49b 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -1130,6 +1130,26 @@ typename regex_traits<_CharT>::string_type
regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, char) const {
const string_type __s(__f, __l);
string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
+# if defined(__FreeBSD__) || defined(__APPLE__)
+ // FreeBSD's (and Darwin's, which shares the same collation code) strxfrm()
+ // emits the weights one collation level at a time, separating the levels with
+ // a byte ('.') that is chosen to sort below every weight byte. Primary
+ // equivalence only depends on the first (primary) level, so drop everything
+ // from the first separator onwards. This keeps characters that share a
+ // primary weight equal (e.g. 'A' and 'A-acute' in cs_CZ) even when the lower
+ // levels expand to a
diff erent number of weights, which the fixed-size logic
+ // below could not handle.
+ typename string_type::size_type __sep = __d.find('.');
+ if (__sep != string_type::npos)
+ __d.erase(__sep, string_type::npos);
+# elif defined(__GLIBC__)
+ // glibc's strxfrm() likewise writes the whole primary level first, then a
+ // '\1' level separator (and a trailing '\0' after the last level); real
+ // weight bytes are always >= 2. Keep only the primary level, as above.
+ typename string_type::size_type __sep = __d.find('\1');
+ if (__sep != string_type::npos)
+ __d.erase(__sep, string_type::npos);
+# else
switch (__d.size()) {
case 1:
break;
@@ -1140,6 +1160,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
__d.clear();
break;
}
+# endif
return __d;
}
@@ -1150,6 +1171,16 @@ typename regex_traits<_CharT>::string_type
regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, wchar_t) const {
const string_type __s(__f, __l);
string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
+# if defined(__FreeBSD__) || defined(__APPLE__) || defined(__GLIBC__)
+ // As in the char overload above, FreeBSD's, Darwin's and glibc's wcsxfrm() all
+ // separate the collation levels with a weight (the value 1) that sorts below
+ // every real weight. Keep only the primary level so primary-equivalent
+ // characters compare equal regardless of how many weights their lower levels
+ // expand to.
+ typename string_type::size_type __sep = __d.find(static_cast<_CharT>(1));
+ if (__sep != string_type::npos)
+ __d.erase(__sep, string_type::npos);
+# else
switch (__d.size()) {
case 1:
break;
@@ -1160,6 +1191,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
__d.clear();
break;
}
+# endif
return __d;
}
# endif
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..b86804b6eed3a 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -10,7 +10,6 @@
// XFAIL: netbsd
// XFAIL: LIBCXX-AIX-FIXME
-// XFAIL: LIBCXX-FREEBSD-FIXME
// REQUIRES: locale.cs_CZ.ISO8859-2
@@ -34,25 +33,53 @@ int main(int, char**)
{
std::regex_traits<char> t;
const char A[] = "A";
- const char Aacute[] = "\xC1";
+ const char AA[] = "AA";
+ const char B[] = "B";
+ const char BB[] = "BB";
+ const char Aacute[] = "\xC1";
+ const char AAacute[] = "\xC1\xC1";
typedef forward_iterator<const char*> F;
assert(t.transform_primary(F(A), F(A+1)) !=
t.transform_primary(F(Aacute), F(Aacute+1)));
+ assert(t.transform_primary(F(B), F(B + 1)) != t.transform_primary(F(Aacute), F(Aacute + 1)));
+ assert(t.transform_primary(F(A), F(A + 1)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+ assert(t.transform_primary(F(AA), F(AA + 2)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+ assert(t.transform_primary(F(BB), F(BB + 2)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+ assert(t.transform_primary(F(AA), F(AA + 2)) != t.transform_primary(F(BB), F(BB + 2)));
t.imbue(std::locale(LOCALE_cs_CZ_ISO8859_2));
assert(t.transform_primary(F(A), F(A+1)) ==
t.transform_primary(F(Aacute), F(Aacute+1)));
+ assert(t.transform_primary(F(B), F(B + 1)) != t.transform_primary(F(Aacute), F(Aacute + 1)));
+ assert(t.transform_primary(F(A), F(A + 1)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+ assert(t.transform_primary(F(AA), F(AA + 2)) == t.transform_primary(F(AAacute), F(AAacute + 2)));
+ assert(t.transform_primary(F(BB), F(BB + 2)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+ assert(t.transform_primary(F(AA), F(AA + 2)) != t.transform_primary(F(BB), F(BB + 2)));
}
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
{
std::regex_traits<wchar_t> t;
const wchar_t A[] = L"A";
+ const wchar_t AA[] = L"AA";
+ const wchar_t B[] = L"B";
+ const wchar_t BB[] = L"BB";
const wchar_t Aacute[] = L"\xC1";
+ const wchar_t AAacute[] = L"\xC1\xC1";
typedef forward_iterator<const wchar_t*> F;
assert(t.transform_primary(F(A), F(A+1)) !=
t.transform_primary(F(Aacute), F(Aacute+1)));
+ assert(t.transform_primary(F(B), F(B + 1)) != t.transform_primary(F(Aacute), F(Aacute + 1)));
+ assert(t.transform_primary(F(A), F(A + 1)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+ assert(t.transform_primary(F(AA), F(AA + 2)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+ assert(t.transform_primary(F(BB), F(BB + 2)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+ assert(t.transform_primary(F(AA), F(AA + 2)) != t.transform_primary(F(BB), F(BB + 2)));
t.imbue(std::locale(LOCALE_cs_CZ_ISO8859_2));
assert(t.transform_primary(F(A), F(A+1)) ==
t.transform_primary(F(Aacute), F(Aacute+1)));
+ assert(t.transform_primary(F(B), F(B + 1)) != t.transform_primary(F(Aacute), F(Aacute + 1)));
+ assert(t.transform_primary(F(A), F(A + 1)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+ assert(t.transform_primary(F(AA), F(AA + 2)) == t.transform_primary(F(AAacute), F(AAacute + 2)));
+ assert(t.transform_primary(F(BB), F(BB + 2)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+ assert(t.transform_primary(F(AA), F(AA + 2)) != t.transform_primary(F(BB), F(BB + 2)));
}
#endif
More information about the libcxx-commits
mailing list