diff --git a/stl/inc/xcharconv_ryu.h b/stl/inc/xcharconv_ryu.h index 1b2b95a8e05..e76714cbcd7 100644 --- a/stl/inc/xcharconv_ryu.h +++ b/stl/inc/xcharconv_ryu.h @@ -39,8 +39,11 @@ #if _STL_COMPILER_PREPROCESSOR #include +#include +#include #include #include +#include #ifdef _M_X64 #include // for _umul128() and __shiftright128() @@ -389,7 +392,10 @@ _NODISCARD inline uint32_t __mulShift_mod1e9(const uint64_t __m, const uint64_t* #endif // ^^^ intrinsics unavailable ^^^ } -inline void __append_n_digits(const uint32_t __olength, uint32_t __digits, char* const __result) { +#define _WIDEN(_TYPE, _CHAR) static_cast<_TYPE>(is_same_v<_TYPE, char> ? _CHAR : L##_CHAR) + +template +void __append_n_digits(const uint32_t __olength, uint32_t __digits, _CharT* const __result) { uint32_t __i = 0; while (__digits >= 10000) { #ifdef __clang__ // TRANSITION, LLVM-38217 @@ -400,21 +406,21 @@ inline void __append_n_digits(const uint32_t __olength, uint32_t __digits, char* __digits /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(__result + __olength - __i - 2, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(__result + __olength - __i - 4, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(__result + __olength - __i - 2, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + __olength - __i - 4, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); __i += 4; } if (__digits >= 100) { const uint32_t __c = (__digits % 100) << 1; __digits /= 100; - _CSTD memcpy(__result + __olength - __i - 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(__result + __olength - __i - 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); __i += 2; } if (__digits >= 10) { const uint32_t __c = __digits << 1; - _CSTD memcpy(__result + __olength - __i - 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(__result + __olength - __i - 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); } else { - __result[0] = static_cast('0' + __digits); + __result[0] = static_cast<_CharT>(_WIDEN(_CharT, '0') + __digits); } } @@ -429,43 +435,45 @@ inline void __append_d_digits(const uint32_t __olength, uint32_t __digits, char* __digits /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(__result + __olength + 1 - __i - 2, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(__result + __olength + 1 - __i - 4, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(__result + __olength + 1 - __i - 2, __DIGIT_TABLE + __c0, 2); + _CSTD memcpy(__result + __olength + 1 - __i - 4, __DIGIT_TABLE + __c1, 2); __i += 4; } if (__digits >= 100) { const uint32_t __c = (__digits % 100) << 1; __digits /= 100; - _CSTD memcpy(__result + __olength + 1 - __i - 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(__result + __olength + 1 - __i - 2, __DIGIT_TABLE + __c, 2); __i += 2; } if (__digits >= 10) { const uint32_t __c = __digits << 1; - __result[2] = __DIGIT_TABLE[__c + 1]; + __result[2] = __DIGIT_TABLE[__c + 1]; __result[1] = '.'; - __result[0] = __DIGIT_TABLE[__c]; + __result[0] = __DIGIT_TABLE[__c]; } else { __result[1] = '.'; __result[0] = static_cast('0' + __digits); } } -inline void __append_c_digits(const uint32_t __count, uint32_t __digits, char* const __result) { +template +void __append_c_digits(const uint32_t __count, uint32_t __digits, _CharT* const __result) { uint32_t __i = 0; for (; __i < __count - 1; __i += 2) { const uint32_t __c = (__digits % 100) << 1; __digits /= 100; - _CSTD memcpy(__result + __count - __i - 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(__result + __count - __i - 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); } if (__i < __count) { - const char __c = static_cast('0' + (__digits % 10)); + const _CharT __c = static_cast<_CharT>(_WIDEN(_CharT, '0') + (__digits % 10)); __result[__count - __i - 1] = __c; } } -inline void __append_nine_digits(uint32_t __digits, char* const __result) { +template +void __append_nine_digits(uint32_t __digits, _CharT* const __result) { if (__digits == 0) { - _CSTD memset(__result, '0', 9); + _STD fill_n(__result, 9, _WIDEN(_CharT, '0')); return; } @@ -478,10 +486,10 @@ inline void __append_nine_digits(uint32_t __digits, char* const __result) { __digits /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(__result + 7 - __i, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(__result + 5 - __i, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(__result + 7 - __i, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + 5 - __i, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); } - __result[0] = static_cast('0' + __digits); + __result[0] = static_cast<_CharT>(_WIDEN(_CharT, '0') + __digits); } _NODISCARD inline uint32_t __indexForExponent(const uint32_t __e) { @@ -497,9 +505,10 @@ _NODISCARD inline uint32_t __lengthForIndex(const uint32_t __idx) { return (__log10Pow2(16 * static_cast(__idx)) + 1 + 16 + 8) / 9; } -_NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const _Last, const double __d, +template +_NODISCARD pair<_CharT*, errc> __d2fixed_buffered_n(_CharT* _First, _CharT* const _Last, const double __d, const uint32_t __precision) { - char* const _Original_first = _First; + _CharT* const _Original_first = _First; const uint64_t __bits = __double_to_bits(__d); @@ -513,10 +522,10 @@ _NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const return { _Last, errc::value_too_large }; } - *_First++ = '0'; + *_First++ = _WIDEN(_CharT, '0'); if (__precision > 0) { - *_First++ = '.'; - _CSTD memset(_First, '0', __precision); + *_First++ = _WIDEN(_CharT, '.'); + _STD fill_n(_First, __precision, _WIDEN(_CharT, '0')); _First += __precision; } return { _First, errc{} }; @@ -568,13 +577,13 @@ _NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const if (_First == _Last) { return { _Last, errc::value_too_large }; } - *_First++ = '0'; + *_First++ = _WIDEN(_CharT, '0'); } if (__precision > 0) { if (_First == _Last) { return { _Last, errc::value_too_large }; } - *_First++ = '.'; + *_First++ = _WIDEN(_CharT, '.'); } if (__e2 < 0) { const int32_t __idx = -__e2 / 16; @@ -587,14 +596,14 @@ _NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const if (_Last - _First < static_cast(__precision)) { return { _Last, errc::value_too_large }; } - _CSTD memset(_First, '0', __precision); + _STD fill_n(_First, __precision, _WIDEN(_CharT, '0')); _First += __precision; } else if (__i < __MIN_BLOCK_2[__idx]) { __i = __MIN_BLOCK_2[__idx]; if (_Last - _First < static_cast(9 * __i)) { return { _Last, errc::value_too_large }; } - _CSTD memset(_First, '0', 9 * __i); + _STD fill_n(_First, 9 * __i, _WIDEN(_CharT, '0')); _First += 9 * __i; } for (; __i < __blocks; ++__i) { @@ -607,7 +616,7 @@ _NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const if (_Last - _First < static_cast(__fill)) { return { _Last, errc::value_too_large }; } - _CSTD memset(_First, '0', __fill); + _STD fill_n(_First, __fill, _WIDEN(_CharT, '0')); _First += __fill; break; } @@ -647,31 +656,31 @@ _NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const } } if (__roundUp != 0) { - char* _Round = _First; - char* _Dot = _Last; + _CharT* _Round = _First; + _CharT* _Dot = _Last; while (true) { if (_Round == _Original_first) { - _Round[0] = '1'; + _Round[0] = _WIDEN(_CharT, '1'); if (_Dot != _Last) { - _Dot[0] = '0'; - _Dot[1] = '.'; + _Dot[0] = _WIDEN(_CharT, '0'); + _Dot[1] = _WIDEN(_CharT, '.'); } if (_First == _Last) { return { _Last, errc::value_too_large }; } - *_First++ = '0'; + *_First++ = _WIDEN(_CharT, '0'); break; } --_Round; - const char __c = _Round[0]; + const _CharT __c = _Round[0]; if (__c == '.') { _Dot = _Round; - } else if (__c == '9') { - _Round[0] = '0'; + } else if (__c == _WIDEN(_CharT, '9')) { + _Round[0] = _WIDEN(_CharT, '0'); __roundUp = 1; } else { if (__roundUp == 1 || __c % 2 != 0) { - _Round[0] = __c + 1; + _Round[0] = static_cast<_CharT>(__c + 1); } break; } @@ -681,7 +690,7 @@ _NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const if (_Last - _First < static_cast(__precision)) { return { _Last, errc::value_too_large }; } - _CSTD memset(_First, '0', __precision); + _STD fill_n(_First, __precision, _WIDEN(_CharT, '0')); _First += __precision; } return { _First, errc{} }; @@ -919,11 +928,11 @@ _NODISCARD inline to_chars_result __d2exp_buffered_n(char* _First, char* const _ if (__exp >= 100) { const int32_t __c = __exp % 10; - _CSTD memcpy(_First, __DIGIT_TABLE + 2 * (__exp / 10), 2); + _CSTD memcpy(_First, __DIGIT_TABLE + 2 * (__exp / 10), 2); _First[2] = static_cast('0' + __c); _First += 3; } else { - _CSTD memcpy(_First, __DIGIT_TABLE + 2 * __exp, 2); + _CSTD memcpy(_First, __DIGIT_TABLE + 2 * __exp, 2); _First += 2; } @@ -1175,7 +1184,8 @@ _NODISCARD inline __floating_decimal_32 __f2d(const uint32_t __ieeeMantissa, con return __fd; } -_NODISCARD inline to_chars_result _Large_integer_to_chars(char* const _First, char* const _Last, +template +_NODISCARD pair<_CharT*, errc> _Large_integer_to_chars(_CharT* const _First, _CharT* const _Last, const uint32_t _Mantissa2, const int32_t _Exponent2) { // Print the integer _Mantissa2 * 2^_Exponent2 exactly. @@ -1283,7 +1293,7 @@ _NODISCARD inline to_chars_result _Large_integer_to_chars(char* const _First, ch return { _Last, errc::value_too_large }; } - char* _Result = _First; + _CharT* _Result = _First; // Print _Data[0]. While it's up to 10 digits, // which is more than Ryu generates, the code below can handle this. @@ -1299,7 +1309,8 @@ _NODISCARD inline to_chars_result _Large_integer_to_chars(char* const _First, ch return { _Result, errc{} }; } -_NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _Last, const __floating_decimal_32 __v, +template +_NODISCARD pair<_CharT*, errc> __to_chars(_CharT* const _First, _CharT* const _Last, const __floating_decimal_32 __v, chars_format _Fmt, const uint32_t __ieeeMantissa, const uint32_t __ieeeExponent) { // Step 5: Print the decimal representation. uint32_t __output = __v.__mantissa; @@ -1385,7 +1396,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La return { _Last, errc::value_too_large }; } - char* _Mid; + _CharT* _Mid; if (_Ryu_exponent > 0) { // case "172900" bool _Can_use_ryu; @@ -1448,35 +1459,35 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La __output /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); } if (__output >= 100) { const uint32_t __c = (__output % 100) << 1; __output /= 100; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); } if (__output >= 10) { const uint32_t __c = __output << 1; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); } else { - *--_Mid = static_cast('0' + __output); + *--_Mid = static_cast<_CharT>(_WIDEN(_CharT, '0') + __output); } if (_Ryu_exponent > 0) { // case "172900" with _Can_use_ryu // Performance note: it might be more efficient to do this immediately after setting _Mid. - _CSTD memset(_First + __olength, '0', static_cast(_Ryu_exponent)); + _STD fill_n(_First + __olength, _Ryu_exponent, _WIDEN(_CharT, '0')); } else if (_Ryu_exponent == 0) { // case "1729" // Done! } else if (_Whole_digits > 0) { // case "17.29" // Performance note: moving digits might not be optimal. - _CSTD memmove(_First, _First + 1, static_cast(_Whole_digits)); + _CSTD memmove(_First, _First + 1, static_cast(_Whole_digits) * sizeof(_CharT)); _First[_Whole_digits] = '.'; } else { // case "0.001729" // Performance note: a larger memset() followed by overwriting '.' might be more efficient. - _First[0] = '0'; - _First[1] = '.'; - _CSTD memset(_First + 2, '0', static_cast(-_Whole_digits)); + _First[0] = _WIDEN(_CharT, '0'); + _First[1] = _WIDEN(_CharT, '.'); + _STD fill_n(_First + 2, -_Whole_digits, _WIDEN(_CharT, '0')); } return { _First + _Total_fixed_length, errc{} }; @@ -1487,7 +1498,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La if (_Last - _First < static_cast(_Total_scientific_length)) { return { _Last, errc::value_too_large }; } - char* const __result = _First; + _CharT* const __result = _First; // Print the decimal digits. uint32_t __i = 0; @@ -1500,50 +1511,55 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La __output /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); __i += 4; } if (__output >= 100) { const uint32_t __c = (__output % 100) << 1; __output /= 100; - _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); __i += 2; } if (__output >= 10) { const uint32_t __c = __output << 1; // We can't use memcpy here: the decimal dot goes between these two digits. - __result[2] = __DIGIT_TABLE[__c + 1]; - __result[0] = __DIGIT_TABLE[__c]; + __result[2] = __DIGIT_TABLE<_CharT>[__c + 1]; + __result[0] = __DIGIT_TABLE<_CharT>[__c]; } else { - __result[0] = static_cast('0' + __output); + __result[0] = static_cast<_CharT>(_WIDEN(_CharT, '0') + __output); } // Print decimal point if needed. uint32_t __index; if (__olength > 1) { - __result[1] = '.'; + __result[1] = _WIDEN(_CharT, '.'); __index = __olength + 1; } else { __index = 1; } // Print the exponent. - __result[__index++] = 'e'; + __result[__index++] = _WIDEN(_CharT, 'e'); if (_Scientific_exponent < 0) { - __result[__index++] = '-'; + __result[__index++] = _WIDEN(_CharT, '-'); _Scientific_exponent = -_Scientific_exponent; } else { - __result[__index++] = '+'; + __result[__index++] = _WIDEN(_CharT, '+'); } - _CSTD memcpy(__result + __index, __DIGIT_TABLE + 2 * _Scientific_exponent, 2); + _CSTD memcpy(__result + __index, __DIGIT_TABLE<_CharT> + 2 * _Scientific_exponent, 2 * sizeof(_CharT)); __index += 2; return { _First + _Total_scientific_length, errc{} }; } -_NODISCARD inline to_chars_result __f2s_buffered_n(char* const _First, char* const _Last, const float __f, +_NODISCARD inline to_chars_result _Convert_to_chars_result(const pair& _Pair) { + return {_Pair.first, _Pair.second}; +} + +template +_NODISCARD pair<_CharT*, errc> __f2s_buffered_n(_CharT* const _First, _CharT* const _Last, const float __f, const chars_format _Fmt) { // Step 1: Decode the floating-point number, and unify normalized and subnormal cases. @@ -1556,7 +1572,11 @@ _NODISCARD inline to_chars_result __f2s_buffered_n(char* const _First, char* con return { _Last, errc::value_too_large }; } - _CSTD memcpy(_First, "0e+00", 5); + if constexpr (is_same_v<_CharT, char>) { + _CSTD memcpy(_First, "0e+00", 5); + } else { + _CSTD memcpy(_First, L"0e+00", 5 * sizeof(wchar_t)); + } return { _First + 5, errc{} }; } @@ -1566,7 +1586,7 @@ _NODISCARD inline to_chars_result __f2s_buffered_n(char* const _First, char* con return { _Last, errc::value_too_large }; } - *_First = '0'; + *_First = _WIDEN(_CharT, '0'); return { _First + 1, errc{} }; } @@ -1897,7 +1917,8 @@ _NODISCARD inline __floating_decimal_64 __d2d(const uint64_t __ieeeMantissa, con return __fd; } -_NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _Last, const __floating_decimal_64 __v, +template +_NODISCARD pair<_CharT*, errc> __to_chars(_CharT* const _First, _CharT* const _Last, const __floating_decimal_64 __v, chars_format _Fmt, const double __f) { // Step 5: Print the decimal representation. uint64_t __output = __v.__mantissa; @@ -1989,7 +2010,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La return { _Last, errc::value_too_large }; } - char* _Mid; + _CharT* _Mid; if (_Ryu_exponent > 0) { // case "172900" bool _Can_use_ryu; @@ -2072,10 +2093,10 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La const uint32_t __d0 = (__d % 100) << 1; const uint32_t __d1 = (__d / 100) << 1; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c1, 2); - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __d0, 2); - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __d1, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __d0, 2 * sizeof(_CharT)); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __d1, 2 * sizeof(_CharT)); } uint32_t __output2 = static_cast(__output); while (__output2 >= 10000) { @@ -2087,35 +2108,35 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La __output2 /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); } if (__output2 >= 100) { const uint32_t __c = (__output2 % 100) << 1; __output2 /= 100; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); } if (__output2 >= 10) { const uint32_t __c = __output2 << 1; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); } else { - *--_Mid = static_cast('0' + __output2); + *--_Mid = static_cast<_CharT>(_WIDEN(_CharT, '0') + __output2); } if (_Ryu_exponent > 0) { // case "172900" with _Can_use_ryu // Performance note: it might be more efficient to do this immediately after setting _Mid. - _CSTD memset(_First + __olength, '0', static_cast(_Ryu_exponent)); + _STD fill_n(_First + __olength, _Ryu_exponent, _WIDEN(_CharT, '0')); } else if (_Ryu_exponent == 0) { // case "1729" // Done! } else if (_Whole_digits > 0) { // case "17.29" // Performance note: moving digits might not be optimal. - _CSTD memmove(_First, _First + 1, static_cast(_Whole_digits)); - _First[_Whole_digits] = '.'; + _CSTD memmove(_First, _First + 1, static_cast(_Whole_digits) * sizeof(_CharT)); + _First[_Whole_digits] = _WIDEN(_CharT, '.'); } else { // case "0.001729" // Performance note: a larger memset() followed by overwriting '.' might be more efficient. - _First[0] = '0'; - _First[1] = '.'; - _CSTD memset(_First + 2, '0', static_cast(-_Whole_digits)); + _First[0] = _WIDEN(_CharT, '0'); + _First[1] = _WIDEN(_CharT, '.'); + _STD fill_n(_First + 2, -_Whole_digits, _WIDEN(_CharT, '0')); } return { _First + _Total_fixed_length, errc{} }; @@ -2126,7 +2147,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La if (_Last - _First < static_cast(_Total_scientific_length)) { return { _Last, errc::value_too_large }; } - char* const __result = _First; + _CharT* const __result = _First; // Print the decimal digits. uint32_t __i = 0; @@ -2147,10 +2168,10 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La const uint32_t __c1 = (__c / 100) << 1; const uint32_t __d0 = (__d % 100) << 1; const uint32_t __d1 = (__d / 100) << 1; - _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE + __c1, 2); - _CSTD memcpy(__result + __olength - __i - 5, __DIGIT_TABLE + __d0, 2); - _CSTD memcpy(__result + __olength - __i - 7, __DIGIT_TABLE + __d1, 2); + _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + __olength - __i - 5, __DIGIT_TABLE<_CharT> + __d0, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + __olength - __i - 7, __DIGIT_TABLE<_CharT> + __d1, 2 * sizeof(_CharT)); __i += 8; } uint32_t __output2 = static_cast(__output); @@ -2163,50 +2184,50 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La __output2 /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); __i += 4; } if (__output2 >= 100) { const uint32_t __c = (__output2 % 100) << 1; __output2 /= 100; - _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); __i += 2; } if (__output2 >= 10) { const uint32_t __c = __output2 << 1; // We can't use memcpy here: the decimal dot goes between these two digits. - __result[2] = __DIGIT_TABLE[__c + 1]; - __result[0] = __DIGIT_TABLE[__c]; + __result[2] = __DIGIT_TABLE<_CharT>[__c + 1]; + __result[0] = __DIGIT_TABLE<_CharT>[__c]; } else { - __result[0] = static_cast('0' + __output2); + __result[0] = static_cast<_CharT>(_WIDEN(_CharT, '0') + __output2); } // Print decimal point if needed. uint32_t __index; if (__olength > 1) { - __result[1] = '.'; + __result[1] = _WIDEN(_CharT, '.'); __index = __olength + 1; } else { __index = 1; } // Print the exponent. - __result[__index++] = 'e'; + __result[__index++] = _WIDEN(_CharT, 'e'); if (_Scientific_exponent < 0) { - __result[__index++] = '-'; + __result[__index++] = _WIDEN(_CharT, '-'); _Scientific_exponent = -_Scientific_exponent; } else { - __result[__index++] = '+'; + __result[__index++] = _WIDEN(_CharT, '+'); } if (_Scientific_exponent >= 100) { const int32_t __c = _Scientific_exponent % 10; - _CSTD memcpy(__result + __index, __DIGIT_TABLE + 2 * (_Scientific_exponent / 10), 2); - __result[__index + 2] = static_cast('0' + __c); + _CSTD memcpy(__result + __index, __DIGIT_TABLE<_CharT> + 2 * (_Scientific_exponent / 10), 2 * sizeof(_CharT)); + __result[__index + 2] = static_cast<_CharT>(_WIDEN(_CharT, '0') + __c); __index += 3; } else { - _CSTD memcpy(__result + __index, __DIGIT_TABLE + 2 * _Scientific_exponent, 2); + _CSTD memcpy(__result + __index, __DIGIT_TABLE<_CharT> + 2 * _Scientific_exponent, 2 * sizeof(_CharT)); __index += 2; } @@ -2245,7 +2266,8 @@ _NODISCARD inline bool __d2d_small_int(const uint64_t __ieeeMantissa, const uint return true; } -_NODISCARD inline to_chars_result __d2s_buffered_n(char* const _First, char* const _Last, const double __f, +template +_NODISCARD pair<_CharT*, errc> __d2s_buffered_n(_CharT* const _First, _CharT* const _Last, const double __f, const chars_format _Fmt) { // Step 1: Decode the floating-point number, and unify normalized and subnormal cases. @@ -2258,7 +2280,11 @@ _NODISCARD inline to_chars_result __d2s_buffered_n(char* const _First, char* con return { _Last, errc::value_too_large }; } - _CSTD memcpy(_First, "0e+00", 5); + if constexpr (is_same_v<_CharT, char>) { + _CSTD memcpy(_First, "0e+00", 5); + } else { + _CSTD memcpy(_First, L"0e+00", 5 * sizeof(wchar_t)); + } return { _First + 5, errc{} }; } @@ -2268,7 +2294,7 @@ _NODISCARD inline to_chars_result __d2s_buffered_n(char* const _First, char* con return { _Last, errc::value_too_large }; } - *_First = '0'; + *_First = _WIDEN(_CharT, '0'); return { _First + 1, errc{} }; } @@ -2331,9 +2357,9 @@ template _NODISCARD to_chars_result _Floating_to_chars_ryu( char* const _First, char* const _Last, const _Floating _Value, const chars_format _Fmt) noexcept { if constexpr (is_same_v<_Floating, float>) { - return __f2s_buffered_n(_First, _Last, _Value, _Fmt); + return _Convert_to_chars_result(__f2s_buffered_n(_First, _Last, _Value, _Fmt)); } else { - return __d2s_buffered_n(_First, _Last, _Value, _Fmt); + return _Convert_to_chars_result(__d2s_buffered_n(_First, _Last, _Value, _Fmt)); } } @@ -2376,11 +2402,13 @@ _NODISCARD to_chars_result _Floating_to_chars_fixed_precision( return {_Last, errc::value_too_large}; } - return __d2fixed_buffered_n(_First, _Last, _Value, static_cast(_Precision)); + return _Convert_to_chars_result(__d2fixed_buffered_n(_First, _Last, _Value, static_cast(_Precision))); } _STD_END +#undef _WIDEN + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) diff --git a/stl/inc/xcharconv_ryu_tables.h b/stl/inc/xcharconv_ryu_tables.h index 508d3479fdf..87bc0c2b34c 100644 --- a/stl/inc/xcharconv_ryu_tables.h +++ b/stl/inc/xcharconv_ryu_tables.h @@ -63,7 +63,9 @@ _STD_BEGIN // A table of all two-digit numbers. This is used to speed up decimal digit // generation by copying pairs of digits into the final output. -inline constexpr char __DIGIT_TABLE[200] = { +template inline constexpr _CharT __DIGIT_TABLE[] = {_CharT{}}; + +template <> inline constexpr char __DIGIT_TABLE[200] = { '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', @@ -76,6 +78,19 @@ inline constexpr char __DIGIT_TABLE[200] = { '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' }; +template <> inline constexpr wchar_t __DIGIT_TABLE[200] = { + L'0',L'0',L'0',L'1',L'0',L'2',L'0',L'3',L'0',L'4',L'0',L'5',L'0',L'6',L'0',L'7',L'0',L'8',L'0',L'9', + L'1',L'0',L'1',L'1',L'1',L'2',L'1',L'3',L'1',L'4',L'1',L'5',L'1',L'6',L'1',L'7',L'1',L'8',L'1',L'9', + L'2',L'0',L'2',L'1',L'2',L'2',L'2',L'3',L'2',L'4',L'2',L'5',L'2',L'6',L'2',L'7',L'2',L'8',L'2',L'9', + L'3',L'0',L'3',L'1',L'3',L'2',L'3',L'3',L'3',L'4',L'3',L'5',L'3',L'6',L'3',L'7',L'3',L'8',L'3',L'9', + L'4',L'0',L'4',L'1',L'4',L'2',L'4',L'3',L'4',L'4',L'4',L'5',L'4',L'6',L'4',L'7',L'4',L'8',L'4',L'9', + L'5',L'0',L'5',L'1',L'5',L'2',L'5',L'3',L'5',L'4',L'5',L'5',L'5',L'6',L'5',L'7',L'5',L'8',L'5',L'9', + L'6',L'0',L'6',L'1',L'6',L'2',L'6',L'3',L'6',L'4',L'6',L'5',L'6',L'6',L'6',L'7',L'6',L'8',L'6',L'9', + L'7',L'0',L'7',L'1',L'7',L'2',L'7',L'3',L'7',L'4',L'7',L'5',L'7',L'6',L'7',L'7',L'7',L'8',L'7',L'9', + L'8',L'0',L'8',L'1',L'8',L'2',L'8',L'3',L'8',L'4',L'8',L'5',L'8',L'6',L'8',L'7',L'8',L'8',L'8',L'9', + L'9',L'0',L'9',L'1',L'9',L'2',L'9',L'3',L'9',L'4',L'9',L'5',L'9',L'6',L'9',L'7',L'9',L'8',L'9',L'9' +}; + // ^^^^^^^^^^ DERIVED FROM digit_table.h ^^^^^^^^^^ // vvvvvvvvvv DERIVED FROM d2s_full_table.h vvvvvvvvvv diff --git a/tests/std/tests/P0067R5_charconv/test.cpp b/tests/std/tests/P0067R5_charconv/test.cpp index 466a8d72d7c..f11365572a7 100644 --- a/tests/std/tests/P0067R5_charconv/test.cpp +++ b/tests/std/tests/P0067R5_charconv/test.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include "float_hex_precision_to_chars_test_cases.hpp" #include "float_scientific_precision_to_chars_test_cases.hpp" #include "float_to_chars_test_cases.hpp" +#include "wchar_test_cases.hpp" #include using namespace std; @@ -1077,6 +1079,40 @@ void test_right_shift_64_bits_with_rounding() { assert(_Right_shift_with_rounding(0xffff'ffff'ffff'ffffULL, 64, false) == 1); } +void wchar_tests() { + static_assert(size(__DIGIT_TABLE) == size(__DIGIT_TABLE)); + auto& fac = use_facet>(locale{}); + for (size_t i = 0; i < size(__DIGIT_TABLE); ++i) { + assert(fac.widen(__DIGIT_TABLE[i]) == __DIGIT_TABLE[i]); + } + + wchar_t buffer[32]; + for (const auto& t : double_to_wide_test_cases) { + auto result = __d2s_buffered_n(begin(buffer), end(buffer), t.value, t.fmt); + const wstring_view sv(t.correct); + assert(equal(buffer, result.first, sv.begin(), sv.end())); + } + + for (const auto& t : float_to_wide_test_cases) { + auto result = __f2s_buffered_n(begin(buffer), end(buffer), t.value, t.fmt); + const wstring_view sv(t.correct); + assert(equal(buffer, result.first, sv.begin(), sv.end())); + } + + for (const auto& t : wide_digit_pairs_test_cases) { + auto result = __d2fixed_buffered_n(begin(buffer), end(buffer), t.value, static_cast(t.precision)); + const wstring_view sv(t.correct); + assert(equal(buffer, result.first, sv.begin(), sv.end())); + } +} + +// GH-1569 - Test instantiation of wchar_t helpers. +template pair std::__to_chars( + wchar_t* const, wchar_t* const, const __floating_decimal_32, chars_format, const uint32_t, const uint32_t); +template pair std::__to_chars( + wchar_t* const, wchar_t* const, const __floating_decimal_64, chars_format, const double); +template pair std::__d2fixed_buffered_n(wchar_t*, wchar_t* const, const double, const uint32_t); + int main(int argc, char** argv) { const auto start = chrono::steady_clock::now(); @@ -1090,6 +1126,8 @@ int main(int argc, char** argv) { test_right_shift_64_bits_with_rounding(); + wchar_tests(); + const auto finish = chrono::steady_clock::now(); const long long ms = chrono::duration_cast(finish - start).count(); diff --git a/tests/std/tests/P0067R5_charconv/test.hpp b/tests/std/tests/P0067R5_charconv/test.hpp index b20c766395e..3d0275b9899 100644 --- a/tests/std/tests/P0067R5_charconv/test.hpp +++ b/tests/std/tests/P0067R5_charconv/test.hpp @@ -31,6 +31,12 @@ struct FloatToCharsTestCase { const char* correct; }; +struct FloatToWideTestCase { + float value; + chars_format fmt; + const wchar_t* correct; +}; + struct FloatPrecisionToCharsTestCase { float value; chars_format fmt; @@ -52,9 +58,22 @@ struct DoubleToCharsTestCase { const char* correct; }; +struct DoubleToWideTestCase { + double value; + chars_format fmt; + const wchar_t* correct; +}; + struct DoublePrecisionToCharsTestCase { double value; chars_format fmt; int precision; const char* correct; }; + +struct DoublePrecisionToWideTestCase { + double value; + chars_format fmt; + int precision; + const wchar_t* correct; +}; diff --git a/tests/std/tests/P0067R5_charconv/wchar_test_cases.hpp b/tests/std/tests/P0067R5_charconv/wchar_test_cases.hpp new file mode 100644 index 00000000000..795ac517987 --- /dev/null +++ b/tests/std/tests/P0067R5_charconv/wchar_test_cases.hpp @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include + +#include "test.hpp" +using namespace std; + +// The wchar_t machinery is currently limited to a subset of the Ryu code. It is known to not handle: negative numbers, +// infinity, NaN, or hex formatting. + +inline constexpr DoublePrecisionToWideTestCase wide_digit_pairs_test_cases[] = { + {0.0001020304, chars_format::fixed, 10, L"0.0001020304"}, + {0.0506070809, chars_format::fixed, 10, L"0.0506070809"}, + {0.1011121314, chars_format::fixed, 10, L"0.1011121314"}, + {0.1516171819, chars_format::fixed, 10, L"0.1516171819"}, + {0.2021222324, chars_format::fixed, 10, L"0.2021222324"}, + {0.2526272829, chars_format::fixed, 10, L"0.2526272829"}, + {0.3031323334, chars_format::fixed, 10, L"0.3031323334"}, + {0.3536373839, chars_format::fixed, 10, L"0.3536373839"}, + {0.4041424344, chars_format::fixed, 10, L"0.4041424344"}, + {0.4546474849, chars_format::fixed, 10, L"0.4546474849"}, + {0.5051525354, chars_format::fixed, 10, L"0.5051525354"}, + {0.5556575859, chars_format::fixed, 10, L"0.5556575859"}, + {0.6061626364, chars_format::fixed, 10, L"0.6061626364"}, + {0.6566676869, chars_format::fixed, 10, L"0.6566676869"}, + {0.7071727374, chars_format::fixed, 10, L"0.7071727374"}, + {0.7576777879, chars_format::fixed, 10, L"0.7576777879"}, + {0.8081828384, chars_format::fixed, 10, L"0.8081828384"}, + {0.8586878889, chars_format::fixed, 10, L"0.8586878889"}, + {0.9091929394, chars_format::fixed, 10, L"0.9091929394"}, + {0.9596979899, chars_format::fixed, 10, L"0.9596979899"}, +}; + +inline constexpr DoubleToWideTestCase double_to_wide_test_cases[] = { + // Test special cases (zero, inf, nan) and an ordinary case. Also test negative signs. + {0.0, chars_format::scientific, L"0e+00"}, + //{-0.0, chars_format::scientific, L"-0e+00"}, + //{double_inf, chars_format::scientific, L"inf"}, + //{-double_inf, chars_format::scientific, L"-inf"}, + //{double_nan, chars_format::scientific, L"nan"}, + //{-double_nan, chars_format::scientific, L"-nan(ind)"}, + //{double_nan_payload, chars_format::scientific, L"nan"}, + //{-double_nan_payload, chars_format::scientific, L"-nan"}, + {2.018, chars_format::scientific, L"2.018e+00"}, + //{-2.018, chars_format::scientific, L"-2.018e+00"}, + {0.2018, chars_format::scientific, L"2.018e-01"}, + //{-0.2018, chars_format::scientific, L"-2.018e-01"}, + + // Ditto for fixed, which doesn't emit exponents. + {0.0, chars_format::fixed, L"0"}, + //{-0.0, chars_format::fixed, L"-0"}, + //{double_inf, chars_format::fixed, L"inf"}, + //{-double_inf, chars_format::fixed, L"-inf"}, + //{double_nan, chars_format::fixed, L"nan"}, + //{-double_nan, chars_format::fixed, L"-nan(ind)"}, + //{double_nan_payload, chars_format::fixed, L"nan"}, + //{-double_nan_payload, chars_format::fixed, L"-nan"}, + {2.018, chars_format::fixed, L"2.018"}, + //{-2.018, chars_format::fixed, L"-2.018"}, + + // Ditto for general, which selects fixed for the scientific exponent 0. + {0.0, chars_format::general, L"0"}, + //{-0.0, chars_format::general, L"-0"}, + //{double_inf, chars_format::general, L"inf"}, + //{-double_inf, chars_format::general, L"-inf"}, + //{double_nan, chars_format::general, L"nan"}, + //{-double_nan, chars_format::general, L"-nan(ind)"}, + //{double_nan_payload, chars_format::general, L"nan"}, + //{-double_nan_payload, chars_format::general, L"-nan"}, + {2.018, chars_format::general, L"2.018"}, + //{-2.018, chars_format::general, L"-2.018"}, + + // Ditto for plain, which selects fixed because it's shorter for these values. + {0.0, chars_format{}, L"0"}, + //{-0.0, chars_format{}, L"-0"}, + //{double_inf, chars_format{}, L"inf"}, + //{-double_inf, chars_format{}, L"-inf"}, + //{double_nan, chars_format{}, L"nan"}, + //{-double_nan, chars_format{}, L"-nan(ind)"}, + //{double_nan_payload, chars_format{}, L"nan"}, + //{-double_nan_payload, chars_format{}, L"-nan"}, + {2.018, chars_format{}, L"2.018"}, + //{-2.018, chars_format{}, L"-2.018"}, + + // Ditto for hex. + //{0.0, chars_format::hex, L"0p+0"}, + //{-0.0, chars_format::hex, L"-0p+0"}, + //{double_inf, chars_format::hex, L"inf"}, + //{-double_inf, chars_format::hex, L"-inf"}, + //{double_nan, chars_format::hex, L"nan"}, + //{-double_nan, chars_format::hex, L"-nan(ind)"}, + //{double_nan_payload, chars_format::hex, L"nan"}, + //{-double_nan_payload, chars_format::hex, L"-nan"}, + //{0x1.729p+0, chars_format::hex, L"1.729p+0"}, + //{-0x1.729p+0, chars_format::hex, L"-1.729p+0"}, + //{0x1.729p-1, chars_format::hex, L"1.729p-1"}, + //{-0x1.729p-1, chars_format::hex, L"-1.729p-1"}, +}; + +inline constexpr FloatToWideTestCase float_to_wide_test_cases[] = { + // Test special cases (zero, inf, nan) and an ordinary case. Also test negative signs. + {0.0f, chars_format::scientific, L"0e+00"}, + //{-0.0f, chars_format::scientific, L"-0e+00"}, + //{float_inf, chars_format::scientific, L"inf"}, + //{-float_inf, chars_format::scientific, L"-inf"}, + //{float_nan, chars_format::scientific, L"nan"}, + //{-float_nan, chars_format::scientific, L"-nan(ind)"}, + //{float_nan_payload, chars_format::scientific, L"nan"}, + //{-float_nan_payload, chars_format::scientific, L"-nan"}, + {2.018f, chars_format::scientific, L"2.018e+00"}, + //{-2.018f, chars_format::scientific, L"-2.018e+00"}, + {0.2018f, chars_format::scientific, L"2.018e-01"}, + //{-0.2018f, chars_format::scientific, L"-2.018e-01"}, + + // Ditto for fixed, which doesn't emit exponents. + {0.0f, chars_format::fixed, L"0"}, + //{-0.0f, chars_format::fixed, L"-0"}, + //{float_inf, chars_format::fixed, L"inf"}, + //{-float_inf, chars_format::fixed, L"-inf"}, + //{float_nan, chars_format::fixed, L"nan"}, + //{-float_nan, chars_format::fixed, L"-nan(ind)"}, + //{float_nan_payload, chars_format::fixed, L"nan"}, + //{-float_nan_payload, chars_format::fixed, L"-nan"}, + {2.018f, chars_format::fixed, L"2.018"}, + //{-2.018f, chars_format::fixed, L"-2.018"}, + + // Ditto for general, which selects fixed for the scientific exponent 0. + {0.0f, chars_format::general, L"0"}, + //{-0.0f, chars_format::general, L"-0"}, + //{float_inf, chars_format::general, L"inf"}, + //{-float_inf, chars_format::general, L"-inf"}, + //{float_nan, chars_format::general, L"nan"}, + //{-float_nan, chars_format::general, L"-nan(ind)"}, + //{float_nan_payload, chars_format::general, L"nan"}, + //{-float_nan_payload, chars_format::general, L"-nan"}, + {2.018f, chars_format::general, L"2.018"}, + //{-2.018f, chars_format::general, L"-2.018"}, + + // Ditto for plain, which selects fixed because it's shorter for these values. + {0.0f, chars_format{}, L"0"}, + //{-0.0f, chars_format{}, L"-0"}, + //{float_inf, chars_format{}, L"inf"}, + //{-float_inf, chars_format{}, L"-inf"}, + //{float_nan, chars_format{}, L"nan"}, + //{-float_nan, chars_format{}, L"-nan(ind)"}, + //{float_nan_payload, chars_format{}, L"nan"}, + //{-float_nan_payload, chars_format{}, L"-nan"}, + {2.018f, chars_format{}, L"2.018"}, + //{-2.018f, chars_format{}, L"-2.018"}, + + // Ditto for hex. + //{0.0f, chars_format::hex, L"0p+0"}, + //{-0.0f, chars_format::hex, L"-0p+0"}, + //{float_inf, chars_format::hex, L"inf"}, + //{-float_inf, chars_format::hex, L"-inf"}, + //{float_nan, chars_format::hex, L"nan"}, + //{-float_nan, chars_format::hex, L"-nan(ind)"}, + //{float_nan_payload, chars_format::hex, L"nan"}, + //{-float_nan_payload, chars_format::hex, L"-nan"}, + //{0x1.729p+0f, chars_format::hex, L"1.729p+0"}, + //{-0x1.729p+0f, chars_format::hex, L"-1.729p+0"}, + //{0x1.729p-1f, chars_format::hex, L"1.729p-1"}, + //{-0x1.729p-1f, chars_format::hex, L"-1.729p-1"}, +};