diff --git a/include/fmt/format.h b/include/fmt/format.h index b143babd..90cfb904 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -174,9 +174,9 @@ FMT_END_NAMESPACE #endif // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of -// int_writer template instances to just one by only using the largest integer -// type. This results in a reduction in binary size but will cause a decrease in -// integer formatting performance. +// integer formatter template instantiations to just one by only using the +// largest integer type. This results in a reduction in binary size but will +// cause a decrease in integer formatting performance. #if !defined(FMT_REDUCE_INT_INSTANTIATIONS) # define FMT_REDUCE_INT_INSTANTIATIONS 0 #endif @@ -402,6 +402,10 @@ template constexpr Iterator& reserve(Iterator& it, size_t) { return it; } +template +using reserve_iterator = + remove_reference_t(), 0))>; + template constexpr T* to_pointer(OutputIt, size_t) { return nullptr; @@ -1634,18 +1638,17 @@ constexpr OutputIt write_padded(OutputIt out, template OutputIt write_bytes(OutputIt out, string_view bytes, const basic_format_specs& specs) { - using iterator = remove_reference_t; - return write_padded(out, specs, bytes.size(), [bytes](iterator it) { - const char* data = bytes.data(); - return copy_str(data, data + bytes.size(), it); - }); + return write_padded(out, specs, bytes.size(), + [bytes](reserve_iterator it) { + const char* data = bytes.data(); + return copy_str(data, data + bytes.size(), it); + }); } template constexpr OutputIt write_char(OutputIt out, Char value, const basic_format_specs& specs) { - using iterator = remove_reference_t; - return write_padded(out, specs, 1, [=](iterator it) { + return write_padded(out, specs, 1, [=](reserve_iterator it) { *it++ = value; return it; }); @@ -1681,16 +1684,154 @@ FMT_CONSTEXPR OutputIt write_int(OutputIt out, int num_digits, string_view prefix, const basic_format_specs& specs, F f) { auto data = write_int_data(num_digits, prefix, specs); - using iterator = remove_reference_t; - return write_padded(out, specs, data.size, [=](iterator it) { - if (prefix.size() != 0) - it = copy_str(prefix.begin(), prefix.end(), it); - it = detail::fill_n(it, data.padding, static_cast('0')); - return f(it); - }); + return write_padded( + out, specs, data.size, [=](reserve_iterator it) { + if (prefix.size() != 0) + it = copy_str(prefix.begin(), prefix.end(), it); + it = detail::fill_n(it, data.padding, static_cast('0')); + return f(it); + }); } -template +template +FMT_CONSTEXPR OutputIt write_dec(OutputIt out, UInt value, string_view prefix, + const basic_format_specs& specs) { + auto num_digits = count_digits(value); + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator it) { + return format_decimal(it, value, num_digits).end; + }); +} + +template +OutputIt write_int_localized(OutputIt out, UInt value, string_view prefix, + const basic_format_specs& specs, + locale_ref loc) { + const auto sep_size = 1; + std::string groups = grouping(loc); + if (groups.empty()) return write_dec(out, value, prefix, specs); + auto sep = thousands_sep(loc); + if (!sep) return write_dec(out, value, prefix, specs); + int num_digits = count_digits(value); + int size = num_digits, n = num_digits; + std::string::const_iterator group = groups.cbegin(); + while (group != groups.cend() && n > *group && *group > 0 && + *group != max_value()) { + size += sep_size; + n -= *group; + ++group; + } + if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back()); + char digits[40]; + format_decimal(digits, value, num_digits); + basic_memory_buffer buffer; + size += static_cast(prefix.size()); + const auto usize = to_unsigned(size); + buffer.resize(usize); + basic_string_view s(&sep, sep_size); + // Index of a decimal digit with the least significant digit having index 0. + int digit_index = 0; + group = groups.cbegin(); + auto p = buffer.data() + size - 1; + for (int i = num_digits - 1; i > 0; --i) { + *p-- = static_cast(digits[i]); + if (*group <= 0 || ++digit_index % *group != 0 || + *group == max_value()) + continue; + if (group + 1 != groups.cend()) { + digit_index = 0; + ++group; + } + std::uninitialized_copy(s.data(), s.data() + s.size(), + make_checked(p, s.size())); + p -= s.size(); + } + *p-- = static_cast(*digits); + if (prefix.size() != 0) *p = static_cast(prefix[0]); + auto data = buffer.data(); + return write_padded( + out, specs, usize, usize, [=](reserve_iterator it) { + return copy_str(data, data + size, it); + }); +} + +template +FMT_CONSTEXPR OutputIt write_int(OutputIt out, T value, + const basic_format_specs& specs, + locale_ref loc) { + using uint_type = uint32_or_64_or_128_t; + auto abs_value = static_cast(value); + + char prefix[4] = {}; + auto prefix_size = 0u; + if (is_negative(value)) { + prefix[0] = '-'; + ++prefix_size; + abs_value = 0 - abs_value; + } else if (specs.sign != sign::none && specs.sign != sign::minus) { + prefix[0] = specs.sign == sign::plus ? '+' : ' '; + ++prefix_size; + } + + switch (specs.type) { + case 0: + case 'd': + return specs.localized + ? write_int_localized(out, abs_value, {prefix, prefix_size}, + specs, loc) + : write_dec(out, abs_value, {prefix, prefix_size}, specs); + case 'x': + case 'X': { + if (specs.alt) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = specs.type; + } + bool upper = specs.type != 'x'; + int num_digits = count_digits<4>(abs_value); + return write_int(out, num_digits, {prefix, prefix_size}, specs, + [=](reserve_iterator it) { + return format_uint<4, Char>(it, abs_value, num_digits, + upper); + }); + } + case 'b': + case 'B': { + if (specs.alt) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = static_cast(specs.type); + } + int num_digits = count_digits<1>(abs_value); + return write_int(out, num_digits, {prefix, prefix_size}, specs, + [=](reserve_iterator it) { + return format_uint<1, Char>(it, abs_value, num_digits); + }); + } + case 'o': { + int num_digits = count_digits<3>(abs_value); + if (specs.alt && specs.precision <= num_digits && abs_value != 0) { + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + prefix[prefix_size++] = '0'; + } + return write_int(out, num_digits, {prefix, prefix_size}, specs, + [=](reserve_iterator it) { + return format_uint<3, Char>(it, abs_value, num_digits); + }); + } +#ifdef FMT_DEPRECATED_N_SPECIFIER + case 'n': + return write_int_localized(out, abs_value, {prefix, prefix_size}, specs, + loc); +#endif + case 'c': + return write_char(out, static_cast(abs_value), specs); + default: + FMT_THROW(format_error("invalid type specifier")); + } + return out; +} + +template FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view s, const basic_format_specs& specs) { auto data = s.data(); @@ -1700,154 +1841,12 @@ FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view s, auto width = specs.width != 0 ? compute_width(basic_string_view(data, size)) : 0; - using iterator = remove_reference_t; - return write_padded(out, specs, size, width, [=](iterator it) { - return copy_str(data, data + size, it); - }); + return write_padded(out, specs, size, width, + [=](reserve_iterator it) { + return copy_str(data, data + size, it); + }); } -// The handle_int_type_spec handler that writes an integer. -template struct int_writer { - OutputIt out; - locale_ref locale; - const basic_format_specs& specs; - UInt abs_value; - char prefix[4]; - unsigned prefix_size; - - using iterator = - remove_reference_t(), 0))>; - - constexpr string_view get_prefix() const { - return string_view(prefix, prefix_size); - } - - FMT_CONSTEXPR void write_dec() { - auto num_digits = count_digits(abs_value); - out = write_int( - out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) { - return format_decimal(it, abs_value, num_digits).end; - }); - } - - template - FMT_CONSTEXPR int_writer(OutputIt output, locale_ref loc, Int value, - const basic_format_specs& s) - : out(output), - locale(loc), - specs(s), - abs_value(static_cast(value)), - prefix_size(0) { - static_assert(std::is_same, UInt>::value, ""); - if (is_negative(value)) { - prefix[0] = '-'; - ++prefix_size; - abs_value = 0 - abs_value; - } else if (specs.sign != sign::none && specs.sign != sign::minus) { - prefix[0] = specs.sign == sign::plus ? '+' : ' '; - ++prefix_size; - } - } - - FMT_CONSTEXPR void on_dec() { - if (specs.localized) return on_num(); - write_dec(); - } - - FMT_CONSTEXPR void on_hex() { - if (specs.alt) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = specs.type; - } - int num_digits = count_digits<4>(abs_value); - out = write_int(out, num_digits, get_prefix(), specs, - [this, num_digits](iterator it) { - return format_uint<4, Char>(it, abs_value, num_digits, - specs.type != 'x'); - }); - } - - FMT_CONSTEXPR void on_bin() { - if (specs.alt) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = static_cast(specs.type); - } - int num_digits = count_digits<1>(abs_value); - out = write_int(out, num_digits, get_prefix(), specs, - [this, num_digits](iterator it) { - return format_uint<1, Char>(it, abs_value, num_digits); - }); - } - - FMT_CONSTEXPR void on_oct() { - int num_digits = count_digits<3>(abs_value); - if (specs.alt && specs.precision <= num_digits && abs_value != 0) { - // Octal prefix '0' is counted as a digit, so only add it if precision - // is not greater than the number of digits. - prefix[prefix_size++] = '0'; - } - out = write_int(out, num_digits, get_prefix(), specs, - [this, num_digits](iterator it) { - return format_uint<3, Char>(it, abs_value, num_digits); - }); - } - - enum { sep_size = 1 }; - - void on_num() { - std::string groups = grouping(locale); - if (groups.empty()) return write_dec(); - auto sep = thousands_sep(locale); - if (!sep) return write_dec(); - int num_digits = count_digits(abs_value); - int size = num_digits, n = num_digits; - std::string::const_iterator group = groups.cbegin(); - while (group != groups.cend() && n > *group && *group > 0 && - *group != max_value()) { - size += sep_size; - n -= *group; - ++group; - } - if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back()); - char digits[40]; - format_decimal(digits, abs_value, num_digits); - basic_memory_buffer buffer; - size += static_cast(prefix_size); - const auto usize = to_unsigned(size); - buffer.resize(usize); - basic_string_view s(&sep, sep_size); - // Index of a decimal digit with the least significant digit having index 0. - int digit_index = 0; - group = groups.cbegin(); - auto p = buffer.data() + size - 1; - for (int i = num_digits - 1; i > 0; --i) { - *p-- = static_cast(digits[i]); - if (*group <= 0 || ++digit_index % *group != 0 || - *group == max_value()) - continue; - if (group + 1 != groups.cend()) { - digit_index = 0; - ++group; - } - std::uninitialized_copy(s.data(), s.data() + s.size(), - make_checked(p, s.size())); - p -= s.size(); - } - *p-- = static_cast(*digits); - if (prefix_size != 0) *p = static_cast(prefix[0]); - auto data = buffer.data(); - out = write_padded( - out, specs, usize, usize, - [=](iterator it) { return copy_str(data, data + size, it); }); - } - - void on_chr() { out = write_char(out, static_cast(abs_value), specs); } - - FMT_NORETURN void on_error() { - FMT_THROW(format_error("invalid type specifier")); - } -}; - template OutputIt write_nonfinite(OutputIt out, bool isinf, const basic_format_specs& specs, @@ -1857,8 +1856,7 @@ OutputIt write_nonfinite(OutputIt out, bool isinf, constexpr size_t str_size = 3; auto sign = fspecs.sign; auto size = str_size + (sign ? 1 : 0); - using iterator = remove_reference_t; - return write_padded(out, specs, size, [=](iterator it) { + return write_padded(out, specs, size, [=](reserve_iterator it) { if (sign) *it++ = static_cast(data::signs[sign]); return copy_str(str, str + str_size, it); }); @@ -1938,7 +1936,7 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, static const Char zero = static_cast('0'); auto sign = fspecs.sign; size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); - using iterator = remove_reference_t; + using iterator = reserve_iterator; int output_exp = fp.exponent + significand_size - 1; auto use_exp_format = [=]() { @@ -2113,8 +2111,7 @@ OutputIt write_ptr(OutputIt out, UIntPtr value, const basic_format_specs* specs) { int num_digits = count_digits<4>(value); auto size = to_unsigned(num_digits) + size_t(2); - using iterator = remove_reference_t; - auto write = [=](iterator it) { + auto write = [=](reserve_iterator it) { *it++ = static_cast('0'); *it++ = static_cast('x'); return format_uint<4, Char>(it, value, num_digits); @@ -2265,17 +2262,6 @@ class arg_formatter_base { return detail::reserve(out_, n); } - using reserve_iterator = remove_reference_t(), 0))>; - - template - FMT_CONSTEXPR void write_int(T value, const format_specs& specs) { - using uint_type = uint32_or_64_or_128_t; - int_writer w(out_, locale_, value, specs); - handle_int_type_spec(specs.type, w); - out_ = w.out; - } - void write(char value) { auto&& it = reserve(1); *it++ = value; @@ -2300,9 +2286,10 @@ class arg_formatter_base { void write(const Ch* s, size_t size, const format_specs& specs) { auto width = specs.width != 0 ? compute_width(basic_string_view(s, size)) : 0; - out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) { - return copy_str(s, s + size, it); - }); + out_ = write_padded(out_, specs, size, width, + [=](reserve_iterator it) { + return copy_str(s, s + size, it); + }); } template @@ -2324,7 +2311,9 @@ class arg_formatter_base { FMT_CONSTEXPR void on_int() { // char is only formatted as int if there are specs. - formatter.write_int(static_cast(value), *formatter.specs_); + formatter.out_ = + detail::write_int(formatter.out_, static_cast(value), + *formatter.specs_, formatter.locale_); } FMT_CONSTEXPR void on_char() { if (formatter.specs_) @@ -2377,11 +2366,8 @@ class arg_formatter_base { template ::value)> FMT_CONSTEXPR FMT_INLINE iterator operator()(T value) { - if (specs_) - write_int(value, *specs_); - else - out_ = detail::write(out_, value); - return out_; + return out_ = specs_ ? detail::write_int(out_, value, *specs_, locale_) + : detail::write(out_, value); } FMT_CONSTEXPR iterator operator()(Char value) {