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,79 @@ '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 const 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 const 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); + memcpy(buffer, itoa_char_traits::digitlut(i * 2), 2 * sizeof(C)); 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 +162,9 @@ return buffer; } -char* -__u64toa(uint64_t value, char* buffer) +template +C* +__u64toa_t(uint64_t value, C* buffer) { if (value < 100000000) { @@ -227,6 +269,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,33 +349,6 @@ // as_string -template -inline -S -as_string(P sprintf_like, S s, const typename S::value_type* fmt, V a) -{ - typedef typename S::size_type size_type; - size_type available = s.size(); - while (true) - { - int status = sprintf_like(&s[0], available + 1, fmt, a); - if ( status >= 0 ) - { - size_type used = static_cast(status); - if ( used <= available ) - { - s.resize( used ); - break; - } - available = used; // Assume this is advice of how much space we need. - } - else - available = available * 2 + 1; - s.resize(available); - } - return s; -} - template ::value> struct initial_string; @@ -417,6 +391,34 @@ } }; +template +inline +S +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) + { + int status = sprintf_like(&s[0], available + 1, fmt, a); + if ( status >= 0 ) + { + size_type used = static_cast(status); + if ( used <= available ) + { + s.resize( used ); + break; + } + available = used; // Assume this is advice of how much space we need. + } + else + available = available * 2 + 1; + s.resize(available); + } + return s; +} + typedef int (*wide_printf)(wchar_t* __restrict, size_t, const wchar_t*__restrict, ...); inline @@ -430,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 as_string(snprintf, initial_string()(), "%d", val); + return i_to_string(snprintf, "%d", val); } string to_string(unsigned val) { - return as_string(snprintf, initial_string()(), "%u", val); + return i_to_string(snprintf, "%u", val); } string to_string(long val) { - return as_string(snprintf, initial_string()(), "%ld", val); + return i_to_string(snprintf, "%ld", val); } string to_string(unsigned long val) { - return as_string(snprintf, initial_string()(), "%lu", val); + return i_to_string(snprintf, "%lu", val); } string to_string(long long val) { - return as_string(snprintf, initial_string()(), "%lld", val); + return i_to_string(snprintf, "%lld", val); } string to_string(unsigned long long val) { - return as_string(snprintf, initial_string()(), "%llu", val); + return i_to_string(snprintf, "%llu", val); } string to_string(float val) { - return as_string(snprintf, initial_string()(), "%f", val); + return as_string(snprintf, "%f", val); } string to_string(double val) { - return as_string(snprintf, initial_string()(), "%f", val); + return as_string(snprintf, "%f", val); } string to_string(long double val) { - return as_string(snprintf, initial_string()(), "%Lf", val); + return as_string(snprintf, "%Lf", val); } wstring to_wstring(int val) { - return as_string(get_swprintf(), initial_string()(), L"%d", val); + return i_to_string(get_swprintf(), L"%d", val); } wstring to_wstring(unsigned val) { - return as_string(get_swprintf(), initial_string()(), L"%u", val); + return i_to_string(get_swprintf(), L"%u", val); } wstring to_wstring(long val) { - return as_string(get_swprintf(), initial_string()(), L"%ld", val); + return i_to_string(get_swprintf(), L"%ld", val); } wstring to_wstring(unsigned long val) { - return as_string(get_swprintf(), initial_string()(), L"%lu", val); + return i_to_string(get_swprintf(), L"%lu", val); } wstring to_wstring(long long val) { - return as_string(get_swprintf(), initial_string()(), L"%lld", val); + return i_to_string(get_swprintf(), L"%lld", val); } wstring to_wstring(unsigned long long val) { - return as_string(get_swprintf(), initial_string()(), L"%llu", val); + return i_to_string(get_swprintf(), L"%llu", val); } wstring to_wstring(float val) { - return as_string(get_swprintf(), initial_string()(), L"%f", val); + return as_string(get_swprintf(), L"%f", val); } wstring to_wstring(double val) { - return as_string(get_swprintf(), initial_string()(), L"%f", val); + return as_string(get_swprintf(), L"%f", val); } wstring to_wstring(long double val) { - return as_string(get_swprintf(), initial_string()(), L"%Lf", val); + return as_string(get_swprintf(), L"%Lf", val); } _LIBCPP_END_NAMESPACE_STD