diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 6d324f9c..71ee20f5 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -602,7 +602,7 @@ class bigint { int num_bigits() const { return static_cast(bigits_.size()) + exp_; } - bigint& operator<<=(int shift) { + FMT_NOINLINE bigint& operator<<=(int shift) { assert(shift >= 0); exp_ += shift / bigit_bits; shift %= bigit_bits; diff --git a/include/fmt/format.h b/include/fmt/format.h index 34b782cd..2c761094 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1413,31 +1413,47 @@ inline OutputIt write_padded(OutputIt out, return write_padded(out, specs, size, size, f); } -template struct write_int_params { +// Data for write_int that doesn't depend on output iterator type. It is used to +// avoid template code bloat. +template struct write_int_data { std::size_t size; std::size_t padding; Char fill; + + write_int_data(int num_digits, string_view prefix, + basic_format_specs& specs) + : size(prefix.size() + to_unsigned(num_digits)), + padding(0), + fill(specs.fill[0]) { + if (specs.align == align::numeric) { + auto width = to_unsigned(specs.width); + if (width > size) { + padding = width - size; + size = width; + } + } else if (specs.precision > num_digits) { + size = prefix.size() + to_unsigned(specs.precision); + padding = to_unsigned(specs.precision - num_digits); + fill = static_cast('0'); + } + if (specs.align == align::none) specs.align = align::right; + } }; -template -write_int_params make_write_int_params(int num_digits, string_view prefix, - basic_format_specs& specs) { - std::size_t size = prefix.size() + to_unsigned(num_digits); - Char fill = specs.fill[0]; - std::size_t padding = 0; - if (specs.align == align::numeric) { - auto width = to_unsigned(specs.width); - if (width > size) { - padding = width - size; - size = width; - } - } else if (specs.precision > num_digits) { - size = prefix.size() + to_unsigned(specs.precision); - padding = to_unsigned(specs.precision - num_digits); - fill = static_cast('0'); - } - if (specs.align == align::none) specs.align = align::right; - return {size, padding, fill}; +// Writes an integer in the format +// +// where are written by f(it). +template +OutputIt write_int(OutputIt out, int num_digits, string_view prefix, + 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 = std::fill_n(it, data.padding, data.fill); + return f(it); + }); } // This template provides operations for formatting and writing data into a @@ -1458,21 +1474,6 @@ template class basic_writer { return internal::reserve(out_, n); } - // Writes an integer in the format - // - // where are written by f(it). - template - void write_int(int num_digits, string_view prefix, format_specs specs, F f) { - auto params = make_write_int_params(num_digits, prefix, specs); - out_ = write_padded(out_, specs, params.size, [=](reserve_iterator it) { - if (prefix.size() != 0) - it = copy_str(prefix.begin(), prefix.end(), it); - it = std::fill_n(it, params.padding, params.fill); - f(it); - return it; - }); - } - // Writes a decimal integer. template void write_decimal(Int value) { auto abs_value = static_cast>(value); @@ -1486,22 +1487,23 @@ template class basic_writer { } // The handle_int_type_spec handler that writes an integer. - template struct int_writer { - using unsigned_type = uint32_or_64_or_128_t; - + template struct int_writer { basic_writer& writer; - const Specs& specs; - unsigned_type abs_value; + const basic_format_specs& specs; + UInt abs_value; char prefix[4]; unsigned prefix_size; string_view get_prefix() const { return string_view(prefix, prefix_size); } - int_writer(basic_writer& w, Int value, const Specs& s) + template + int_writer(basic_writer& w, Int value, + const basic_format_specs& s) : writer(w), specs(s), - abs_value(static_cast(value)), + abs_value(static_cast(value)), prefix_size(0) { + static_assert(std::is_same, UInt>::value, ""); if (is_negative(value)) { prefix[0] = '-'; ++prefix_size; @@ -1512,58 +1514,40 @@ template class basic_writer { } } - struct dec_writer { - unsigned_type abs_value; - int num_digits; - - template void operator()(It&& it) const { - it = internal::format_decimal(it, abs_value, num_digits); - } - }; - void on_dec() { - int num_digits = count_digits(abs_value); - writer.write_int(num_digits, get_prefix(), specs, - dec_writer{abs_value, num_digits}); + auto num_digits = count_digits(abs_value); + writer.out_ = internal::write_int(writer.out_, num_digits, get_prefix(), + specs, [=](reserve_iterator it) { + return format_decimal( + it, abs_value, num_digits); + }); } - struct hex_writer { - int_writer& self; - int num_digits; - - template void operator()(It&& it) const { - it = format_uint<4, char_type>(it, self.abs_value, num_digits, - self.specs.type != 'x'); - } - }; - void on_hex() { if (specs.alt) { prefix[prefix_size++] = '0'; prefix[prefix_size++] = specs.type; } int num_digits = count_digits<4>(abs_value); - writer.write_int(num_digits, get_prefix(), specs, - hex_writer{*this, num_digits}); + writer.out_ = internal::write_int(writer.out_, num_digits, get_prefix(), + specs, [=](reserve_iterator it) { + return format_uint<4, char_type>( + it, abs_value, num_digits, + specs.type != 'x'); + }); } - template struct bin_writer { - unsigned_type abs_value; - int num_digits; - - template void operator()(It&& it) const { - it = format_uint(it, abs_value, num_digits); - } - }; - 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); - writer.write_int(num_digits, get_prefix(), specs, - bin_writer<1>{abs_value, num_digits}); + writer.out_ = internal::write_int(writer.out_, num_digits, get_prefix(), + specs, [=](reserve_iterator it) { + return format_uint<1, char_type>( + it, abs_value, num_digits); + }); } void on_oct() { @@ -1573,25 +1557,28 @@ template class basic_writer { // is not greater than the number of digits. prefix[prefix_size++] = '0'; } - writer.write_int(num_digits, get_prefix(), specs, - bin_writer<3>{abs_value, num_digits}); + writer.out_ = internal::write_int(writer.out_, num_digits, get_prefix(), + specs, [=](reserve_iterator it) { + return format_uint<3, char_type>( + it, abs_value, num_digits); + }); } enum { sep_size = 1 }; struct num_writer { - unsigned_type abs_value; + UInt abs_value; int size; const std::string& groups; char_type sep; - template void operator()(It&& it) const { + template It operator()(It it) const { basic_string_view s(&sep, sep_size); // Index of a decimal digit with the least significant digit having // index 0. int digit_index = 0; std::string::const_iterator group = groups.cbegin(); - it = format_decimal( + return format_decimal( it, abs_value, size, [this, s, &group, &digit_index](char_type*& buffer) { if (*group <= 0 || ++digit_index % *group != 0 || @@ -1624,8 +1611,9 @@ template class basic_writer { } if (group == groups.cend()) size += sep_size * ((num_digits - 1) / groups.back()); - writer.write_int(size, get_prefix(), specs, - num_writer{abs_value, size, groups, sep}); + writer.out_ = + internal::write_int(writer.out_, size, get_prefix(), specs, + num_writer{abs_value, size, groups, sep}); } FMT_NORETURN void on_error() { @@ -1655,9 +1643,9 @@ template class basic_writer { void write(uint128_t value) { write_decimal(value); } #endif - template - void write_int(T value, const Spec& spec) { - handle_int_type_spec(spec.type, int_writer(*this, value, spec)); + template void write_int(T value, const format_specs& spec) { + using uint_type = uint32_or_64_or_128_t; + handle_int_type_spec(spec.type, int_writer(*this, value, spec)); } template ::value)>