diff --git a/libcxx/benchmarks/to_chars.bench.cpp b/libcxx/benchmarks/to_chars.bench.cpp --- a/libcxx/benchmarks/to_chars.bench.cpp +++ b/libcxx/benchmarks/to_chars.bench.cpp @@ -20,6 +20,8 @@ return result; }(); +//#define ALL + static void BM_to_chars_good(benchmark::State& state) { char buffer[128]; int base = state.range(0); @@ -27,7 +29,12 @@ for (auto value : input) benchmark::DoNotOptimize(std::to_chars(buffer, &buffer[128], value, base)); } -BENCHMARK(BM_to_chars_good)->DenseRange(2, 36, 1); +BENCHMARK(BM_to_chars_good) +#ifdef ALL + ->DenseRange(2, 36, 1); +#else + ->Arg(2)->Arg(8)->Arg(9)->Arg(10)->Arg(16); +#endif static void BM_to_chars_bad(benchmark::State& state) { char buffer[128]; @@ -47,7 +54,12 @@ for (auto element : data) benchmark::DoNotOptimize(std::to_chars(buffer, &buffer[element.size], element.value, base)); } -BENCHMARK(BM_to_chars_bad)->DenseRange(2, 36, 1); +BENCHMARK(BM_to_chars_bad) +#ifdef ALL + ->DenseRange(2, 36, 1); +#else + ->Arg(2)->Arg(8)->Arg(9)->Arg(10)->Arg(16); +#endif int main(int argc, char** argv) { benchmark::Initialize(&argc, argv); diff --git a/libcxx/include/charconv b/libcxx/include/charconv --- a/libcxx/include/charconv +++ b/libcxx/include/charconv @@ -318,6 +318,201 @@ return __to_chars_integral(__first, __last, __x, __base, false_type()); } +namespace __itoa { + +template +struct _LIBCPP_HIDDEN __integral; + +template <> +struct _LIBCPP_HIDDEN __integral<2> { + enum : unsigned { _Base = 2 }; + + template + _LIBCPP_INLINE_VISIBILITY static constexpr int __width(_Tp __value) noexcept { + // If value == 0 still need one digit. If the value != this has no + // effect since the code scans for the most significat bit set. (Note + // that __libcpp_clz doesn't work for 0.) + return numeric_limits<_Tp>::digits - _VSTD::__libcpp_clz(__value | 1); + } + + template + _LIBCPP_INLINE_VISIBILITY static to_chars_result __to_chars(char* __first, char* __last, _Tp __value) { + ptrdiff_t __cap = __last - __first; + int __n = __width(__value); + if (__n > __cap) + return {__last, errc::value_too_large}; + + __last = __first + __n; + char* __p = __last; + const unsigned __divisor = 16; + while (__value > __divisor) { + unsigned __c = __value % __divisor; + __value /= __divisor; + __p -= 4; + _VSTD::memcpy(__p, &__base_2_lut[4 * __c], 4); + } + do { + unsigned __c = __value % _Base; + __value /= _Base; + *--__p = "01"[__c]; + } while (__value != 0); + return {__last, errc(0)}; + } + + static _LIBCPP_CONSTEXPR char __base_2_lut[64] = { + '0', '0', '0', '0', + '0', '0', '0', '1', + '0', '0', '1', '0', + '0', '0', '1', '1', + '0', '1', '0', '0', + '0', '1', '0', '1', + '0', '1', '1', '0', + '0', '1', '1', '1', + '1', '0', '0', '0', + '1', '0', '0', '1', + '1', '0', '1', '0', + '1', '0', '1', '1', + '1', '1', '0', '0', + '1', '1', '0', '1', + '1', '1', '1', '0', + '1', '1', '1', '1' + }; +}; + +template <> +struct _LIBCPP_HIDDEN __integral<8> { + enum : unsigned { _Base = 8 }; + + template + _LIBCPP_INLINE_VISIBILITY static constexpr int __width(_Tp __value) noexcept { + // If value == 0 still need one digit. If the value != this has no + // effect since the code scans for the most significat bit set. (Note + // that __libcpp_clz doesn't work for 0.) + return ((numeric_limits<_Tp>::digits - _VSTD::__libcpp_clz(__value | 1)) + 2) / 3; + } + + template + _LIBCPP_INLINE_VISIBILITY static to_chars_result __to_chars(char* __first, char* __last, _Tp __value) { + ptrdiff_t __cap = __last - __first; + int __n = __width(__value); + if (__n > __cap) + return {__last, errc::value_too_large}; + + __last = __first + __n; + char* __p = __last; + unsigned __divisor = 64; + while (__value > __divisor) { + unsigned __c = __value % __divisor; + __value /= __divisor; + __p -= 2; + _VSTD::memcpy(__p, &__base_8_lut[2 * __c], 2); + } + do { + unsigned __c = __value % _Base; + __value /= _Base; + *--__p = "01234567"[__c]; + } while (__value != 0); + return {__last, errc(0)}; + } + + static _LIBCPP_CONSTEXPR char __base_8_lut[128] = { + '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', + '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', + '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', + '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', + '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', + '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', + '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', + '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7' + }; +}; + +template <> +struct _LIBCPP_HIDDEN __integral<16> { + enum : unsigned { _Base = 16 }; + + template + _LIBCPP_INLINE_VISIBILITY static constexpr int __width(_Tp __value) noexcept { + // If value == 0 still need one digit. If the value != this has no + // effect since the code scans for the most significat bit set. (Note + // that __libcpp_clz doesn't work for 0.) + return (numeric_limits<_Tp>::digits - _VSTD::__libcpp_clz(__value | 1) + 3) / 4; + } + + template + _LIBCPP_INLINE_VISIBILITY static to_chars_result __to_chars(char* __first, char* __last, _Tp __value) { + ptrdiff_t __cap = __last - __first; + int __n = __width(__value); + if (__n > __cap) + return {__last, errc::value_too_large}; + + __last = __first + __n; + char* __p = __last; + unsigned __divisor = 256; + while (__value > __divisor) { + unsigned __c = __value % __divisor; + __value /= __divisor; + __p -= 2; + _VSTD::memcpy(__p, &__base_16_lut[2 * __c], 2); + } + if (__first != __last) + do { + unsigned __c = __value % _Base; + __value /= _Base; + *--__p = "0123456789abcdef"[__c]; + } while (__value != 0); + return {__last, errc(0)}; + } + static _LIBCPP_CONSTEXPR char __base_16_lut[512] = { + '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', '0', 'a', '0', 'b', '0', 'c', '0', 'd', '0', 'e', '0', 'f', + '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '1', 'a', '1', 'b', '1', 'c', '1', 'd', '1', 'e', '1', 'f', + '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '2', 'a', '2', 'b', '2', 'c', '2', 'd', '2', 'e', '2', 'f', + '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '3', 'a', '3', 'b', '3', 'c', '3', 'd', '3', 'e', '3', 'f', + '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '4', 'a', '4', 'b', '4', 'c', '4', 'd', '4', 'e', '4', 'f', + '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', '5', 'a', '5', 'b', '5', 'c', '5', 'd', '5', 'e', '5', 'f', + '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', '6', 'a', '6', 'b', '6', 'c', '6', 'd', '6', 'e', '6', 'f', + '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', '7', 'a', '7', 'b', '7', 'c', '7', 'd', '7', 'e', '7', 'f', + '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '8', 'a', '8', 'b', '8', 'c', '8', 'd', '8', 'e', '8', 'f', + '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9', '9', 'a', '9', 'b', '9', 'c', '9', 'd', '9', 'e', '9', 'f', + 'a', '0', 'a', '1', 'a', '2', 'a', '3', 'a', '4', 'a', '5', 'a', '6', 'a', '7', 'a', '8', 'a', '9', 'a', 'a', 'a', 'b', 'a', 'c', 'a', 'd', 'a', 'e', 'a', 'f', + 'b', '0', 'b', '1', 'b', '2', 'b', '3', 'b', '4', 'b', '5', 'b', '6', 'b', '7', 'b', '8', 'b', '9', 'b', 'a', 'b', 'b', 'b', 'c', 'b', 'd', 'b', 'e', 'b', 'f', + 'c', '0', 'c', '1', 'c', '2', 'c', '3', 'c', '4', 'c', '5', 'c', '6', 'c', '7', 'c', '8', 'c', '9', 'c', 'a', 'c', 'b', 'c', 'c', 'c', 'd', 'c', 'e', 'c', 'f', + 'd', '0', 'd', '1', 'd', '2', 'd', '3', 'd', '4', 'd', '5', 'd', '6', 'd', '7', 'd', '8', 'd', '9', 'd', 'a', 'd', 'b', 'd', 'c', 'd', 'd', 'd', 'e', 'd', 'f', + 'e', '0', 'e', '1', 'e', '2', 'e', '3', 'e', '4', 'e', '5', 'e', '6', 'e', '7', 'e', '8', 'e', '9', 'e', 'a', 'e', 'b', 'e', 'c', 'e', 'd', 'e', 'e', 'e', 'f', + 'f', '0', 'f', '1', 'f', '2', 'f', '3', 'f', '4', 'f', '5', 'f', '6', 'f', '7', 'f', '8', 'f', '9', 'f', 'a', 'f', 'b', 'f', 'c', 'f', 'd', 'f', 'e', 'f', 'f' + }; +}; + +} // namespace __itoa + +template = sizeof(unsigned)), int>::type = 0> +_LIBCPP_AVAILABILITY_TO_CHARS _LIBCPP_INLINE_VISIBILITY int +__to_chars_integral_width(_Tp __value) { + return __itoa::__integral<_Base>::__width(__value); +} + +template ::type = 0> +_LIBCPP_AVAILABILITY_TO_CHARS _LIBCPP_INLINE_VISIBILITY int +__to_chars_integral_width(_Tp __value) { + return __to_chars_integral_width<_Base>(static_cast(__value)); +} + +template = sizeof(unsigned)), int>::type = 0> +_LIBCPP_AVAILABILITY_TO_CHARS _LIBCPP_INLINE_VISIBILITY to_chars_result +__to_chars_integral(char* __first, char* __last, _Tp __value) { + return __itoa::__integral<_Base>::__to_chars(__first, __last, __value); +} + +template ::type = 0> +_LIBCPP_AVAILABILITY_TO_CHARS _LIBCPP_INLINE_VISIBILITY to_chars_result +__to_chars_integral(char* __first, char* __last, _Tp __value) { + return __to_chars_integral<_Base>(__first, __last, static_cast(__value)); +} + template _LIBCPP_AVAILABILITY_TO_CHARS _LIBCPP_INLINE_VISIBILITY int __to_chars_integral_width(_Tp __value, unsigned __base) { _LIBCPP_ASSERT(__value >= 0, "The function requires a non-negative value."); @@ -350,9 +545,18 @@ __to_chars_integral(char* __first, char* __last, _Tp __value, int __base, false_type) { - if (__base == 10) + if (__base == 10) [[likely]] return __to_chars_itoa(__first, __last, __value, false_type()); - +#if 1 + switch (__base) { + case 2: + return __to_chars_integral<2>(__first, __last, __value); + case 8: + return __to_chars_integral<8>(__first, __last, __value); + case 16: + return __to_chars_integral<16>(__first, __last, __value); + } +#endif ptrdiff_t __cap = __last - __first; int __n = __to_chars_integral_width(__value, __base); if (__n > __cap)