[libcxx-commits] [libcxx] [libcxx][FreeBSD] Fix __transform_primary in FreeBSD (PR #186130)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jun 26 08:09:39 PDT 2026
https://github.com/aokblast updated https://github.com/llvm/llvm-project/pull/186130
>From 28c99ae55416b428da6b76c23fe44646e32aa65a Mon Sep 17 00:00:00 2001
From: aokblast <aokblast at FreeBSD.org>
Date: Sat, 6 Jun 2026 00:55:42 +0800
Subject: [PATCH 01/10] Fix __transform_primary in FreeBSD
FreeBSD's strxfrm() encodes collation weights one level at a time,
separating the primary, secondary, and tertiary with '.' bytes. Since
primary equivalence only depends on the primary collation weight, ignore
everything after the first separator when constructing the transformed
key.
This patch the intended behavior of primary equivalence and avoids
relying on glibc's fixed-size collation-key representation.
---
libcxx/include/regex | 23 +++++++++++++++++++
.../re/re.traits/transform_primary.pass.cpp | 1 -
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/regex b/libcxx/include/regex
index 695a0f21754c4..91968a5c87c74 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -1130,6 +1130,18 @@ 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__)
+ // FreeBSD's 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 different 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);
+# else
switch (__d.size()) {
case 1:
break;
@@ -1140,6 +1152,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
__d.clear();
break;
}
+# endif
return __d;
}
@@ -1150,6 +1163,15 @@ 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__)
+ // As in the char overload above, FreeBSD's wcsxfrm() separates 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);
+# else
switch (__d.size()) {
case 1:
break;
@@ -1160,6 +1182,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..ff866e6031b4a 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
>From 3178b71ff195b6ce2d1d9dcfb92d42294bf223aa Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Wed, 24 Jun 2026 02:55:40 +0800
Subject: [PATCH 02/10] fixup! Fix __transform_primary in FreeBSD
---
libcxx/include/regex | 4 ++--
libcxx/test/std/re/re.traits/transform_primary.pass.cpp | 6 ++++++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/regex b/libcxx/include/regex
index 6f5109a64da22..ab06bd6387b6f 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -1140,7 +1140,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
// 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);
+ __d.erase(__sep, string_type::npos);
# else
switch (__d.size()) {
case 1:
@@ -1170,7 +1170,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
// 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);
+ __d.erase(__sep, string_type::npos);
# else
switch (__d.size()) {
case 1:
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 ff866e6031b4a..26bddb6636b12 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -33,25 +33,31 @@ int main(int, char**)
{
std::regex_traits<char> t;
const char A[] = "A";
+ const char B[] = "B";
const char Aacute[] = "\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)));
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)));
}
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
{
std::regex_traits<wchar_t> t;
const wchar_t A[] = L"A";
+ const wchar_t B[] = L"B";
const wchar_t Aacute[] = L"\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)));
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)));
}
#endif
>From bd3b5da3a74d847ca3cbf8020033618ca833d79d Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Wed, 24 Jun 2026 02:58:34 +0800
Subject: [PATCH 03/10] fixup! Fix __transform_primary in FreeBSD
---
libcxx/include/regex | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/regex b/libcxx/include/regex
index ab06bd6387b6f..38ce1fb715ad6 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -1140,7 +1140,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
// 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);
+ __d.erase(__sep, string_type::npos);
# else
switch (__d.size()) {
case 1:
>From 07fcb3a18ecb929219796f6c39a1d0e3bf0914df Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Wed, 24 Jun 2026 03:00:31 +0800
Subject: [PATCH 04/10] fixup! Fix __transform_primary in FreeBSD
---
libcxx/test/std/re/re.traits/transform_primary.pass.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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 26bddb6636b12..82ae7f7e77372 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -33,7 +33,7 @@ int main(int, char**)
{
std::regex_traits<char> t;
const char A[] = "A";
- const char B[] = "B";
+ const char B[] = "B";
const char Aacute[] = "\xC1";
typedef forward_iterator<const char*> F;
assert(t.transform_primary(F(A), F(A+1)) !=
@@ -48,7 +48,7 @@ int main(int, char**)
{
std::regex_traits<wchar_t> t;
const wchar_t A[] = L"A";
- const wchar_t B[] = L"B";
+ const wchar_t B[] = L"B";
const wchar_t Aacute[] = L"\xC1";
typedef forward_iterator<const wchar_t*> F;
assert(t.transform_primary(F(A), F(A+1)) !=
>From c30dbd19cdaef941866e96d8cafdd4ddebc9e4c4 Mon Sep 17 00:00:00 2001
From: aokblast <aokblast at FreeBSD.org>
Date: Tue, 23 Jun 2026 16:41:55 -0300
Subject: [PATCH 05/10] fixup! Fix __transform_primary in FreeBSD
---
libcxx/include/regex | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/libcxx/include/regex b/libcxx/include/regex
index 38ce1fb715ad6..43731926e85fb 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -1141,6 +1141,13 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
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:
@@ -1163,11 +1170,11 @@ 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__)
- // As in the char overload above, FreeBSD's wcsxfrm() separates 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.
+# if defined(__FreeBSD__) || defined(__GLIBC__)
+ // As in the char overload above, both FreeBSD's and glibc's wcsxfrm() 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);
>From 16aca8fbbbfead0ad721c1d3e379620e7c39cb0a Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Wed, 24 Jun 2026 21:57:19 +0800
Subject: [PATCH 06/10] fixup! Fix __transform_primary in FreeBSD
---
.../re/re.traits/transform_primary.pass.cpp | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
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 82ae7f7e77372..4e8ad5a73fb2a 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -33,31 +33,53 @@ int main(int, char**)
{
std::regex_traits<char> t;
const char A[] = "A";
+ 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
>From 2d49f4f60fff4439e49cffc521547712e86df870 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Thu, 25 Jun 2026 00:55:19 +0800
Subject: [PATCH 07/10] fixup! Fix __transform_primary in FreeBSD
---
libcxx/include/__cxx03/regex | 30 +++++++++++++++++++
.../re/re.traits/transform_primary.pass.cpp | 4 +--
2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__cxx03/regex b/libcxx/include/__cxx03/regex
index bbd6eeee19ee9..6489c01929cee 100644
--- a/libcxx/include/__cxx03/regex
+++ b/libcxx/include/__cxx03/regex
@@ -1108,6 +1108,25 @@ 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__)
+ // FreeBSD's 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 different 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 +1137,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
__d.clear();
break;
}
+#endif
return __d;
}
@@ -1128,6 +1148,15 @@ 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(__GLIBC__)
+ // As in the char overload above, both FreeBSD's and glibc's wcsxfrm() 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 +1167,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 4e8ad5a73fb2a..d0e96132b623d 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -36,7 +36,7 @@ int main(int, char**)
const char AA[] = "AA";
const char B[] = "B";
const char BB[] = "BB";
- const char Aacute[] = "\xC1";
+ const char Aacute[] = "\xC1";
const char AAacute[] = "\xC1\xC1";
typedef forward_iterator<const char*> F;
assert(t.transform_primary(F(A), F(A+1)) !=
@@ -60,7 +60,7 @@ int main(int, char**)
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 B[] = L"B";
const wchar_t BB[] = L"BB";
const wchar_t Aacute[] = L"\xC1";
const wchar_t AAacute[] = L"\xC1\xC1";
>From a69b6c5ef014dd0678028bd6ae603c1b1ba1336a Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Thu, 25 Jun 2026 01:00:06 +0800
Subject: [PATCH 08/10] fixup! Fix __transform_primary in FreeBSD
---
libcxx/include/__cxx03/regex | 4 ++--
libcxx/test/std/re/re.traits/transform_primary.pass.cpp | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__cxx03/regex b/libcxx/include/__cxx03/regex
index 6489c01929cee..88fd6fd46e158 100644
--- a/libcxx/include/__cxx03/regex
+++ b/libcxx/include/__cxx03/regex
@@ -1137,7 +1137,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
__d.clear();
break;
}
-#endif
+# endif
return __d;
}
@@ -1167,7 +1167,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
__d.clear();
break;
}
-#endif
+# 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 d0e96132b623d..b86804b6eed3a 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -34,7 +34,7 @@ int main(int, char**)
std::regex_traits<char> t;
const char A[] = "A";
const char AA[] = "AA";
- const char B[] = "B";
+ const char B[] = "B";
const char BB[] = "BB";
const char Aacute[] = "\xC1";
const char AAacute[] = "\xC1\xC1";
>From 34d42ae2fbb3ae7e036f78230974439ec317b1be Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Thu, 25 Jun 2026 01:04:56 +0800
Subject: [PATCH 09/10] fixup! Fix __transform_primary in FreeBSD
---
libcxx/include/__cxx03/regex | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/libcxx/include/__cxx03/regex b/libcxx/include/__cxx03/regex
index 88fd6fd46e158..9ada638babf45 100644
--- a/libcxx/include/__cxx03/regex
+++ b/libcxx/include/__cxx03/regex
@@ -1108,7 +1108,7 @@ 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__)
+#if defined(__FreeBSD__)
// FreeBSD's 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)
@@ -1119,14 +1119,14 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
typename string_type::size_type __sep = __d.find('.');
if (__sep != string_type::npos)
__d.erase(__sep, string_type::npos);
-# elif defined(__GLIBC__)
+#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
+#else
switch (__d.size()) {
case 1:
break;
@@ -1137,7 +1137,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
__d.clear();
break;
}
-# endif
+#endif
return __d;
}
@@ -1148,7 +1148,7 @@ 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(__GLIBC__)
+# if defined(__FreeBSD__) || defined(__GLIBC__)
// As in the char overload above, both FreeBSD's and glibc's wcsxfrm() 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
@@ -1156,7 +1156,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
typename string_type::size_type __sep = __d.find(static_cast<_CharT>(1));
if (__sep != string_type::npos)
__d.erase(__sep, string_type::npos);
-# else
+# else
switch (__d.size()) {
case 1:
break;
@@ -1167,7 +1167,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
__d.clear();
break;
}
-# endif
+# endif
return __d;
}
#endif
>From e25ab48dcb1b3f7ecb15b2941b29d822c82a7b9f Mon Sep 17 00:00:00 2001
From: aokblast <aokblast at FreeBSD.org>
Date: Fri, 26 Jun 2026 12:09:14 -0300
Subject: [PATCH 10/10] fixup! Fix __transform_primary in FreeBSD
---
libcxx/include/__cxx03/regex | 28 +++++++++++++++-------------
libcxx/include/regex | 28 +++++++++++++++-------------
2 files changed, 30 insertions(+), 26 deletions(-)
diff --git a/libcxx/include/__cxx03/regex b/libcxx/include/__cxx03/regex
index 9ada638babf45..a0564c91e26c0 100644
--- a/libcxx/include/__cxx03/regex
+++ b/libcxx/include/__cxx03/regex
@@ -1108,14 +1108,15 @@ 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__)
- // FreeBSD's 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 different number of weights,
- // which the fixed-size logic below could not handle.
+#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 different 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);
@@ -1148,11 +1149,12 @@ 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(__GLIBC__)
- // As in the char overload above, both FreeBSD's and glibc's wcsxfrm() 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.
+# 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);
diff --git a/libcxx/include/regex b/libcxx/include/regex
index 43731926e85fb..57f670b24da38 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -1130,14 +1130,15 @@ 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__)
- // FreeBSD's 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 different number of weights,
- // which the fixed-size logic below could not handle.
+# 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 different 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);
@@ -1170,11 +1171,12 @@ 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(__GLIBC__)
- // As in the char overload above, both FreeBSD's and glibc's wcsxfrm() 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.
+# 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);
More information about the libcxx-commits
mailing list