Index: libcxx/include/charconv =================================================================== --- libcxx/include/charconv +++ libcxx/include/charconv @@ -94,6 +94,8 @@ namespace __itoa { _LIBCPP_FUNC_VIS char* __u64toa(uint64_t __value, char* __buffer); _LIBCPP_FUNC_VIS char* __u32toa(uint32_t __value, char* __buffer); +_LIBCPP_FUNC_VIS wchar_t* __u64toa(uint64_t __value, wchar_t* __buffer); +_LIBCPP_FUNC_VIS wchar_t* __u32toa(uint32_t __value, wchar_t* __buffer); } #ifndef _LIBCPP_CXX03_LANG @@ -124,6 +126,21 @@ namespace __itoa { +template +inline _LIBCPP_INLINE_VISIBILITY _Cp __minus(); + +template <> +inline _LIBCPP_INLINE_VISIBILITY char __minus() +{ + return '-'; +} + +template <> +inline _LIBCPP_INLINE_VISIBILITY wchar_t __minus() +{ + return L'-'; +} + static _LIBCPP_CONSTEXPR uint64_t __pow10_64[] = { UINT64_C(0), UINT64_C(10), @@ -167,7 +184,8 @@ } #endif - static _LIBCPP_INLINE_VISIBILITY char* __convert(_Tp __v, char* __p) + template + static _LIBCPP_INLINE_VISIBILITY _Cp* __convert(_Tp __v, _Cp* __p) { return __u64toa(__v, __p); } @@ -189,7 +207,8 @@ } #endif - static _LIBCPP_INLINE_VISIBILITY char* __convert(_Tp __v, char* __p) + template + static _LIBCPP_INLINE_VISIBILITY _Cp* __convert(_Tp __v, _Cp* __p) { return __u32toa(__v, __p); } @@ -291,23 +310,36 @@ return static_cast::type>(__x); } -template +template +struct _LIBCPP_HIDDEN __to_chars_itoa_result +{ + _Cp* ptr; + errc ec; +}; + inline _LIBCPP_INLINE_VISIBILITY to_chars_result -__to_chars_itoa(char* __first, char* __last, _Tp __value, true_type) +__make_to_chars_result(__to_chars_itoa_result __x) +{ + return {__x.ptr, __x.ec}; +} + +template +inline _LIBCPP_INLINE_VISIBILITY __to_chars_itoa_result<_Cp> +__to_chars_itoa(_Cp* __first, _Cp* __last, _Tp __value, true_type) { auto __x = __to_unsigned(__value); if (__value < 0 && __first != __last) { - *__first++ = '-'; + *__first++ = __itoa::__minus<_Cp>(); __x = __complement(__x); } return __to_chars_itoa(__first, __last, __x, false_type()); } -template -inline _LIBCPP_INLINE_VISIBILITY to_chars_result -__to_chars_itoa(char* __first, char* __last, _Tp __value, false_type) +template +inline _LIBCPP_INLINE_VISIBILITY __to_chars_itoa_result<_Cp> +__to_chars_itoa(_Cp* __first, _Cp* __last, _Tp __value, false_type) { using __tx = __itoa::__traits<_Tp>; auto __diff = __last - __first; @@ -322,12 +354,12 @@ return {__tx::__convert(__value, __first), {}}; else { - char __buf[__tx::digits]; + _Cp __buf[__tx::digits]; auto __p = __tx::__convert(__value, __buf); auto __len = __p - __buf; if (__len <= __diff) { - memcpy(__first, __buf, __len); + memcpy(__first, __buf, __len * sizeof(_Cp)); return {__first + __len, {}}; } else @@ -357,7 +389,10 @@ false_type) { if (__base == 10) - return __to_chars_itoa(__first, __last, __value, false_type()); + { + const auto res = __to_chars_itoa(__first, __last, __value, false_type()); + return __make_to_chars_result(res); + } auto __p = __last; while (__p != __first) @@ -383,7 +418,8 @@ inline _LIBCPP_INLINE_VISIBILITY to_chars_result to_chars(char* __first, char* __last, _Tp __value) { - return __to_chars_itoa(__first, __last, __value, is_signed<_Tp>()); + const auto res = __to_chars_itoa(__first, __last, __value, is_signed<_Tp>()); + return __make_to_chars_result(res); } template ::value, int>::type = 0> Index: libcxx/src/charconv.cpp =================================================================== --- libcxx/src/charconv.cpp +++ libcxx/src/charconv.cpp @@ -30,38 +30,81 @@ '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'}; -template -inline _LIBCPP_INLINE_VISIBILITY char* -append1(char* buffer, T i) +static constexpr wchar_t cDigitsLutW[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'}; + +template +struct itoa_char_traits; + +template <> +struct itoa_char_traits +{ + static constexpr _LIBCPP_INLINE_VISIBILITY char zero() { return '0'; } + + static constexpr _LIBCPP_INLINE_VISIBILITY char + digitlut(int i) { return cDigitsLut[i]; } +}; + +template <> +struct itoa_char_traits { - *buffer = '0' + static_cast(i); + static constexpr _LIBCPP_INLINE_VISIBILITY wchar_t zero() { return L'0'; } + + static constexpr _LIBCPP_INLINE_VISIBILITY wchar_t + digitlut(int i) { return cDigitsLutW[i]; } +}; + +template +inline _LIBCPP_INLINE_VISIBILITY C* +append1(C* buffer, T i) +{ + *buffer = itoa_char_traits::zero() + static_cast(i); return buffer + 1; } -template -inline _LIBCPP_INLINE_VISIBILITY char* -append2(char* buffer, T i) +template +inline _LIBCPP_INLINE_VISIBILITY C* +append2(C* buffer, T i) { - memcpy(buffer, &cDigitsLut[(i)*2], 2); + const int ix = static_cast(i) * 2; + *buffer = itoa_char_traits::digitlut(ix); + *(buffer + 1) = itoa_char_traits::digitlut(ix + 1); return buffer + 2; } -template -inline _LIBCPP_INLINE_VISIBILITY char* -append3(char* buffer, T i) +template +inline _LIBCPP_INLINE_VISIBILITY C* +append3(C* buffer, T i) { return append2(append1(buffer, (i) / 100), (i) % 100); } -template -inline _LIBCPP_INLINE_VISIBILITY char* -append4(char* buffer, T i) +template +inline _LIBCPP_INLINE_VISIBILITY C* +append4(C* buffer, T i) { return append2(append2(buffer, (i) / 100), (i) % 100); } -char* -__u32toa(uint32_t value, char* buffer) +template +C* +__u32toa_t(uint32_t value, C* buffer) { if (value < 10000) { @@ -121,8 +164,9 @@ return buffer; } -char* -__u64toa(uint64_t value, char* buffer) +template +C* +__u64toa_t(uint64_t value, C* buffer) { if (value < 100000000) { @@ -227,6 +271,26 @@ return buffer; } +char* __u32toa(uint32_t value, char* buffer) +{ + return __u32toa_t(value, buffer); +} + +wchar_t* __u32toa(uint32_t value, wchar_t* buffer) +{ + return __u32toa_t(value, buffer); +} + +char* __u64toa(uint64_t value, char* buffer) +{ + return __u64toa_t(value, buffer); +} + +wchar_t* __u64toa(uint64_t value, wchar_t* buffer) +{ + return __u64toa_t(value, buffer); +} + } // namespace __itoa _LIBCPP_END_NAMESPACE_STD Index: libcxx/src/string.cpp =================================================================== --- libcxx/src/string.cpp +++ libcxx/src/string.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "string" +#include "charconv" #include "cstdlib" #include "cwchar" #include "cerrno" @@ -348,90 +349,54 @@ // as_string -template -struct string_traits; - -template<> -struct string_traits -{ - static char zero() { return '0'; } - static char minus() { return '-'; } -}; +template ::value> +struct initial_string; -template<> -struct string_traits +template +struct initial_string { - static wchar_t zero() { return L'0'; } - static wchar_t minus() { return L'-'; } + string + operator()() const + { + string s; + s.resize(s.capacity()); + return s; + } }; -// returns index of the last written digit -template -inline -int -write_positive_in_buffer_tail(V a, typename S::value_type* buf, const int size) +template +struct initial_string { - const auto zero = string_traits::zero(); - int start = size; - do + wstring + operator()() const { - buf[--start] = zero + static_cast(a % 10); - a /= 10; + const size_t n = (numeric_limits::digits / 3) + + ((numeric_limits::digits % 3) != 0) + + 1; + wstring s(n, wchar_t()); + s.resize(s.capacity()); + return s; } - while(a); - return start; -} +}; -template -inline -S -signed_to_string(V a) +template +struct initial_string { - constexpr int size = 4 * sizeof(V); - typename S::value_type buf[size] = {}; - - int start = size; - - if (a < 0) - { - // std::numeric_limits::min() can not be converted to unsigned value - // of the same type, split off the last char for negative values. - // - // (a % 10) <= 0 according to "Multiplicative operators" paragraph of - // C++ standard with reference to "truncation towards zero" - const auto zero = string_traits::zero(); - buf[--start] = zero + static_cast(-(a % 10)); - a /= 10; - - if (a != 0) - start = write_positive_in_buffer_tail(-a, buf, start); - - buf[--start] = string_traits::minus(); - } - else + wstring + operator()() const { - start = write_positive_in_buffer_tail(a, buf, start); + wstring s(20, wchar_t()); + s.resize(s.capacity()); + return s; } - - return S(buf + start, buf + size); -} - -template -inline -S -unsigned_to_string(const V a) -{ - constexpr int size = 4 * sizeof(V); - typename S::value_type buf[size] = {}; - int start = write_positive_in_buffer_tail(a, buf, size); - return S(buf + start, buf + size); -} +}; template inline S -f_to_string(P sprintf_like, S s, const typename S::value_type* fmt, V a) +as_string(P sprintf_like, const typename S::value_type* fmt, V a) { + S s = initial_string()(); typedef typename S::size_type size_type; size_type available = s.size(); while (true) @@ -454,22 +419,6 @@ return s; } -inline -string initial_string() -{ - string s; - s.resize(s.capacity()); - return s; -} - -inline -wstring initial_wstring() -{ - wstring s(20, wchar_t()); - s.resize(s.capacity()); - return s; -} - typedef int (*wide_printf)(wchar_t* __restrict, size_t, const wchar_t*__restrict, ...); inline @@ -483,95 +432,110 @@ #endif } +template +inline +S i_to_string(P sprintf_like, const typename S::value_type* fmt, V v) +{ + // try fast conversion using to_chars + constexpr size_t bufsize = numeric_limits::digits10 + 1; // +1 for minus + typename S::value_type buf[bufsize]; + const auto res = __to_chars_itoa(buf, buf + bufsize, v, is_signed()); + if (res.ec == errc()) + return S(buf, res.ptr); + + // fallback to slow path on error + return as_string(sprintf_like, fmt, v); +} + } // unnamed namespace string to_string(int val) { - return signed_to_string(val); + return i_to_string(snprintf, "%d", val); } string to_string(unsigned val) { - return unsigned_to_string(val); + return i_to_string(snprintf, "%u", val); } string to_string(long val) { - return signed_to_string(val); + return i_to_string(snprintf, "%ld", val); } string to_string(unsigned long val) { - return unsigned_to_string(val); + return i_to_string(snprintf, "%lu", val); } string to_string(long long val) { - return signed_to_string(val); + return i_to_string(snprintf, "%lld", val); } string to_string(unsigned long long val) { - return unsigned_to_string(val); + return i_to_string(snprintf, "%llu", val); } string to_string(float val) { - return f_to_string(snprintf, initial_string(), "%f", val); + return as_string(snprintf, "%f", val); } string to_string(double val) { - return f_to_string(snprintf, initial_string(), "%f", val); + return as_string(snprintf, "%f", val); } string to_string(long double val) { - return f_to_string(snprintf, initial_string(), "%Lf", val); + return as_string(snprintf, "%Lf", val); } wstring to_wstring(int val) { - return signed_to_string(val); + return i_to_string(get_swprintf(), L"%d", val); } wstring to_wstring(unsigned val) { - return unsigned_to_string(val); + return i_to_string(get_swprintf(), L"%u", val); } wstring to_wstring(long val) { - return signed_to_string(val); + return i_to_string(get_swprintf(), L"%ld", val); } wstring to_wstring(unsigned long val) { - return unsigned_to_string(val); + return i_to_string(get_swprintf(), L"%lu", val); } wstring to_wstring(long long val) { - return signed_to_string(val); + return i_to_string(get_swprintf(), L"%lld", val); } wstring to_wstring(unsigned long long val) { - return unsigned_to_string(val); + return i_to_string(get_swprintf(), L"%llu", val); } wstring to_wstring(float val) { - return f_to_string(get_swprintf(), initial_wstring(), L"%f", val); + return as_string(get_swprintf(), L"%f", val); } wstring to_wstring(double val) { - return f_to_string(get_swprintf(), initial_wstring(), L"%f", val); + return as_string(get_swprintf(), L"%f", val); } wstring to_wstring(long double val) { - return f_to_string(get_swprintf(), initial_wstring(), L"%Lf", val); + return as_string(get_swprintf(), L"%Lf", val); } _LIBCPP_END_NAMESPACE_STD Index: libcxx/test/std/strings/string.conversions/to_string.pass.cpp =================================================================== --- libcxx/test/std/strings/string.conversions/to_string.pass.cpp +++ libcxx/test/std/strings/string.conversions/to_string.pass.cpp @@ -45,12 +45,6 @@ assert(s[s.size()] == 0); assert(s == "-12345"); } - { - std::string s = std::to_string(T(-2)); - assert(s.size() == 2); - assert(s[s.size()] == 0); - assert(s == "-2"); - } { std::string s = std::to_string(std::numeric_limits::max()); assert(s.size() == std::numeric_limits::digits10 + 1); Index: libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp =================================================================== --- libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp +++ libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp @@ -45,12 +45,6 @@ assert(s[s.size()] == 0); assert(s == L"-12345"); } - { - std::wstring s = std::to_wstring(T(-2)); - assert(s.size() == 2); - assert(s[s.size()] == 0); - assert(s == L"-2"); - } { std::wstring s = std::to_wstring(std::numeric_limits::max()); assert(s.size() == std::numeric_limits::digits10 + 1);