From eb12245452c13baa3d1f70087dc206eed960e569 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 11:08:25 -0800 Subject: [PATCH 01/15] Drop `__CRTDECL` on internal header-only functions. `__CRTDECL (_LStr(coll|xfrm)|_Makloc(byte|chr|str)|_Getloctxt)` => `$1` These functions aren't dllexported or address-taken, so their calling convention doesn't matter. --- stl/inc/locale | 21 ++++++++++----------- stl/inc/xlocale | 21 ++++++++++----------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/stl/inc/locale b/stl/inc/locale index d14c40d368..460ae5c05e 100644 --- a/stl/inc/locale +++ b/stl/inc/locale @@ -23,7 +23,7 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN template -int __CRTDECL _LStrcoll(const _Elem* _First1, const _Elem* _Last1, const _Elem* _First2, const _Elem* _Last2, +int _LStrcoll(const _Elem* _First1, const _Elem* _Last1, const _Elem* _First2, const _Elem* _Last2, const _Locinfo::_Collvec*) { // perform locale-specific comparison of _Elem sequences for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, ++_First2) { if (*_First1 < *_First2) { @@ -37,22 +37,21 @@ int __CRTDECL _LStrcoll(const _Elem* _First1, const _Elem* _Last1, const _Elem* } template <> -inline int __CRTDECL _LStrcoll(const char* _First1, const char* _Last1, const char* _First2, const char* _Last2, +inline int _LStrcoll(const char* _First1, const char* _Last1, const char* _First2, const char* _Last2, const _Locinfo::_Collvec* _Vector) { // perform locale-specific comparison of char sequences return _Strcoll(_First1, _Last1, _First2, _Last2, _Vector); } template <> -inline int __CRTDECL _LStrcoll(const wchar_t* _First1, const wchar_t* _Last1, const wchar_t* _First2, - const wchar_t* _Last2, +inline int _LStrcoll(const wchar_t* _First1, const wchar_t* _Last1, const wchar_t* _First2, const wchar_t* _Last2, const _Locinfo::_Collvec* _Vector) { // perform locale-specific comparison of wchar_t sequences return _Wcscoll(_First1, _Last1, _First2, _Last2, _Vector); } #ifdef _CRTBLD template <> -inline int __CRTDECL _LStrcoll(const unsigned short* _First1, const unsigned short* _Last1, - const unsigned short* _First2, const unsigned short* _Last2, +inline int _LStrcoll(const unsigned short* _First1, const unsigned short* _Last1, const unsigned short* _First2, + const unsigned short* _Last2, const _Locinfo::_Collvec* _Vector) { // perform locale-specific comparison of unsigned short sequences return _Wcscoll(reinterpret_cast(_First1), reinterpret_cast(_Last1), reinterpret_cast(_First2), reinterpret_cast(_Last2), _Vector); @@ -60,7 +59,7 @@ inline int __CRTDECL _LStrcoll(const unsigned short* _First1, const unsigned sho #endif // defined(_CRTBLD) template -size_t __CRTDECL _LStrxfrm(_Elem* _First1, _Elem* _Last1, const _Elem* _First2, const _Elem* _Last2, +size_t _LStrxfrm(_Elem* _First1, _Elem* _Last1, const _Elem* _First2, const _Elem* _Last2, const _Locinfo::_Collvec*) { // perform locale-specific transform of _Elems [_First1, _Last1) const ptrdiff_t _Count = _Last2 - _First2; if (_Count <= _Last1 - _First1) { @@ -71,14 +70,14 @@ size_t __CRTDECL _LStrxfrm(_Elem* _First1, _Elem* _Last1, const _Elem* _First2, } template <> -inline size_t __CRTDECL _LStrxfrm(_Out_writes_(_Last1 - _First1) _Post_readable_size_(return) char* _First1, - _In_z_ char* _Last1, const char* _First2, const char* _Last2, +inline size_t _LStrxfrm(_Out_writes_(_Last1 - _First1) _Post_readable_size_(return) char* _First1, _In_z_ char* _Last1, + const char* _First2, const char* _Last2, const _Locinfo::_Collvec* _Vector) { // perform locale-specific transform of chars [_First1, _Last1) return _Strxfrm(_First1, _Last1, _First2, _Last2, _Vector); } template <> -inline size_t __CRTDECL _LStrxfrm(_Out_writes_(_Last1 - _First1) _Post_readable_size_(return) wchar_t* _First1, +inline size_t _LStrxfrm(_Out_writes_(_Last1 - _First1) _Post_readable_size_(return) wchar_t* _First1, _In_z_ wchar_t* _Last1, const wchar_t* _First2, const wchar_t* _Last2, const _Locinfo::_Collvec* _Vector) { // perform locale-specific transform of wchar_ts [_First1, _Last1) return _Wcsxfrm(_First1, _Last1, _First2, _Last2, _Vector); @@ -86,7 +85,7 @@ inline size_t __CRTDECL _LStrxfrm(_Out_writes_(_Last1 - _First1) _Post_readable_ #ifdef _CRTBLD template <> -inline size_t __CRTDECL _LStrxfrm(_Out_writes_(_Last1 - _First1) _Post_readable_size_(return) unsigned short* _First1, +inline size_t _LStrxfrm(_Out_writes_(_Last1 - _First1) _Post_readable_size_(return) unsigned short* _First1, _In_z_ unsigned short* _Last1, const unsigned short* _First2, const unsigned short* _Last2, const _Locinfo::_Collvec* _Vector) { // perform locale-specific transform of unsigned shorts [_First1, _Last1) return _Wcsxfrm(reinterpret_cast(_First1), reinterpret_cast(_Last1), diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 0409a1aa0d..96b0327fe3 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -471,13 +471,13 @@ const _Facet& __CRTDECL use_facet(const locale& _Loc) { // get facet reference f } // end of use_facet body template -char __CRTDECL _Maklocbyte(_Elem _Char, const _Locinfo::_Cvtvec&) { +char _Maklocbyte(_Elem _Char, const _Locinfo::_Cvtvec&) { // convert _Elem to char using _Cvtvec return static_cast(static_cast(_Char)); } template <> -inline char __CRTDECL _Maklocbyte(wchar_t _Char, const _Locinfo::_Cvtvec& _Cvt) { +inline char _Maklocbyte(wchar_t _Char, const _Locinfo::_Cvtvec& _Cvt) { // convert wchar_t to char using _Cvtvec char _Byte = '\0'; mbstate_t _Mbst1 = {}; @@ -487,7 +487,7 @@ inline char __CRTDECL _Maklocbyte(wchar_t _Char, const _Locinfo::_Cvtvec& _Cvt) #if defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS template <> -inline char __CRTDECL _Maklocbyte(unsigned short _Char, const _Locinfo::_Cvtvec& _Cvt) { +inline char _Maklocbyte(unsigned short _Char, const _Locinfo::_Cvtvec& _Cvt) { // convert unsigned short to char using _Cvtvec char _Byte = '\0'; mbstate_t _Mbst1 = {}; @@ -497,13 +497,13 @@ inline char __CRTDECL _Maklocbyte(unsigned short _Char, const _Locinfo::_Cvtvec& #endif // defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS template -_Elem __CRTDECL _Maklocchr(char _Byte, _Elem*, const _Locinfo::_Cvtvec&) { +_Elem _Maklocchr(char _Byte, _Elem*, const _Locinfo::_Cvtvec&) { // convert char to _Elem using _Cvtvec return static_cast<_Elem>(static_cast(_Byte)); } template <> -inline wchar_t __CRTDECL _Maklocchr(char _Byte, wchar_t*, const _Locinfo::_Cvtvec& _Cvt) { +inline wchar_t _Maklocchr(char _Byte, wchar_t*, const _Locinfo::_Cvtvec& _Cvt) { // convert char to wchar_t using _Cvtvec wchar_t _Wc = L'\0'; mbstate_t _Mbst1 = {}; @@ -513,7 +513,7 @@ inline wchar_t __CRTDECL _Maklocchr(char _Byte, wchar_t*, const _Locinfo::_Cvtve #if defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS template <> -inline unsigned short __CRTDECL _Maklocchr(char _Byte, unsigned short*, const _Locinfo::_Cvtvec& _Cvt) { +inline unsigned short _Maklocchr(char _Byte, unsigned short*, const _Locinfo::_Cvtvec& _Cvt) { // convert char to unsigned short using _Cvtvec unsigned short _Wc = 0; mbstate_t _Mbst1 = {}; @@ -523,7 +523,7 @@ inline unsigned short __CRTDECL _Maklocchr(char _Byte, unsigned short*, const _L #endif // defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS template -_Elem* __CRTDECL _Maklocstr(const char* _Ptr, _Elem*, const _Locinfo::_Cvtvec&) { +_Elem* _Maklocstr(const char* _Ptr, _Elem*, const _Locinfo::_Cvtvec&) { // convert C string to _Elem sequence using _Cvtvec size_t _Count = _CSTD strlen(_Ptr) + 1; @@ -541,7 +541,7 @@ _Elem* __CRTDECL _Maklocstr(const char* _Ptr, _Elem*, const _Locinfo::_Cvtvec&) } template <> -inline wchar_t* __CRTDECL _Maklocstr(const char* _Ptr, wchar_t*, const _Locinfo::_Cvtvec& _Cvt) { +inline wchar_t* _Maklocstr(const char* _Ptr, wchar_t*, const _Locinfo::_Cvtvec& _Cvt) { // convert C string to wchar_t sequence using _Cvtvec size_t _Count; size_t _Count1; @@ -582,7 +582,7 @@ inline wchar_t* __CRTDECL _Maklocstr(const char* _Ptr, wchar_t*, const _Locinfo: #if defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS template <> -inline unsigned short* __CRTDECL _Maklocstr(const char* _Ptr, unsigned short*, const _Locinfo::_Cvtvec& _Cvt) { +inline unsigned short* _Maklocstr(const char* _Ptr, unsigned short*, const _Locinfo::_Cvtvec& _Cvt) { // convert C string to unsigned short sequence using _Cvtvec size_t _Count; size_t _Count1; @@ -3309,8 +3309,7 @@ protected: enum class _Case_sensitive : bool { _Nope, _Yes }; template -int __CRTDECL _Getloctxt( - _InIt& _First, _InIt& _Last, size_t _Numfields, const _Elem* _Ptr, const _Case_sensitive _Matching) { +int _Getloctxt(_InIt& _First, _InIt& _Last, size_t _Numfields, const _Elem* _Ptr, const _Case_sensitive _Matching) { // find field at _Ptr that matches longest in [_First, _Last) for (size_t _Off = 0; _Ptr[_Off] != _Elem{}; ++_Off) { if (_Ptr[_Off] == _Ptr[0]) { From 0318b57cf9e1f324bfc1710ee5e1b7fcdf800148 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 24 Nov 2025 09:44:53 -0800 Subject: [PATCH 02/15] Unify with `if constexpr`, part 1: `_LStrcoll()` --- stl/inc/locale | 51 +++++++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/stl/inc/locale b/stl/inc/locale index 460ae5c05e..be5c7b974b 100644 --- a/stl/inc/locale +++ b/stl/inc/locale @@ -24,39 +24,30 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN template int _LStrcoll(const _Elem* _First1, const _Elem* _Last1, const _Elem* _First2, const _Elem* _Last2, - const _Locinfo::_Collvec*) { // perform locale-specific comparison of _Elem sequences - for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, ++_First2) { - if (*_First1 < *_First2) { - return -1; // [_First1, _Last1) < [_First2, _Last2) - } else if (*_First2 < *_First1) { - return +1; // [_First1, _Last1) > [_First2, _Last2) - } + const _Locinfo::_Collvec* _Vector) { // perform locale-specific comparison of _Elem sequences + if constexpr (is_same_v<_Elem, char>) { + return _Strcoll(_First1, _Last1, _First2, _Last2, _Vector); + } else if constexpr (is_same_v<_Elem, wchar_t>) { + return _Wcscoll(_First1, _Last1, _First2, _Last2, _Vector); } - - return _First2 != _Last2 ? -1 : _First1 != _Last1 ? +1 : 0; -} - -template <> -inline int _LStrcoll(const char* _First1, const char* _Last1, const char* _First2, const char* _Last2, - const _Locinfo::_Collvec* _Vector) { // perform locale-specific comparison of char sequences - return _Strcoll(_First1, _Last1, _First2, _Last2, _Vector); -} - -template <> -inline int _LStrcoll(const wchar_t* _First1, const wchar_t* _Last1, const wchar_t* _First2, const wchar_t* _Last2, - const _Locinfo::_Collvec* _Vector) { // perform locale-specific comparison of wchar_t sequences - return _Wcscoll(_First1, _Last1, _First2, _Last2, _Vector); -} - #ifdef _CRTBLD -template <> -inline int _LStrcoll(const unsigned short* _First1, const unsigned short* _Last1, const unsigned short* _First2, - const unsigned short* _Last2, - const _Locinfo::_Collvec* _Vector) { // perform locale-specific comparison of unsigned short sequences - return _Wcscoll(reinterpret_cast(_First1), reinterpret_cast(_Last1), - reinterpret_cast(_First2), reinterpret_cast(_Last2), _Vector); -} + else if constexpr (is_same_v<_Elem, unsigned short>) { + return _Wcscoll(reinterpret_cast(_First1), reinterpret_cast(_Last1), + reinterpret_cast(_First2), reinterpret_cast(_Last2), _Vector); + } #endif // defined(_CRTBLD) + else { + for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, ++_First2) { + if (*_First1 < *_First2) { + return -1; // [_First1, _Last1) < [_First2, _Last2) + } else if (*_First2 < *_First1) { + return +1; // [_First1, _Last1) > [_First2, _Last2) + } + } + + return _First2 != _Last2 ? -1 : _First1 != _Last1 ? +1 : 0; + } +} template size_t _LStrxfrm(_Elem* _First1, _Elem* _Last1, const _Elem* _First2, const _Elem* _Last2, From 5f2dd982f9dbfdb3444cd5c6c1b4e8ff9c0d2dc5 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 11:26:52 -0800 Subject: [PATCH 03/15] Unify with `if constexpr`, part 2: `_LStrxfrm()` --- stl/inc/locale | 50 ++++++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/stl/inc/locale b/stl/inc/locale index be5c7b974b..3c1de8760d 100644 --- a/stl/inc/locale +++ b/stl/inc/locale @@ -50,39 +50,29 @@ int _LStrcoll(const _Elem* _First1, const _Elem* _Last1, const _Elem* _First2, c } template -size_t _LStrxfrm(_Elem* _First1, _Elem* _Last1, const _Elem* _First2, const _Elem* _Last2, - const _Locinfo::_Collvec*) { // perform locale-specific transform of _Elems [_First1, _Last1) - const ptrdiff_t _Count = _Last2 - _First2; - if (_Count <= _Last1 - _First1) { - _CSTD memcpy(_First1, _First2, _Count * sizeof(_Elem)); +size_t _LStrxfrm( + _Elem* _First1, _Elem* _Last1, const _Elem* _First2, const _Elem* _Last2, const _Locinfo::_Collvec* _Vector) { + // perform locale-specific transform of _Elems [_First1, _Last1) + if constexpr (is_same_v<_Elem, char>) { + return _Strxfrm(_First1, _Last1, _First2, _Last2, _Vector); + } else if constexpr (is_same_v<_Elem, wchar_t>) { + return _Wcsxfrm(_First1, _Last1, _First2, _Last2, _Vector); } - - return _Count; -} - -template <> -inline size_t _LStrxfrm(_Out_writes_(_Last1 - _First1) _Post_readable_size_(return) char* _First1, _In_z_ char* _Last1, - const char* _First2, const char* _Last2, - const _Locinfo::_Collvec* _Vector) { // perform locale-specific transform of chars [_First1, _Last1) - return _Strxfrm(_First1, _Last1, _First2, _Last2, _Vector); -} - -template <> -inline size_t _LStrxfrm(_Out_writes_(_Last1 - _First1) _Post_readable_size_(return) wchar_t* _First1, - _In_z_ wchar_t* _Last1, const wchar_t* _First2, const wchar_t* _Last2, - const _Locinfo::_Collvec* _Vector) { // perform locale-specific transform of wchar_ts [_First1, _Last1) - return _Wcsxfrm(_First1, _Last1, _First2, _Last2, _Vector); -} - #ifdef _CRTBLD -template <> -inline size_t _LStrxfrm(_Out_writes_(_Last1 - _First1) _Post_readable_size_(return) unsigned short* _First1, - _In_z_ unsigned short* _Last1, const unsigned short* _First2, const unsigned short* _Last2, - const _Locinfo::_Collvec* _Vector) { // perform locale-specific transform of unsigned shorts [_First1, _Last1) - return _Wcsxfrm(reinterpret_cast(_First1), reinterpret_cast(_Last1), - reinterpret_cast(_First2), reinterpret_cast(_Last2), _Vector); -} + else if constexpr (is_same_v<_Elem, unsigned short>) { + return _Wcsxfrm(reinterpret_cast(_First1), reinterpret_cast(_Last1), + reinterpret_cast(_First2), reinterpret_cast(_Last2), _Vector); + } #endif // defined(_CRTBLD) + else { + const ptrdiff_t _Count = _Last2 - _First2; + if (_Count <= _Last1 - _First1) { + _CSTD memcpy(_First1, _First2, _Count * sizeof(_Elem)); + } + + return _Count; + } +} template class _Regex_traits; From fc2f5269dd02bf4697cd3b68ef022b68b38cd603 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 04:45:18 -0800 Subject: [PATCH 04/15] Unify with `if constexpr`, part 3: `_Maklocbyte`, `_Maklocchr`, `_Maklocstr` --- stl/inc/xlocale | 216 +++++++++++++++++++++++------------------------- 1 file changed, 103 insertions(+), 113 deletions(-) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 96b0327fe3..a85eada164 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -471,154 +471,144 @@ const _Facet& __CRTDECL use_facet(const locale& _Loc) { // get facet reference f } // end of use_facet body template -char _Maklocbyte(_Elem _Char, const _Locinfo::_Cvtvec&) { +char _Maklocbyte(_Elem _Char, const _Locinfo::_Cvtvec& _Cvt) { // convert _Elem to char using _Cvtvec - return static_cast(static_cast(_Char)); -} - -template <> -inline char _Maklocbyte(wchar_t _Char, const _Locinfo::_Cvtvec& _Cvt) { - // convert wchar_t to char using _Cvtvec - char _Byte = '\0'; - mbstate_t _Mbst1 = {}; - _Wcrtomb(&_Byte, _Char, &_Mbst1, &_Cvt); - return _Byte; -} - + if constexpr (is_same_v<_Elem, wchar_t>) { + char _Byte = '\0'; + mbstate_t _Mbst1 = {}; + _Wcrtomb(&_Byte, _Char, &_Mbst1, &_Cvt); + return _Byte; + } #if defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS -template <> -inline char _Maklocbyte(unsigned short _Char, const _Locinfo::_Cvtvec& _Cvt) { - // convert unsigned short to char using _Cvtvec - char _Byte = '\0'; - mbstate_t _Mbst1 = {}; - _Wcrtomb(&_Byte, static_cast(_Char), &_Mbst1, &_Cvt); - return _Byte; -} + else if constexpr (is_same_v<_Elem, unsigned short>) { + char _Byte = '\0'; + mbstate_t _Mbst1 = {}; + _Wcrtomb(&_Byte, static_cast(_Char), &_Mbst1, &_Cvt); + return _Byte; + } #endif // defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS + else { + return static_cast(static_cast(_Char)); + } +} template -_Elem _Maklocchr(char _Byte, _Elem*, const _Locinfo::_Cvtvec&) { +_Elem _Maklocchr(char _Byte, _Elem*, const _Locinfo::_Cvtvec& _Cvt) { // convert char to _Elem using _Cvtvec - return static_cast<_Elem>(static_cast(_Byte)); -} - -template <> -inline wchar_t _Maklocchr(char _Byte, wchar_t*, const _Locinfo::_Cvtvec& _Cvt) { - // convert char to wchar_t using _Cvtvec - wchar_t _Wc = L'\0'; - mbstate_t _Mbst1 = {}; - _Mbrtowc(&_Wc, &_Byte, 1, &_Mbst1, &_Cvt); - return _Wc; -} - + if constexpr (is_same_v<_Elem, wchar_t>) { + wchar_t _Wc = L'\0'; + mbstate_t _Mbst1 = {}; + _Mbrtowc(&_Wc, &_Byte, 1, &_Mbst1, &_Cvt); + return _Wc; + } #if defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS -template <> -inline unsigned short _Maklocchr(char _Byte, unsigned short*, const _Locinfo::_Cvtvec& _Cvt) { - // convert char to unsigned short using _Cvtvec - unsigned short _Wc = 0; - mbstate_t _Mbst1 = {}; - _Mbrtowc(reinterpret_cast(&_Wc), &_Byte, 1, &_Mbst1, &_Cvt); - return _Wc; -} + else if constexpr (is_same_v<_Elem, unsigned short>) { + unsigned short _Wc = 0; + mbstate_t _Mbst1 = {}; + _Mbrtowc(reinterpret_cast(&_Wc), &_Byte, 1, &_Mbst1, &_Cvt); + return _Wc; + } #endif // defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS + else { + return static_cast<_Elem>(static_cast(_Byte)); + } +} template -_Elem* _Maklocstr(const char* _Ptr, _Elem*, const _Locinfo::_Cvtvec&) { +_Elem* _Maklocstr(const char* _Ptr, _Elem*, const _Locinfo::_Cvtvec& _Cvt) { // convert C string to _Elem sequence using _Cvtvec - size_t _Count = _CSTD strlen(_Ptr) + 1; + if constexpr (is_same_v<_Elem, wchar_t>) { + size_t _Count; + size_t _Count1; + size_t _Wchars; + const char* _Ptr1; + int _Bytes; + wchar_t _Wc; + mbstate_t _Mbst1 = {}; - _Elem* _Ptrdest = static_cast<_Elem*>(_calloc_dbg(_Count, sizeof(_Elem), _CRT_BLOCK, __FILE__, __LINE__)); + _Count1 = _CSTD strlen(_Ptr) + 1; + for (_Count = _Count1, _Wchars = 0, _Ptr1 = _Ptr; 0 < _Count; _Count -= _Bytes, _Ptr1 += _Bytes, ++_Wchars) { + if ((_Bytes = _Mbrtowc(&_Wc, _Ptr1, _Count, &_Mbst1, &_Cvt)) <= 0) { + break; + } + } - if (!_Ptrdest) { - _Xbad_alloc(); - } + ++_Wchars; // count terminating nul - for (_Elem* _Ptrnext = _Ptrdest; 0 < _Count; --_Count, ++_Ptrnext, ++_Ptr) { - *_Ptrnext = static_cast<_Elem>(static_cast(*_Ptr)); - } + wchar_t* _Ptrdest = + static_cast(_calloc_dbg(_Wchars, sizeof(wchar_t), _CRT_BLOCK, __FILE__, __LINE__)); - return _Ptrdest; -} + if (!_Ptrdest) { + _Xbad_alloc(); + } -template <> -inline wchar_t* _Maklocstr(const char* _Ptr, wchar_t*, const _Locinfo::_Cvtvec& _Cvt) { - // convert C string to wchar_t sequence using _Cvtvec - size_t _Count; - size_t _Count1; - size_t _Wchars; - const char* _Ptr1; - int _Bytes; - wchar_t _Wc; - mbstate_t _Mbst1 = {}; + wchar_t* _Ptrnext = _Ptrdest; + mbstate_t _Mbst2 = {}; - _Count1 = _CSTD strlen(_Ptr) + 1; - for (_Count = _Count1, _Wchars = 0, _Ptr1 = _Ptr; 0 < _Count; _Count -= _Bytes, _Ptr1 += _Bytes, ++_Wchars) { - if ((_Bytes = _Mbrtowc(&_Wc, _Ptr1, _Count, &_Mbst1, &_Cvt)) <= 0) { - break; + for (; 0 < _Wchars; _Count -= _Bytes, _Ptr += _Bytes, --_Wchars, ++_Ptrnext) { + if ((_Bytes = _Mbrtowc(_Ptrnext, _Ptr, _Count1, &_Mbst2, &_Cvt)) <= 0) { + break; + } } - } - ++_Wchars; // count terminating nul + *_Ptrnext = L'\0'; - wchar_t* _Ptrdest = static_cast(_calloc_dbg(_Wchars, sizeof(wchar_t), _CRT_BLOCK, __FILE__, __LINE__)); - - if (!_Ptrdest) { - _Xbad_alloc(); + return _Ptrdest; } +#if defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS + else if constexpr (is_same_v<_Elem, unsigned short>) { + size_t _Count; + size_t _Count1; + size_t _Wchars; + const char* _Ptr1; + int _Bytes; + unsigned short _Wc; + mbstate_t _Mbst1 = {}; - wchar_t* _Ptrnext = _Ptrdest; - mbstate_t _Mbst2 = {}; - - for (; 0 < _Wchars; _Count -= _Bytes, _Ptr += _Bytes, --_Wchars, ++_Ptrnext) { - if ((_Bytes = _Mbrtowc(_Ptrnext, _Ptr, _Count1, &_Mbst2, &_Cvt)) <= 0) { - break; + _Count1 = _CSTD strlen(_Ptr) + 1; + for (_Count = _Count1, _Wchars = 0, _Ptr1 = _Ptr; 0 < _Count; _Count -= _Bytes, _Ptr1 += _Bytes, ++_Wchars) { + if ((_Bytes = _Mbrtowc(reinterpret_cast(&_Wc), _Ptr1, _Count, &_Mbst1, &_Cvt)) <= 0) { + break; + } } - } - *_Ptrnext = L'\0'; + ++_Wchars; // count terminating nul - return _Ptrdest; -} + wchar_t* _Ptrdest = + static_cast(_calloc_dbg(_Wchars, sizeof(wchar_t), _CRT_BLOCK, __FILE__, __LINE__)); -#if defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS -template <> -inline unsigned short* _Maklocstr(const char* _Ptr, unsigned short*, const _Locinfo::_Cvtvec& _Cvt) { - // convert C string to unsigned short sequence using _Cvtvec - size_t _Count; - size_t _Count1; - size_t _Wchars; - const char* _Ptr1; - int _Bytes; - unsigned short _Wc; - mbstate_t _Mbst1 = {}; + if (!_Ptrdest) { + _Xbad_alloc(); + } - _Count1 = _CSTD strlen(_Ptr) + 1; - for (_Count = _Count1, _Wchars = 0, _Ptr1 = _Ptr; 0 < _Count; _Count -= _Bytes, _Ptr1 += _Bytes, ++_Wchars) { - if ((_Bytes = _Mbrtowc(reinterpret_cast(&_Wc), _Ptr1, _Count, &_Mbst1, &_Cvt)) <= 0) { - break; + wchar_t* _Ptrnext = _Ptrdest; + mbstate_t _Mbst2 = {}; + for (; 0 < _Wchars; _Count -= _Bytes, _Ptr += _Bytes, --_Wchars, ++_Ptrnext) { + if ((_Bytes = _Mbrtowc(_Ptrnext, _Ptr, _Count1, &_Mbst2, &_Cvt)) <= 0) { + break; + } } - } - ++_Wchars; // count terminating nul + *_Ptrnext = L'\0'; + return reinterpret_cast(_Ptrdest); + } +#endif // defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS + else { + size_t _Count = _CSTD strlen(_Ptr) + 1; - wchar_t* _Ptrdest = static_cast(_calloc_dbg(_Wchars, sizeof(wchar_t), _CRT_BLOCK, __FILE__, __LINE__)); + _Elem* _Ptrdest = static_cast<_Elem*>(_calloc_dbg(_Count, sizeof(_Elem), _CRT_BLOCK, __FILE__, __LINE__)); - if (!_Ptrdest) { - _Xbad_alloc(); - } + if (!_Ptrdest) { + _Xbad_alloc(); + } - wchar_t* _Ptrnext = _Ptrdest; - mbstate_t _Mbst2 = {}; - for (; 0 < _Wchars; _Count -= _Bytes, _Ptr += _Bytes, --_Wchars, ++_Ptrnext) { - if ((_Bytes = _Mbrtowc(_Ptrnext, _Ptr, _Count1, &_Mbst2, &_Cvt)) <= 0) { - break; + for (_Elem* _Ptrnext = _Ptrdest; 0 < _Count; --_Count, ++_Ptrnext, ++_Ptr) { + *_Ptrnext = static_cast<_Elem>(static_cast(*_Ptr)); } - } - *_Ptrnext = L'\0'; - return reinterpret_cast(_Ptrdest); + return _Ptrdest; + } } -#endif // defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS _EXPORT_STD extern "C++" class _CRTIMP2_PURE_IMPORT codecvt_base // base class for codecvt : public locale::facet // TRANSITION, ABI, shouldn't be derived from locale::facet From fd5a8c00368e18f4d4c01c959ce7edeb9b3c2392 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 11:21:56 -0800 Subject: [PATCH 05/15] Fix bogus SAL annotations on `_Strxfrm()`, `_Wcsxfrm()`, and `_LStrxfrm()`, following GH 5444. --- stl/inc/locale | 5 +++-- stl/inc/xlocinfo | 12 ++++++------ stl/src/xstrxfrm.cpp | 6 +++--- stl/src/xwcsxfrm.cpp | 6 +++--- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/stl/inc/locale b/stl/inc/locale index 3c1de8760d..b5757b253a 100644 --- a/stl/inc/locale +++ b/stl/inc/locale @@ -50,8 +50,9 @@ int _LStrcoll(const _Elem* _First1, const _Elem* _Last1, const _Elem* _First2, c } template -size_t _LStrxfrm( - _Elem* _First1, _Elem* _Last1, const _Elem* _First2, const _Elem* _Last2, const _Locinfo::_Collvec* _Vector) { +size_t _LStrxfrm(_Out_writes_(_Last1 - _First1) _Post_readable_size_(return) _Elem* _First1, _Elem* _Last1, + _In_reads_(_Last2 - _First2) const _Elem* _First2, const _Elem* _Last2, + _In_opt_ const _Locinfo::_Collvec* _Vector) { // perform locale-specific transform of _Elems [_First1, _Last1) if constexpr (is_same_v<_Elem, char>) { return _Strxfrm(_First1, _Last1, _First2, _Last2, _Vector); diff --git a/stl/inc/xlocinfo b/stl/inc/xlocinfo index 054c2524fa..4b198aa3c5 100644 --- a/stl/inc/xlocinfo +++ b/stl/inc/xlocinfo @@ -59,18 +59,18 @@ _MRTIMP2 _Success_(return >= 0) int __cdecl _Mbrtowc(_When_(_Max_multibyte != 0, _CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Strcoll( const char*, const char*, const char*, const char*, const _Collvec*) noexcept; -_CRTIMP2_PURE size_t __CLRCALL_PURE_OR_CDECL _Strxfrm(_Out_writes_(_End1 - _String1) - _Post_readable_size_(return) char* _String1, - _In_z_ char* _End1, const char*, const char*, const _Collvec*) noexcept; +_CRTIMP2_PURE size_t __CLRCALL_PURE_OR_CDECL _Strxfrm( + _Out_writes_(_End1 - _String1) _Post_readable_size_(return) char* _String1, char* _End1, + _In_reads_(_End2 - _String2) const char* _String2, const char* _End2, _In_opt_ const _Collvec*) noexcept; _CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Tolower(int, const _Ctypevec*) noexcept; _CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Toupper(int, const _Ctypevec*) noexcept; _CRTIMP2_PURE _Success_(return != -1) int __CLRCALL_PURE_OR_CDECL _Wcrtomb(_Out_ char*, wchar_t, mbstate_t*, const _Cvtvec*) noexcept; _CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Wcscoll( const wchar_t*, const wchar_t*, const wchar_t*, const wchar_t*, const _Collvec*) noexcept; -_CRTIMP2_PURE size_t __CLRCALL_PURE_OR_CDECL _Wcsxfrm(_Out_writes_(_End1 - _String1) _Post_readable_size_(return) - wchar_t* _String1, - _In_z_ wchar_t* _End1, const wchar_t*, const wchar_t*, const _Collvec*) noexcept; +_CRTIMP2_PURE size_t __CLRCALL_PURE_OR_CDECL _Wcsxfrm( + _Out_writes_(_End1 - _String1) _Post_readable_size_(return) wchar_t* _String1, wchar_t* _End1, + _In_reads_(_End2 - _String2) const wchar_t* _String2, const wchar_t* _End2, _In_opt_ const _Collvec*) noexcept; _CRTIMP2_PURE short __CLRCALL_PURE_OR_CDECL _Getwctype(wchar_t, const _Ctypevec*) noexcept; _CRTIMP2_PURE const wchar_t* __CLRCALL_PURE_OR_CDECL _Getwctypes( diff --git a/stl/src/xstrxfrm.cpp b/stl/src/xstrxfrm.cpp index c094a843a4..69209b5817 100644 --- a/stl/src/xstrxfrm.cpp +++ b/stl/src/xstrxfrm.cpp @@ -51,9 +51,9 @@ _EXTERN_C_UNLESS_PURE // // Exceptions: // Non-standard: if OM/API error, return SIZE_MAX. -_CRTIMP2_PURE size_t __CLRCALL_PURE_OR_CDECL _Strxfrm(_Out_writes_(end1 - string1) - _Post_readable_size_(return) char* string1, - _In_z_ char* end1, const char* string2, const char* end2, const _Collvec* ploc) noexcept { +_CRTIMP2_PURE size_t __CLRCALL_PURE_OR_CDECL _Strxfrm( + _Out_writes_(end1 - string1) _Post_readable_size_(return) char* string1, char* end1, + _In_reads_(end2 - string2) const char* string2, const char* end2, _In_opt_ const _Collvec* ploc) noexcept { size_t n1 = end1 - string1; size_t n2 = end2 - string2; size_t retval = static_cast(-1); // NON-ANSI: default if OM or API error diff --git a/stl/src/xwcsxfrm.cpp b/stl/src/xwcsxfrm.cpp index 631c735d63..4b2e9b5d92 100644 --- a/stl/src/xwcsxfrm.cpp +++ b/stl/src/xwcsxfrm.cpp @@ -44,9 +44,9 @@ _EXTERN_C_UNLESS_PURE // // Exceptions: // Non-standard: if OM/API error, return SIZE_MAX. -_CRTIMP2_PURE size_t __CLRCALL_PURE_OR_CDECL _Wcsxfrm(_Out_writes_(end1 - string1) _Post_readable_size_(return) - wchar_t* string1, - _In_z_ wchar_t* end1, const wchar_t* string2, const wchar_t* end2, const _Collvec* ploc) noexcept { +_CRTIMP2_PURE size_t __CLRCALL_PURE_OR_CDECL _Wcsxfrm( + _Out_writes_(end1 - string1) _Post_readable_size_(return) wchar_t* string1, wchar_t* end1, + _In_reads_(end2 - string2) const wchar_t* string2, const wchar_t* end2, _In_opt_ const _Collvec* ploc) noexcept { size_t n1 = end1 - string1; size_t n2 = end2 - string2; size_t size = static_cast(-1); From 61b033f1ee20293840cfef2be9964172fde376b1 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 04:28:24 -0800 Subject: [PATCH 06/15] Fix comment: `_XStrxfrm` (no such thing) => `_LStrxfrm` --- stl/inc/locale | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/locale b/stl/inc/locale index b5757b253a..a7875514e5 100644 --- a/stl/inc/locale +++ b/stl/inc/locale @@ -172,7 +172,7 @@ protected: } private: - _Locinfo::_Collvec _Coll; // used by _LStrcoll and _XStrxfrm + _Locinfo::_Collvec _Coll; // used by _LStrcoll and _LStrxfrm friend _Regex_traits<_Elem>; }; From 3f445363d289f0fd003424b3779fd7fdaa5a060c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 04:49:16 -0800 Subject: [PATCH 07/15] `_Maklocbyte`, `_Maklocchr`, `_Maklocstr`: Guard `unsigned short` with `#ifdef _CRTBLD`. This follows `_LStrcoll` and `_LStrxfrm`, changed by GH 5361. --- stl/inc/xlocale | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index a85eada164..581771b9f7 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -479,14 +479,14 @@ char _Maklocbyte(_Elem _Char, const _Locinfo::_Cvtvec& _Cvt) { _Wcrtomb(&_Byte, _Char, &_Mbst1, &_Cvt); return _Byte; } -#if defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS +#ifdef _CRTBLD else if constexpr (is_same_v<_Elem, unsigned short>) { char _Byte = '\0'; mbstate_t _Mbst1 = {}; _Wcrtomb(&_Byte, static_cast(_Char), &_Mbst1, &_Cvt); return _Byte; } -#endif // defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS +#endif // defined(_CRTBLD) else { return static_cast(static_cast(_Char)); } @@ -501,14 +501,14 @@ _Elem _Maklocchr(char _Byte, _Elem*, const _Locinfo::_Cvtvec& _Cvt) { _Mbrtowc(&_Wc, &_Byte, 1, &_Mbst1, &_Cvt); return _Wc; } -#if defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS +#ifdef _CRTBLD else if constexpr (is_same_v<_Elem, unsigned short>) { unsigned short _Wc = 0; mbstate_t _Mbst1 = {}; _Mbrtowc(reinterpret_cast(&_Wc), &_Byte, 1, &_Mbst1, &_Cvt); return _Wc; } -#endif // defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS +#endif // defined(_CRTBLD) else { return static_cast<_Elem>(static_cast(_Byte)); } @@ -555,7 +555,7 @@ _Elem* _Maklocstr(const char* _Ptr, _Elem*, const _Locinfo::_Cvtvec& _Cvt) { return _Ptrdest; } -#if defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS +#ifdef _CRTBLD else if constexpr (is_same_v<_Elem, unsigned short>) { size_t _Count; size_t _Count1; @@ -592,7 +592,7 @@ _Elem* _Maklocstr(const char* _Ptr, _Elem*, const _Locinfo::_Cvtvec& _Cvt) { *_Ptrnext = L'\0'; return reinterpret_cast(_Ptrdest); } -#endif // defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS +#endif // defined(_CRTBLD) else { size_t _Count = _CSTD strlen(_Ptr) + 1; From 8ede56923fa5281d1d130a34216a3a6610f88586 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 05:06:22 -0800 Subject: [PATCH 08/15] Pass an explicit template arg to `_Maklocchr` and `_Maklocstr` instead of a typed null pointer. This is fine because it can't be deduced now. These are header-only (and aren't `extern "C"`) and their signatures are changing, so we don't need to rename them. --- stl/inc/xlocale | 8 ++++---- stl/inc/xlocmon | 17 ++++++++--------- stl/inc/xlocnum | 17 ++++++++--------- stl/inc/xloctime | 6 +++--- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 581771b9f7..4df01ea935 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -493,7 +493,7 @@ char _Maklocbyte(_Elem _Char, const _Locinfo::_Cvtvec& _Cvt) { } template -_Elem _Maklocchr(char _Byte, _Elem*, const _Locinfo::_Cvtvec& _Cvt) { +_Elem _Maklocchr(char _Byte, const _Locinfo::_Cvtvec& _Cvt) { // convert char to _Elem using _Cvtvec if constexpr (is_same_v<_Elem, wchar_t>) { wchar_t _Wc = L'\0'; @@ -515,7 +515,7 @@ _Elem _Maklocchr(char _Byte, _Elem*, const _Locinfo::_Cvtvec& _Cvt) { } template -_Elem* _Maklocstr(const char* _Ptr, _Elem*, const _Locinfo::_Cvtvec& _Cvt) { +_Elem* _Maklocstr(const char* _Ptr, const _Locinfo::_Cvtvec& _Cvt) { // convert C string to _Elem sequence using _Cvtvec if constexpr (is_same_v<_Elem, wchar_t>) { size_t _Count; @@ -2626,14 +2626,14 @@ protected: } virtual _Elem __CLR_OR_THIS_CALL do_widen(char _Byte) const { // widen char - return _Maklocchr(_Byte, static_cast<_Elem*>(nullptr), _Cvt); + return _Maklocchr<_Elem>(_Byte, _Cvt); } virtual const char* __CLR_OR_THIS_CALL do_widen( const char* _First, const char* _Last, _Elem* _Dest) const { // widen chars in [_First, _Last) _Adl_verify_range(_First, _Last); for (; _First != _Last; ++_First, ++_Dest) { - *_Dest = _Maklocchr(*_First, static_cast<_Elem*>(nullptr), _Cvt); + *_Dest = _Maklocchr<_Elem>(*_First, _Cvt); } return _First; diff --git a/stl/inc/xlocmon b/stl/inc/xlocmon index 661991f766..1ef6b3019b 100644 --- a/stl/inc/xlocmon +++ b/stl/inc/xlocmon @@ -116,7 +116,7 @@ protected: _Minussign = nullptr; _Tidy_guard<_Mpunct> _Guard{this}; - _Grouping = _Maklocstr(_Ptr->mon_grouping, static_cast(nullptr), _Cvt); + _Grouping = _Maklocstr(_Ptr->mon_grouping, _Cvt); if constexpr (is_same_v<_Elem, wchar_t>) { _Currencysign = _Maklocwcs(_International ? _Ptr->_W_int_curr_symbol : _Ptr->_W_currency_symbol); _Plussign = _Maklocwcs(4 < static_cast(_Ptr->p_sign_posn) ? L"" : _Ptr->_W_positive_sign); @@ -124,14 +124,13 @@ protected: _Decimalpoint = _Ptr->_W_mon_decimal_point[0]; _Kseparator = _Ptr->_W_mon_thousands_sep[0]; } else { - _Currencysign = _Maklocstr( - _International ? _Ptr->int_curr_symbol : _Ptr->currency_symbol, static_cast<_Elem*>(nullptr), _Cvt); - _Plussign = _Maklocstr(4 < static_cast(_Ptr->p_sign_posn) ? "" : _Ptr->positive_sign, - static_cast<_Elem*>(nullptr), _Cvt); - _Minussign = _Maklocstr(4 < static_cast(_Ptr->n_sign_posn) ? "-" : _Ptr->negative_sign, - static_cast<_Elem*>(nullptr), _Cvt); - _Decimalpoint = _Maklocchr(_Ptr->mon_decimal_point[0], static_cast<_Elem*>(nullptr), _Cvt); - _Kseparator = _Maklocchr(_Ptr->mon_thousands_sep[0], static_cast<_Elem*>(nullptr), _Cvt); + _Currencysign = _Maklocstr<_Elem>(_International ? _Ptr->int_curr_symbol : _Ptr->currency_symbol, _Cvt); + _Plussign = + _Maklocstr<_Elem>(4 < static_cast(_Ptr->p_sign_posn) ? "" : _Ptr->positive_sign, _Cvt); + _Minussign = + _Maklocstr<_Elem>(4 < static_cast(_Ptr->n_sign_posn) ? "-" : _Ptr->negative_sign, _Cvt); + _Decimalpoint = _Maklocchr<_Elem>(_Ptr->mon_decimal_point[0], _Cvt); + _Kseparator = _Maklocchr<_Elem>(_Ptr->mon_thousands_sep[0], _Cvt); } _Guard._Target = nullptr; diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index fca7454e0b..97f9589d1d 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -120,8 +120,7 @@ public: _BEGIN_LOCINFO(_Lobj) _Init(_Lobj); if (_Kseparator == 0) { - _Kseparator = // NB: differs from "C" locale - _Maklocchr(',', static_cast<_Elem*>(nullptr), _Lobj._Getcvt()); + _Kseparator = _Maklocchr<_Elem>(',', _Lobj._Getcvt()); // NB: differs from "C" locale } _END_LOCINFO() } @@ -158,21 +157,21 @@ protected: _Truename = nullptr; _Tidy_guard _Guard{this}; - _Grouping = _Maklocstr(_Isdef ? "" : _Ptr->grouping, static_cast(nullptr), _Cvt); - _Falsename = _Maklocstr(_Lobj._Getfalse(), static_cast<_Elem*>(nullptr), _Cvt); - _Truename = _Maklocstr(_Lobj._Gettrue(), static_cast<_Elem*>(nullptr), _Cvt); + _Grouping = _Maklocstr(_Isdef ? "" : _Ptr->grouping, _Cvt); + _Falsename = _Maklocstr<_Elem>(_Lobj._Getfalse(), _Cvt); + _Truename = _Maklocstr<_Elem>(_Lobj._Gettrue(), _Cvt); _Guard._Target = nullptr; if (_Isdef) { // apply defaults for required facets - _Dp = _Maklocchr('.', static_cast<_Elem*>(nullptr), _Cvt); - _Kseparator = _Maklocchr(',', static_cast<_Elem*>(nullptr), _Cvt); + _Dp = _Maklocchr<_Elem>('.', _Cvt); + _Kseparator = _Maklocchr<_Elem>(',', _Cvt); } else { if constexpr (is_same_v<_Elem, wchar_t>) { _Dp = _Ptr->_W_decimal_point[0]; _Kseparator = _Ptr->_W_thousands_sep[0]; } else { - _Dp = _Maklocchr(_Ptr->decimal_point[0], static_cast<_Elem*>(nullptr), _Cvt); - _Kseparator = _Maklocchr(_Ptr->thousands_sep[0], static_cast<_Elem*>(nullptr), _Cvt); + _Dp = _Maklocchr<_Elem>(_Ptr->decimal_point[0], _Cvt); + _Kseparator = _Maklocchr<_Elem>(_Ptr->thousands_sep[0], _Cvt); } } } diff --git a/stl/inc/xloctime b/stl/inc/xloctime index c9f9486014..15036ae8d7 100644 --- a/stl/inc/xloctime +++ b/stl/inc/xloctime @@ -244,9 +244,9 @@ protected: reinterpret_cast(_Maklocwcs(reinterpret_cast(_Lobj._W_Getmonths()))); _Ampm = reinterpret_cast(_Maklocwcs(L":AM:am:PM:pm")); } else { - _Days = _Maklocstr(_Lobj._Getdays(), static_cast<_Elem*>(nullptr), _Cvt); - _Months = _Maklocstr(_Lobj._Getmonths(), static_cast<_Elem*>(nullptr), _Cvt); - _Ampm = _Maklocstr(":AM:am:PM:pm", static_cast<_Elem*>(nullptr), _Cvt); + _Days = _Maklocstr<_Elem>(_Lobj._Getdays(), _Cvt); + _Months = _Maklocstr<_Elem>(_Lobj._Getmonths(), _Cvt); + _Ampm = _Maklocstr<_Elem>(":AM:am:PM:pm", _Cvt); } } From c5da35469f09b5968f6976657d5462c92aea7d90 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 05:55:42 -0800 Subject: [PATCH 09/15] Unify `wchar_t` and `unsigned short`, part 1: Prepare `_Maklocstr` --- stl/inc/xlocale | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 4df01ea935..fd4ae0ba04 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -523,12 +523,14 @@ _Elem* _Maklocstr(const char* _Ptr, const _Locinfo::_Cvtvec& _Cvt) { size_t _Wchars; const char* _Ptr1; int _Bytes; - wchar_t _Wc; + _Elem _Wc; mbstate_t _Mbst1 = {}; _Count1 = _CSTD strlen(_Ptr) + 1; for (_Count = _Count1, _Wchars = 0, _Ptr1 = _Ptr; 0 < _Count; _Count -= _Bytes, _Ptr1 += _Bytes, ++_Wchars) { - if ((_Bytes = _Mbrtowc(&_Wc, _Ptr1, _Count, &_Mbst1, &_Cvt)) <= 0) { + if ((_Bytes = _Mbrtowc(reinterpret_cast(&_Wc), // no-op cast if _Elem is wchar_t + _Ptr1, _Count, &_Mbst1, &_Cvt)) + <= 0) { break; } } @@ -553,7 +555,7 @@ _Elem* _Maklocstr(const char* _Ptr, const _Locinfo::_Cvtvec& _Cvt) { *_Ptrnext = L'\0'; - return _Ptrdest; + return reinterpret_cast<_Elem*>(_Ptrdest); // no-op cast if _Elem is wchar_t } #ifdef _CRTBLD else if constexpr (is_same_v<_Elem, unsigned short>) { @@ -562,12 +564,14 @@ _Elem* _Maklocstr(const char* _Ptr, const _Locinfo::_Cvtvec& _Cvt) { size_t _Wchars; const char* _Ptr1; int _Bytes; - unsigned short _Wc; + _Elem _Wc; mbstate_t _Mbst1 = {}; _Count1 = _CSTD strlen(_Ptr) + 1; for (_Count = _Count1, _Wchars = 0, _Ptr1 = _Ptr; 0 < _Count; _Count -= _Bytes, _Ptr1 += _Bytes, ++_Wchars) { - if ((_Bytes = _Mbrtowc(reinterpret_cast(&_Wc), _Ptr1, _Count, &_Mbst1, &_Cvt)) <= 0) { + if ((_Bytes = _Mbrtowc(reinterpret_cast(&_Wc), // no-op cast if _Elem is wchar_t + _Ptr1, _Count, &_Mbst1, &_Cvt)) + <= 0) { break; } } @@ -583,6 +587,7 @@ _Elem* _Maklocstr(const char* _Ptr, const _Locinfo::_Cvtvec& _Cvt) { wchar_t* _Ptrnext = _Ptrdest; mbstate_t _Mbst2 = {}; + for (; 0 < _Wchars; _Count -= _Bytes, _Ptr += _Bytes, --_Wchars, ++_Ptrnext) { if ((_Bytes = _Mbrtowc(_Ptrnext, _Ptr, _Count1, &_Mbst2, &_Cvt)) <= 0) { break; @@ -590,7 +595,8 @@ _Elem* _Maklocstr(const char* _Ptr, const _Locinfo::_Cvtvec& _Cvt) { } *_Ptrnext = L'\0'; - return reinterpret_cast(_Ptrdest); + + return reinterpret_cast<_Elem*>(_Ptrdest); // no-op cast if _Elem is wchar_t } #endif // defined(_CRTBLD) else { From afe02c4f9d4d7ed4b9895558e7a32575fe21197a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 06:05:04 -0800 Subject: [PATCH 10/15] Unify `wchar_t` and `unsigned short`, part 2: Prepare `_Maklocbyte`, `_Maklocchr` --- stl/inc/xlocale | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index fd4ae0ba04..8f4afcfaf5 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -476,14 +476,14 @@ char _Maklocbyte(_Elem _Char, const _Locinfo::_Cvtvec& _Cvt) { if constexpr (is_same_v<_Elem, wchar_t>) { char _Byte = '\0'; mbstate_t _Mbst1 = {}; - _Wcrtomb(&_Byte, _Char, &_Mbst1, &_Cvt); + _Wcrtomb(&_Byte, static_cast(_Char), &_Mbst1, &_Cvt); // no-op cast if _Elem is wchar_t return _Byte; } #ifdef _CRTBLD else if constexpr (is_same_v<_Elem, unsigned short>) { char _Byte = '\0'; mbstate_t _Mbst1 = {}; - _Wcrtomb(&_Byte, static_cast(_Char), &_Mbst1, &_Cvt); + _Wcrtomb(&_Byte, static_cast(_Char), &_Mbst1, &_Cvt); // no-op cast if _Elem is wchar_t return _Byte; } #endif // defined(_CRTBLD) @@ -496,16 +496,16 @@ template _Elem _Maklocchr(char _Byte, const _Locinfo::_Cvtvec& _Cvt) { // convert char to _Elem using _Cvtvec if constexpr (is_same_v<_Elem, wchar_t>) { - wchar_t _Wc = L'\0'; + _Elem _Wc{}; mbstate_t _Mbst1 = {}; - _Mbrtowc(&_Wc, &_Byte, 1, &_Mbst1, &_Cvt); + _Mbrtowc(reinterpret_cast(&_Wc), &_Byte, 1, &_Mbst1, &_Cvt); // no-op cast if _Elem is wchar_t return _Wc; } #ifdef _CRTBLD else if constexpr (is_same_v<_Elem, unsigned short>) { - unsigned short _Wc = 0; - mbstate_t _Mbst1 = {}; - _Mbrtowc(reinterpret_cast(&_Wc), &_Byte, 1, &_Mbst1, &_Cvt); + _Elem _Wc{}; + mbstate_t _Mbst1 = {}; + _Mbrtowc(reinterpret_cast(&_Wc), &_Byte, 1, &_Mbst1, &_Cvt); // no-op cast if _Elem is wchar_t return _Wc; } #endif // defined(_CRTBLD) From 2185e639ee6bb1eb05b635b1ad332efecaa944ae Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 05:58:47 -0800 Subject: [PATCH 11/15] Unify `wchar_t` and `unsigned short`, part 3: Extract `_Is_wchar_or_crtbld_ushort`. `remove_cv_t<_Elem>` is currently unnecessary, but it makes me feel better to have type traits that behave like the primary type categories. --- stl/inc/locale | 20 +++--------- stl/inc/xlocale | 81 +++++++++---------------------------------------- 2 files changed, 18 insertions(+), 83 deletions(-) diff --git a/stl/inc/locale b/stl/inc/locale index a7875514e5..64d71c3aab 100644 --- a/stl/inc/locale +++ b/stl/inc/locale @@ -27,16 +27,10 @@ int _LStrcoll(const _Elem* _First1, const _Elem* _Last1, const _Elem* _First2, c const _Locinfo::_Collvec* _Vector) { // perform locale-specific comparison of _Elem sequences if constexpr (is_same_v<_Elem, char>) { return _Strcoll(_First1, _Last1, _First2, _Last2, _Vector); - } else if constexpr (is_same_v<_Elem, wchar_t>) { - return _Wcscoll(_First1, _Last1, _First2, _Last2, _Vector); - } -#ifdef _CRTBLD - else if constexpr (is_same_v<_Elem, unsigned short>) { + } else if constexpr (_Is_wchar_or_crtbld_ushort<_Elem>) { // no-op casts if _Elem is wchar_t return _Wcscoll(reinterpret_cast(_First1), reinterpret_cast(_Last1), reinterpret_cast(_First2), reinterpret_cast(_Last2), _Vector); - } -#endif // defined(_CRTBLD) - else { + } else { for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, ++_First2) { if (*_First1 < *_First2) { return -1; // [_First1, _Last1) < [_First2, _Last2) @@ -56,16 +50,10 @@ size_t _LStrxfrm(_Out_writes_(_Last1 - _First1) _Post_readable_size_(return) _El // perform locale-specific transform of _Elems [_First1, _Last1) if constexpr (is_same_v<_Elem, char>) { return _Strxfrm(_First1, _Last1, _First2, _Last2, _Vector); - } else if constexpr (is_same_v<_Elem, wchar_t>) { - return _Wcsxfrm(_First1, _Last1, _First2, _Last2, _Vector); - } -#ifdef _CRTBLD - else if constexpr (is_same_v<_Elem, unsigned short>) { + } else if constexpr (_Is_wchar_or_crtbld_ushort<_Elem>) { // no-op casts if _Elem is wchar_t return _Wcsxfrm(reinterpret_cast(_First1), reinterpret_cast(_Last1), reinterpret_cast(_First2), reinterpret_cast(_Last2), _Vector); - } -#endif // defined(_CRTBLD) - else { + } else { const ptrdiff_t _Count = _Last2 - _First2; if (_Count <= _Last1 - _First1) { _CSTD memcpy(_First1, _First2, _Count * sizeof(_Elem)); diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 8f4afcfaf5..ead3a87399 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -470,24 +470,23 @@ const _Facet& __CRTDECL use_facet(const locale& _Loc) { // get facet reference f _END_LOCK() } // end of use_facet body +#ifdef _CRTBLD +template +constexpr bool _Is_wchar_or_crtbld_ushort = _Is_any_of_v, wchar_t, unsigned short>; +#else // ^^^ defined(_CRTBLD) / !defined(_CRTBLD) vvv +template +constexpr bool _Is_wchar_or_crtbld_ushort = is_same_v, wchar_t>; +#endif // ^^^ !defined(_CRTBLD) ^^^ + template char _Maklocbyte(_Elem _Char, const _Locinfo::_Cvtvec& _Cvt) { // convert _Elem to char using _Cvtvec - if constexpr (is_same_v<_Elem, wchar_t>) { - char _Byte = '\0'; - mbstate_t _Mbst1 = {}; - _Wcrtomb(&_Byte, static_cast(_Char), &_Mbst1, &_Cvt); // no-op cast if _Elem is wchar_t - return _Byte; - } -#ifdef _CRTBLD - else if constexpr (is_same_v<_Elem, unsigned short>) { + if constexpr (_Is_wchar_or_crtbld_ushort<_Elem>) { char _Byte = '\0'; mbstate_t _Mbst1 = {}; _Wcrtomb(&_Byte, static_cast(_Char), &_Mbst1, &_Cvt); // no-op cast if _Elem is wchar_t return _Byte; - } -#endif // defined(_CRTBLD) - else { + } else { return static_cast(static_cast(_Char)); } } @@ -495,21 +494,12 @@ char _Maklocbyte(_Elem _Char, const _Locinfo::_Cvtvec& _Cvt) { template _Elem _Maklocchr(char _Byte, const _Locinfo::_Cvtvec& _Cvt) { // convert char to _Elem using _Cvtvec - if constexpr (is_same_v<_Elem, wchar_t>) { - _Elem _Wc{}; - mbstate_t _Mbst1 = {}; - _Mbrtowc(reinterpret_cast(&_Wc), &_Byte, 1, &_Mbst1, &_Cvt); // no-op cast if _Elem is wchar_t - return _Wc; - } -#ifdef _CRTBLD - else if constexpr (is_same_v<_Elem, unsigned short>) { + if constexpr (_Is_wchar_or_crtbld_ushort<_Elem>) { _Elem _Wc{}; mbstate_t _Mbst1 = {}; _Mbrtowc(reinterpret_cast(&_Wc), &_Byte, 1, &_Mbst1, &_Cvt); // no-op cast if _Elem is wchar_t return _Wc; - } -#endif // defined(_CRTBLD) - else { + } else { return static_cast<_Elem>(static_cast(_Byte)); } } @@ -517,48 +507,7 @@ _Elem _Maklocchr(char _Byte, const _Locinfo::_Cvtvec& _Cvt) { template _Elem* _Maklocstr(const char* _Ptr, const _Locinfo::_Cvtvec& _Cvt) { // convert C string to _Elem sequence using _Cvtvec - if constexpr (is_same_v<_Elem, wchar_t>) { - size_t _Count; - size_t _Count1; - size_t _Wchars; - const char* _Ptr1; - int _Bytes; - _Elem _Wc; - mbstate_t _Mbst1 = {}; - - _Count1 = _CSTD strlen(_Ptr) + 1; - for (_Count = _Count1, _Wchars = 0, _Ptr1 = _Ptr; 0 < _Count; _Count -= _Bytes, _Ptr1 += _Bytes, ++_Wchars) { - if ((_Bytes = _Mbrtowc(reinterpret_cast(&_Wc), // no-op cast if _Elem is wchar_t - _Ptr1, _Count, &_Mbst1, &_Cvt)) - <= 0) { - break; - } - } - - ++_Wchars; // count terminating nul - - wchar_t* _Ptrdest = - static_cast(_calloc_dbg(_Wchars, sizeof(wchar_t), _CRT_BLOCK, __FILE__, __LINE__)); - - if (!_Ptrdest) { - _Xbad_alloc(); - } - - wchar_t* _Ptrnext = _Ptrdest; - mbstate_t _Mbst2 = {}; - - for (; 0 < _Wchars; _Count -= _Bytes, _Ptr += _Bytes, --_Wchars, ++_Ptrnext) { - if ((_Bytes = _Mbrtowc(_Ptrnext, _Ptr, _Count1, &_Mbst2, &_Cvt)) <= 0) { - break; - } - } - - *_Ptrnext = L'\0'; - - return reinterpret_cast<_Elem*>(_Ptrdest); // no-op cast if _Elem is wchar_t - } -#ifdef _CRTBLD - else if constexpr (is_same_v<_Elem, unsigned short>) { + if constexpr (_Is_wchar_or_crtbld_ushort<_Elem>) { size_t _Count; size_t _Count1; size_t _Wchars; @@ -597,9 +546,7 @@ _Elem* _Maklocstr(const char* _Ptr, const _Locinfo::_Cvtvec& _Cvt) { *_Ptrnext = L'\0'; return reinterpret_cast<_Elem*>(_Ptrdest); // no-op cast if _Elem is wchar_t - } -#endif // defined(_CRTBLD) - else { + } else { size_t _Count = _CSTD strlen(_Ptr) + 1; _Elem* _Ptrdest = static_cast<_Elem*>(_calloc_dbg(_Count, sizeof(_Elem), _CRT_BLOCK, __FILE__, __LINE__)); From aee2bcadb80d65eca7299e2e279670f339f7da86 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 17:09:52 -0800 Subject: [PATCH 12/15] `_Maklocstr`: Remove dead `_Count -= _Bytes`. `_Count` is unused at this point (and there's no outer loop). --- stl/inc/xlocale | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index ead3a87399..6e3f3fcfb9 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -537,7 +537,7 @@ _Elem* _Maklocstr(const char* _Ptr, const _Locinfo::_Cvtvec& _Cvt) { wchar_t* _Ptrnext = _Ptrdest; mbstate_t _Mbst2 = {}; - for (; 0 < _Wchars; _Count -= _Bytes, _Ptr += _Bytes, --_Wchars, ++_Ptrnext) { + for (; 0 < _Wchars; _Ptr += _Bytes, --_Wchars, ++_Ptrnext) { if ((_Bytes = _Mbrtowc(_Ptrnext, _Ptr, _Count1, &_Mbst2, &_Cvt)) <= 0) { break; } From defe88fe714f48f2a8d7bc3f4b3f49019a990d44 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 17:11:02 -0800 Subject: [PATCH 13/15] `_Maklocstr`: Improve variable scoping, part 1. `_Count` can be scoped to the for-loop. `_Count1` can be const. `_Wchars` and `_Ptr1` can be initialized when defined, instead of assigned at the beginning of the for-loop. --- stl/inc/xlocale | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 6e3f3fcfb9..33ab2d501d 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -508,16 +508,14 @@ template _Elem* _Maklocstr(const char* _Ptr, const _Locinfo::_Cvtvec& _Cvt) { // convert C string to _Elem sequence using _Cvtvec if constexpr (_Is_wchar_or_crtbld_ushort<_Elem>) { - size_t _Count; - size_t _Count1; - size_t _Wchars; - const char* _Ptr1; + size_t _Wchars = 0; + const char* _Ptr1 = _Ptr; int _Bytes; _Elem _Wc; mbstate_t _Mbst1 = {}; - _Count1 = _CSTD strlen(_Ptr) + 1; - for (_Count = _Count1, _Wchars = 0, _Ptr1 = _Ptr; 0 < _Count; _Count -= _Bytes, _Ptr1 += _Bytes, ++_Wchars) { + const size_t _Count1 = _CSTD strlen(_Ptr) + 1; + for (size_t _Count = _Count1; 0 < _Count; _Count -= _Bytes, _Ptr1 += _Bytes, ++_Wchars) { if ((_Bytes = _Mbrtowc(reinterpret_cast(&_Wc), // no-op cast if _Elem is wchar_t _Ptr1, _Count, &_Mbst1, &_Cvt)) <= 0) { From 713148a98cd29ef6fc9ebb47e40d9e10bfa94f83 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 17:11:51 -0800 Subject: [PATCH 14/15] `_Maklocstr`: Improve variable scoping, part 2. `_Bytes` doesn't communicate information across loops or loop iterations. It's always assigned to in the loop body, then read from at the end of the iteration. Making it a const local in each loop iteration is significantly clearer. --- stl/inc/xlocale | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 33ab2d501d..1a9644518e 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -510,17 +510,18 @@ _Elem* _Maklocstr(const char* _Ptr, const _Locinfo::_Cvtvec& _Cvt) { if constexpr (_Is_wchar_or_crtbld_ushort<_Elem>) { size_t _Wchars = 0; const char* _Ptr1 = _Ptr; - int _Bytes; _Elem _Wc; mbstate_t _Mbst1 = {}; const size_t _Count1 = _CSTD strlen(_Ptr) + 1; - for (size_t _Count = _Count1; 0 < _Count; _Count -= _Bytes, _Ptr1 += _Bytes, ++_Wchars) { - if ((_Bytes = _Mbrtowc(reinterpret_cast(&_Wc), // no-op cast if _Elem is wchar_t - _Ptr1, _Count, &_Mbst1, &_Cvt)) - <= 0) { + for (size_t _Count = _Count1; 0 < _Count; ++_Wchars) { + const int _Bytes = _Mbrtowc(reinterpret_cast(&_Wc), // no-op cast if _Elem is wchar_t + _Ptr1, _Count, &_Mbst1, &_Cvt); + if (_Bytes <= 0) { break; } + _Count -= _Bytes; + _Ptr1 += _Bytes; } ++_Wchars; // count terminating nul @@ -535,10 +536,12 @@ _Elem* _Maklocstr(const char* _Ptr, const _Locinfo::_Cvtvec& _Cvt) { wchar_t* _Ptrnext = _Ptrdest; mbstate_t _Mbst2 = {}; - for (; 0 < _Wchars; _Ptr += _Bytes, --_Wchars, ++_Ptrnext) { - if ((_Bytes = _Mbrtowc(_Ptrnext, _Ptr, _Count1, &_Mbst2, &_Cvt)) <= 0) { + for (; 0 < _Wchars; --_Wchars, ++_Ptrnext) { + const int _Bytes = _Mbrtowc(_Ptrnext, _Ptr, _Count1, &_Mbst2, &_Cvt); + if (_Bytes <= 0) { break; } + _Ptr += _Bytes; } *_Ptrnext = L'\0'; From 2877e05d31075b111bec848c4447208f3584d398 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 29 Nov 2025 09:46:25 -0800 Subject: [PATCH 15/15] Guard `locale::_Locimp::_Makeushloc()` with `#ifdef _CRTBLD`. Users with real wchar_t should never see this. --- stl/inc/xlocale | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 1a9644518e..260c1c55b5 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -193,10 +193,10 @@ public: static void __CLRCALL_OR_CDECL _Makewloc( const _Locinfo&, category, _Locimp*, const locale*); // make wchar_t facets -#if defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS +#ifdef _CRTBLD static void __CLRCALL_OR_CDECL _Makeushloc( const _Locinfo&, category, _Locimp*, const locale*); // make ushort facets -#endif // defined(_NATIVE_WCHAR_T_DEFINED) && !_ENFORCE_FACET_SPECIALIZATIONS +#endif // defined(_CRTBLD) static void __CLRCALL_OR_CDECL _Makexloc( const _Locinfo&, category, _Locimp*, const locale*); // make remaining facets