diff --git a/libcxx/include/__format/formatter_integral.h b/libcxx/include/__format/formatter_integral.h --- a/libcxx/include/__format/formatter_integral.h +++ b/libcxx/include/__format/formatter_integral.h @@ -143,15 +143,25 @@ // /** Wrapper around @ref to_chars, returning the output pointer. */ -template -_LIBCPP_HIDE_FROM_ABI char* __to_buffer(char* __first, char* __last, _Tp __value, int __base) { - // TODO FMT Evaluate code overhead due to not calling the internal function - // directly. (Should be zero overhead.) - to_chars_result __r = _VSTD::to_chars(__first, __last, __value, __base); +template +_LIBCPP_HIDE_FROM_ABI char* __to_buffer(char* __first, char* __last, _Tp __value) { + to_chars_result __r = _VSTD::__to_chars_integral<_Base>(__first, __last, __value); _LIBCPP_ASSERT(__r.ec == errc(0), "Internal buffer too small"); return __r.ptr; } +# ifndef _LIBCPP_HAS_NO_INT128 +template +_LIBCPP_HIDE_FROM_ABI char* __to_buffer(char* __first, char* __last, __uint128_t __value) { + if (__value <= std::numeric_limits::max()) + return __to_buffer<_Base>(__first, __last, static_cast(__value)); + + to_chars_result __r = _VSTD::__to_chars_integral<_Base>(__first, __last, __value); + _LIBCPP_ASSERT(__r.ec == errc(0), "Internal buffer too small"); + return __r.ptr; +} +# endif + /** * Helper to determine the buffer size to output a integer in Base @em x. * @@ -197,28 +207,15 @@ + 1; // Reserve space for the sign. } -template +template _LIBCPP_HIDE_FROM_ABI auto __format_integer( - _Tp __value, - auto& __ctx, - __format_spec::__parsed_specifications<_CharT> __specs, - bool __negative, - char* __begin, - char* __end, - const char* __prefix, - int __base) -> decltype(__ctx.out()) { - char* __first = __formatter::__insert_sign(__begin, __negative, __specs.__std_.__sign_); - if (__specs.__std_.__alternate_form_ && __prefix) - while (*__prefix) - *__first++ = *__prefix++; - - char* __last = __formatter::__to_buffer(__first, __end, __value, __base); - + char* __begin, char* __prefix, char* __end, auto& __ctx, __format_spec::__parsed_specifications<_CharT> __specs) + -> decltype(__ctx.out()) { # ifndef _LIBCPP_HAS_NO_LOCALIZATION if (__specs.__std_.__locale_specific_form_) { const auto& __np = use_facet>(__ctx.locale()); string __grouping = __np.grouping(); - ptrdiff_t __size = __last - __first; + ptrdiff_t __size = __end - __prefix; // Writing the grouped form has more overhead than the normal output // routines. If there will be no separators written the locale-specific // form is identical to the normal routine. Test whether to grouped form @@ -227,34 +224,99 @@ return __formatter::__write_using_decimal_separators( __ctx.out(), __begin, - __first, - __last, + __prefix, + __end, __formatter::__determine_grouping(__size, __grouping), __np.thousands_sep(), __specs); } # endif auto __out_it = __ctx.out(); - if (__specs.__alignment_ != __format_spec::__alignment::__zero_padding) - __first = __begin; - else { + if (__specs.__alignment_ == __format_spec::__alignment::__zero_padding) { // __buf contains [sign][prefix]data // ^ location of __first // The zero padding is done like: // - Write [sign][prefix] // - Write data right aligned with '0' as fill character. - __out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it)); + __out_it = _VSTD::copy(__begin, __prefix, _VSTD::move(__out_it)); __specs.__alignment_ = __format_spec::__alignment::__right; __specs.__fill_ = _CharT('0'); - int32_t __size = __first - __begin; + int32_t __size = __prefix - __begin; __specs.__width_ -= _VSTD::min(__size, __specs.__width_); + __begin = __prefix; } if (__specs.__std_.__type_ != __format_spec::__type::__hexadecimal_upper_case) [[likely]] - return __formatter::__write(__first, __last, __ctx.out(), __specs); + return __formatter::__write(__begin, __end, __ctx.out(), __specs); + + return __formatter::__write_transformed(__begin, __end, __ctx.out(), __specs, __formatter::__hex_to_upper); +} + +/// Writes the base prefix in the buffer according to [tab:format.type.int] +template +char* __set_prefix(char* __begin, _Tp __value, bool __upper_case) { + *__begin = '0'; + if constexpr (_Base == 2) { + *(__begin + 1) = __upper_case ? 'B' : 'b'; + return __begin + 2; + } else if constexpr (_Base == 8) { + return __value == 0 ? __begin : __begin + 1; + } else if constexpr (_Base == 16) { + *(__begin + 1) = __upper_case ? 'X' : 'x'; + return __begin + 2; + } else { + static_assert(sizeof(_Tp) == 0, "unsupported base"); + } +} + +template +_LIBCPP_HIDE_FROM_ABI auto +__format_integer_base(_Tp __value, auto& __ctx, __format_spec::__parsed_specifications<_CharT> __specs, bool __negative) + -> decltype(__ctx.out()) { + array()> __array; + + char* __prefix = __formatter::__insert_sign(__array.begin(), __negative, __specs.__std_.__sign_); + if (__specs.__std_.__alternate_form_) { + bool __upper_case = + __specs.__std_.__type_ == __format_spec::__type::__binary_upper_case || + __specs.__std_.__type_ == __format_spec::__type::__hexadecimal_upper_case; + + __prefix = __set_prefix<_Base>(__prefix, __value, __upper_case); + } - return __formatter::__write_transformed(__first, __last, __ctx.out(), __specs, __formatter::__hex_to_upper); + char* __last = __to_buffer<_Base>(__prefix, __array.end(), __value); + return __formatter::__format_integer(__array.begin(), __prefix, __last, __ctx, __specs); +} + +template +char* __to_chars_base_10(char* __first, _Tp __value) { + if constexpr (sizeof(_Tp) <= sizeof(uint32_t)) + return __itoa::__base_10_u32(__first, static_cast(__value)); + else if constexpr (sizeof(_Tp) == sizeof(uint64_t)) + return __itoa::__base_10_u64(__first, static_cast(__value)); +# ifndef _LIBCPP_HAS_NO_INT128 + else if constexpr (sizeof(_Tp) == sizeof(__uint128_t)) { + if (__value <= std::numeric_limits::max()) + return __itoa::__base_10_u64(__first, static_cast(__value)); + else + return __itoa::__base_10_u128(__first, __value); + } +# endif + else + static_assert(sizeof(_Tp) == 0, "unsupported unsigned integral type"); +} + +template +_LIBCPP_HIDE_FROM_ABI auto __format_integer_base_10( + _Tp __value, auto& __ctx, __format_spec::__parsed_specifications<_CharT> __specs, bool __negative) + -> decltype(__ctx.out()) { + array()> __array; + + char* __prefix = __formatter::__insert_sign(__array.begin(), __negative, __specs.__std_.__sign_); + char* __last = __formatter::__to_chars_base_10(__prefix, __value); + + return __formatter::__format_integer(__array.begin(), __prefix, __last, __ctx, __specs); } template @@ -262,34 +324,21 @@ _Tp __value, auto& __ctx, __format_spec::__parsed_specifications<_CharT> __specs, bool __negative = false) -> decltype(__ctx.out()) { switch (__specs.__std_.__type_) { - case __format_spec::__type::__binary_lower_case: { - array()> __array; - return __formatter::__format_integer(__value, __ctx, __specs, __negative, __array.begin(), __array.end(), "0b", 2); - } - case __format_spec::__type::__binary_upper_case: { - array()> __array; - return __formatter::__format_integer(__value, __ctx, __specs, __negative, __array.begin(), __array.end(), "0B", 2); - } - case __format_spec::__type::__octal: { - // Octal is special; if __value == 0 there's no prefix. - array()> __array; - return __formatter::__format_integer( - __value, __ctx, __specs, __negative, __array.begin(), __array.end(), __value != 0 ? "0" : nullptr, 8); - } + case __format_spec::__type::__binary_lower_case: + case __format_spec::__type::__binary_upper_case: + return __formatter::__format_integer_base<2>(__value, __ctx, __specs, __negative); + + case __format_spec::__type::__octal: + return __formatter::__format_integer_base<8>(__value, __ctx, __specs, __negative); + case __format_spec::__type::__default: - case __format_spec::__type::__decimal: { - array()> __array; - return __formatter::__format_integer( - __value, __ctx, __specs, __negative, __array.begin(), __array.end(), nullptr, 10); - } - case __format_spec::__type::__hexadecimal_lower_case: { - array()> __array; - return __formatter::__format_integer(__value, __ctx, __specs, __negative, __array.begin(), __array.end(), "0x", 16); - } - case __format_spec::__type::__hexadecimal_upper_case: { - array()> __array; - return __formatter::__format_integer(__value, __ctx, __specs, __negative, __array.begin(), __array.end(), "0X", 16); - } + case __format_spec::__type::__decimal: + return __formatter::__format_integer_base_10(__value, __ctx, __specs, __negative); + + case __format_spec::__type::__hexadecimal_lower_case: + case __format_spec::__type::__hexadecimal_upper_case: + return __formatter::__format_integer_base<16>(__value, __ctx, __specs, __negative); + default: _LIBCPP_ASSERT(false, "The parse function should have validated the type"); __libcpp_unreachable();