Optimize bloat generated by write_padding

Reimplment write_padded function as a structure that removes align
and <typename F> template parameters in order to reduce the number of
template specializations generated. This results in a binary size
reduction.

Caveat, static_assert to detect invalid alignment has been removed as
the aligment parameter has been removed from the template arguments and
added to the constructor parameters.

Resolves #1774
This commit is contained in:
Khalil Estell 2020-07-15 16:58:07 -07:00
parent f5d4215b7c
commit 6f4f1b8d8f

View File

@ -1397,43 +1397,81 @@ FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t<Char>& fill) {
return it; return it;
} }
// Writes the output of f, padded according to format specifications in specs. // Helper class to handle writing padding to an an output function according to
// size: output size in code units. // a format specification.
// width: output display width in (terminal) column positions. // Typically Usage:
template <align::type align = align::left, typename OutputIt, typename Char, //
typename F> // return write_padding(iterator, specs, size)
inline OutputIt write_padded(OutputIt out, // .left()
const basic_format_specs<Char>& specs, size_t size, // .content([](iterator it) {
size_t width, const F& f) { // /* do something */
static_assert(align == align::left || align == align::right, ""); // })
unsigned spec_width = to_unsigned(specs.width); // .right()
size_t padding = spec_width > width ? spec_width - width : 0; // .get_base_iterator();
auto* shifts = align == align::left ? data::left_padding_shifts //
: data::right_padding_shifts; template <typename OutputIt, typename Char> struct write_padding {
size_t left_padding = padding >> shifts[specs.align]; // Writes the output of f, padded according to format specifications in specs.
auto it = reserve(out, size + padding * specs.fill.size()); //
it = fill(it, left_padding, specs.fill); // size: output size in code units.
it = f(it); // width: output display width in (terminal) column positions.
it = fill(it, padding - left_padding, specs.fill); write_padding(OutputIt out, const basic_format_specs<Char>& specs,
return base_iterator(out, it); size_t size, size_t width, align::type align = align::left)
} : out_(out), specs_(specs) {
unsigned spec_width = to_unsigned(specs.width);
size_t padding = spec_width > width ? spec_width - width : 0;
auto* shifts = align == align::left ? data::left_padding_shifts
: data::right_padding_shifts;
left_padding_ = padding >> shifts[specs.align];
right_padding_ = padding - left_padding_;
it_ = reserve(out, size + padding * specs.fill.size());
}
template <align::type align = align::left, typename OutputIt, typename Char, // Constructor that allows the width parameter to be omitted.
typename F> // Note that size becomes the new width.
inline OutputIt write_padded(OutputIt out, write_padding(OutputIt out, const basic_format_specs<Char>& specs,
const basic_format_specs<Char>& specs, size_t size, size_t size, align::type align = align::left)
const F& f) { : write_padding(out, specs, size, size, align) {}
return write_padded<align>(out, specs, size, size, f);
} write_padding& left() {
it_ = fill(it_, left_padding_, specs_.fill);
return *this;
}
// Writes the contents to the output using function f.
template <typename F> write_padding& content(const F& f) {
it_ = f(it_);
return *this;
}
write_padding& right() {
it_ = fill(it_, right_padding_, specs_.fill);
return *this;
}
OutputIt get_base_iterator() { return base_iterator(out_, it_); }
OutputIt out_;
const basic_format_specs<Char>& specs_;
using Iterator = decltype(reserve(out_, 0));
Iterator it_ = reserve(out_, 0);
size_t left_padding_ = 0;
size_t right_padding_ = 0;
};
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
OutputIt write_bytes(OutputIt out, string_view bytes, OutputIt write_bytes(OutputIt out, string_view bytes,
const basic_format_specs<Char>& specs) { const basic_format_specs<Char>& specs) {
using iterator = remove_reference_t<decltype(reserve(out, 0))>; using iterator = remove_reference_t<decltype(reserve(out, 0))>;
return write_padded(out, specs, bytes.size(), [bytes](iterator it) { return write_padding<OutputIt, Char>(out, specs, bytes.size())
const char* data = bytes.data(); .left()
return copy_str<Char>(data, data + bytes.size(), it); .content([bytes](iterator it) {
}); const char* data = bytes.data();
return copy_str<Char>(data, data + bytes.size(), it);
})
.right()
.get_base_iterator();
} }
// Data for write_int that doesn't depend on output iterator type. It is used to // Data for write_int that doesn't depend on output iterator type. It is used to
@ -1466,12 +1504,16 @@ OutputIt write_int(OutputIt out, int num_digits, string_view prefix,
const basic_format_specs<Char>& specs, F f) { const basic_format_specs<Char>& specs, F f) {
auto data = write_int_data<Char>(num_digits, prefix, specs); auto data = write_int_data<Char>(num_digits, prefix, specs);
using iterator = remove_reference_t<decltype(reserve(out, 0))>; using iterator = remove_reference_t<decltype(reserve(out, 0))>;
return write_padded<align::right>(out, specs, data.size, [=](iterator it) { return write_padding<OutputIt, Char>(out, specs, data.size, align::right)
if (prefix.size() != 0) .left()
it = copy_str<Char>(prefix.begin(), prefix.end(), it); .content([=](iterator it) {
it = std::fill_n(it, data.padding, static_cast<Char>('0')); if (prefix.size() != 0)
return f(it); it = copy_str<Char>(prefix.begin(), prefix.end(), it);
}); it = std::fill_n(it, data.padding, static_cast<Char>('0'));
return f(it);
})
.right()
.get_base_iterator();
} }
template <typename StrChar, typename Char, typename OutputIt> template <typename StrChar, typename Char, typename OutputIt>
@ -1485,9 +1527,12 @@ OutputIt write(OutputIt out, basic_string_view<StrChar> s,
? count_code_points(basic_string_view<StrChar>(data, size)) ? count_code_points(basic_string_view<StrChar>(data, size))
: 0; : 0;
using iterator = remove_reference_t<decltype(reserve(out, 0))>; using iterator = remove_reference_t<decltype(reserve(out, 0))>;
return write_padded(out, specs, size, width, [=](iterator it) { return write_padding<OutputIt, Char>(out, specs, size, width)
return copy_str<Char>(data, data + size, it); .left()
}); .content(
[=](iterator it) { return copy_str<Char>(data, data + size, it); })
.right()
.get_base_iterator();
} }
// The handle_int_type_spec handler that writes an integer. // The handle_int_type_spec handler that writes an integer.
@ -1630,10 +1675,14 @@ OutputIt write_nonfinite(OutputIt out, bool isinf,
auto sign = fspecs.sign; auto sign = fspecs.sign;
auto size = str_size + (sign ? 1 : 0); auto size = str_size + (sign ? 1 : 0);
using iterator = remove_reference_t<decltype(reserve(out, 0))>; using iterator = remove_reference_t<decltype(reserve(out, 0))>;
return write_padded(out, specs, size, [=](iterator it) { return write_padding<OutputIt, Char>(out, specs, size)
if (sign) *it++ = static_cast<Char>(data::signs[sign]); .left()
return copy_str<Char>(str, str + str_size, it); .content([=](iterator it) {
}); if (sign) *it++ = static_cast<Char>(data::signs[sign]);
return copy_str<Char>(str, str + str_size, it);
})
.right()
.get_base_iterator();
} }
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
@ -1682,7 +1731,11 @@ OutputIt write(OutputIt out, T value, basic_format_specs<Char> specs,
fspecs.locale ? decimal_point<Char>(loc) : static_cast<Char>('.'); fspecs.locale ? decimal_point<Char>(loc) : static_cast<Char>('.');
float_writer<Char> w(buffer.data(), static_cast<int>(buffer.size()), exp, float_writer<Char> w(buffer.data(), static_cast<int>(buffer.size()), exp,
fspecs, point); fspecs, point);
return write_padded<align::right>(out, specs, w.size(), w); return write_padding<OutputIt, Char>(out, specs, w.size(), align::right)
.left()
.content(w)
.right()
.get_base_iterator();
} }
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
@ -1714,10 +1767,14 @@ template <typename Char, typename OutputIt>
OutputIt write_char(OutputIt out, Char value, OutputIt write_char(OutputIt out, Char value,
const basic_format_specs<Char>& specs) { const basic_format_specs<Char>& specs) {
using iterator = remove_reference_t<decltype(reserve(out, 0))>; using iterator = remove_reference_t<decltype(reserve(out, 0))>;
return write_padded(out, specs, 1, [=](iterator it) { return write_padding<OutputIt, Char>(out, specs, 1)
*it++ = value; .left()
return it; .content([=](iterator it) {
}); *it++ = value;
return it;
})
.right()
.get_base_iterator();
} }
template <typename Char, typename OutputIt, typename UIntPtr> template <typename Char, typename OutputIt, typename UIntPtr>
@ -1731,7 +1788,11 @@ OutputIt write_ptr(OutputIt out, UIntPtr value,
*it++ = static_cast<Char>('x'); *it++ = static_cast<Char>('x');
return format_uint<4, Char>(it, value, num_digits); return format_uint<4, Char>(it, value, num_digits);
}; };
return specs ? write_padded<align::right>(out, *specs, size, write) return specs ? write_padding<OutputIt, Char>(out, *specs, size, align::right)
.left()
.content(write)
.right()
.get_base_iterator()
: base_iterator(out, write(reserve(out, size))); : base_iterator(out, write(reserve(out, size)));
} }
@ -1895,9 +1956,13 @@ class arg_formatter_base {
auto width = specs.width != 0 auto width = specs.width != 0
? count_code_points(basic_string_view<Ch>(s, size)) ? count_code_points(basic_string_view<Ch>(s, size))
: 0; : 0;
out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) { out_ = write_padding<OutputIt, Char>(out_, specs, size, width)
return copy_str<Char>(s, s + size, it); .left()
}); .content([=](reserve_iterator it) {
return copy_str<Char>(s, s + size, it);
})
.right()
.get_base_iterator();
} }
template <typename Ch> template <typename Ch>