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:
parent
f5d4215b7c
commit
6f4f1b8d8f
@ -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>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user