Optimize default formatting

This commit is contained in:
Victor Zverovich 2018-09-12 07:34:22 -07:00
parent c8a8464f7d
commit 7110b46076
5 changed files with 58 additions and 44 deletions

View File

@ -1338,12 +1338,10 @@ FMT_CONSTEXPR void handle_float_type_spec(Char spec, Handler &&handler) {
template <typename Char, typename Handler> template <typename Char, typename Handler>
FMT_CONSTEXPR void handle_char_specs( FMT_CONSTEXPR void handle_char_specs(
const basic_format_specs<Char> &specs, Handler &&handler) { const basic_format_specs<Char> *specs, Handler &&handler) {
if (specs.type() && specs.type() != 'c') { if (!specs) return handler.on_char();
handler.on_int(); if (specs->type() && specs->type() != 'c') return handler.on_int();
return; if (specs->align() == ALIGN_NUMERIC || specs->flag(~0u) != 0)
}
if (specs.align() == ALIGN_NUMERIC || specs.flag(~0u) != 0)
handler.on_error("invalid format specifier for char"); handler.on_error("invalid format specifier for char");
handler.on_char(); handler.on_char();
} }
@ -1470,7 +1468,7 @@ class arg_formatter_base {
private: private:
typedef basic_writer<Range> writer_type; typedef basic_writer<Range> writer_type;
writer_type writer_; writer_type writer_;
format_specs &specs_; format_specs *specs_;
struct char_writer { struct char_writer {
char_type value; char_type value;
@ -1479,11 +1477,14 @@ class arg_formatter_base {
}; };
void write_char(char_type value) { void write_char(char_type value) {
writer_.write_padded(1, specs_, char_writer{value}); if (specs_)
writer_.write_padded(1, *specs_, char_writer{value});
else
writer_.write(value);
} }
void write_pointer(const void *p) { void write_pointer(const void *p) {
format_specs specs = specs_; format_specs specs = specs_ ? *specs_ : format_specs();
specs.flags_ = HASH_FLAG; specs.flags_ = HASH_FLAG;
specs.type_ = 'x'; specs.type_ = 'x';
writer_.write_int(reinterpret_cast<uintptr_t>(p), specs); writer_.write_int(reinterpret_cast<uintptr_t>(p), specs);
@ -1491,22 +1492,24 @@ class arg_formatter_base {
protected: protected:
writer_type &writer() { return writer_; } writer_type &writer() { return writer_; }
format_specs &spec() { return specs_; } format_specs *spec() { return specs_; }
iterator out() { return writer_.out(); } iterator out() { return writer_.out(); }
void write(bool value) { void write(bool value) {
writer_.write_str(string_view(value ? "true" : "false"), specs_); string_view sv(value ? "true" : "false");
specs_ ? writer_.write_str(sv, *specs_) : writer_.write(sv);
} }
void write(const char_type *value) { void write(const char_type *value) {
if (!value) if (!value)
FMT_THROW(format_error("string pointer is null")); FMT_THROW(format_error("string pointer is null"));
auto length = std::char_traits<char_type>::length(value); auto length = std::char_traits<char_type>::length(value);
writer_.write_str(basic_string_view<char_type>(value, length), specs_); basic_string_view<char_type> sv(value, length);
specs_ ? writer_.write_str(sv, *specs_) : writer_.write(sv);
} }
public: public:
arg_formatter_base(Range r, format_specs &s): writer_(r), specs_(s) {} arg_formatter_base(Range r, format_specs *s): writer_(r), specs_(s) {}
iterator operator()(monostate) { iterator operator()(monostate) {
FMT_ASSERT(false, "invalid argument type"); FMT_ASSERT(false, "invalid argument type");
@ -1519,14 +1522,14 @@ class arg_formatter_base {
// MSVC2013 fails to compile separate overloads for bool and char_type so // MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead. // use std::is_same instead.
if (std::is_same<T, bool>::value) { if (std::is_same<T, bool>::value) {
if (specs_.type_) if (specs_ && specs_->type_)
return (*this)(value ? 1 : 0); return (*this)(value ? 1 : 0);
write(value != 0); write(value != 0);
} else if (std::is_same<T, char_type>::value) { } else if (std::is_same<T, char_type>::value) {
internal::handle_char_specs( internal::handle_char_specs(
specs_, char_spec_handler(*this, static_cast<char_type>(value))); specs_, char_spec_handler(*this, static_cast<char_type>(value)));
} else { } else {
writer_.write_int(value, specs_); specs_ ? writer_.write_int(value, *specs_) : writer_.write(value);
} }
return out(); return out();
} }
@ -1534,7 +1537,7 @@ class arg_formatter_base {
template <typename T> template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, iterator>::type typename std::enable_if<std::is_floating_point<T>::value, iterator>::type
operator()(T value) { operator()(T value) {
writer_.write_double(value, specs_); writer_.write_double(value, specs_ ? *specs_ : format_specs());
return out(); return out();
} }
@ -1545,7 +1548,12 @@ class arg_formatter_base {
char_spec_handler(arg_formatter_base& f, char_type val) char_spec_handler(arg_formatter_base& f, char_type val)
: formatter(f), value(val) {} : formatter(f), value(val) {}
void on_int() { formatter.writer_.write_int(value, formatter.specs_); } void on_int() {
if (formatter.specs_)
formatter.writer_.write_int(value, *formatter.specs_);
else
formatter.writer_.write(value);
}
void on_char() { formatter.write_char(value); } void on_char() { formatter.write_char(value); }
}; };
@ -1561,19 +1569,26 @@ class arg_formatter_base {
}; };
iterator operator()(const char_type *value) { iterator operator()(const char_type *value) {
if (!specs_) return write(value), out();
internal::handle_cstring_type_spec( internal::handle_cstring_type_spec(
specs_.type_, cstring_spec_handler(*this, value)); specs_->type_, cstring_spec_handler(*this, value));
return out(); return out();
} }
iterator operator()(basic_string_view<char_type> value) { iterator operator()(basic_string_view<char_type> value) {
internal::check_string_type_spec(specs_.type_, internal::error_handler()); if (specs_) {
writer_.write_str(value, specs_); internal::check_string_type_spec(
specs_->type_, internal::error_handler());
writer_.write_str(value, *specs_);
} else {
writer_.write(value);
}
return out(); return out();
} }
iterator operator()(const void *value) { iterator operator()(const void *value) {
check_pointer_type_spec(specs_.type_, internal::error_handler()); if (specs_)
check_pointer_type_spec(specs_->type_, internal::error_handler());
write_pointer(value); write_pointer(value);
return out(); return out();
} }
@ -2346,7 +2361,7 @@ class arg_formatter:
*spec* contains format specifier information for standard argument types. *spec* contains format specifier information for standard argument types.
\endrst \endrst
*/ */
arg_formatter(context_type &ctx, format_specs &spec) explicit arg_formatter(context_type &ctx, format_specs *spec = {})
: base(Range(ctx.out()), spec), ctx_(ctx) {} : base(Range(ctx.out()), spec), ctx_(ctx) {}
using base::operator(); using base::operator();
@ -2500,7 +2515,7 @@ class basic_writer {
auto &&it = reserve((is_negative ? 1 : 0) + num_digits); auto &&it = reserve((is_negative ? 1 : 0) + num_digits);
if (is_negative) if (is_negative)
*it++ = '-'; *it++ = '-';
internal::format_decimal(it, abs_value, num_digits); it = internal::format_decimal(it, abs_value, num_digits);
} }
// The handle_int_type_spec handler that writes an integer. // The handle_int_type_spec handler that writes an integer.
@ -2775,7 +2790,7 @@ class basic_writer {
void write(wstring_view value) { void write(wstring_view value) {
internal::require_wchar<char_type>(); internal::require_wchar<char_type>();
auto &&it = reserve(value.size()); auto &&it = reserve(value.size());
it = std::uninitialized_copy(value.begin(), value.end(), it); it = std::copy(value.begin(), value.end(), it);
} }
template <typename... FormatSpecs> template <typename... FormatSpecs>
@ -3176,7 +3191,7 @@ struct formatter<
break; break;
case internal::char_type: case internal::char_type:
handle_char_specs( handle_char_specs(
specs_, &specs_,
internal::char_specs_checker<decltype(eh), decltype(type_spec)>( internal::char_specs_checker<decltype(eh), decltype(type_spec)>(
type_spec, eh)); type_spec, eh));
break; break;
@ -3211,7 +3226,7 @@ struct formatter<
specs_.precision_, specs_.precision_ref, ctx); specs_.precision_, specs_.precision_ref, ctx);
typedef output_range<typename FormatContext::iterator, typedef output_range<typename FormatContext::iterator,
typename FormatContext::char_type> range_type; typename FormatContext::char_type> range_type;
return visit(arg_formatter<range_type>(ctx, specs_), return visit(arg_formatter<range_type>(ctx, &specs_),
internal::make_arg<FormatContext>(val)); internal::make_arg<FormatContext>(val));
} }
@ -3272,7 +3287,7 @@ class dynamic_formatter {
checker.end_precision(); checker.end_precision();
typedef output_range<typename FormatContext::iterator, typedef output_range<typename FormatContext::iterator,
typename FormatContext::char_type> range; typename FormatContext::char_type> range;
visit(arg_formatter<range>(ctx, specs_), visit(arg_formatter<range>(ctx, &specs_),
internal::make_arg<FormatContext>(val)); internal::make_arg<FormatContext>(val));
return ctx.out(); return ctx.out();
} }
@ -3328,10 +3343,8 @@ struct format_handler : internal::error_handler {
void on_replacement_field(const Char *p) { void on_replacement_field(const Char *p) {
context.parse_context().advance_to(p); context.parse_context().advance_to(p);
if (visit(internal::custom_formatter<Char, Context>(context), arg)) if (!visit(internal::custom_formatter<Char, Context>(context), arg))
return; context.advance_to(visit(ArgFormatter(context), arg));
basic_format_specs<Char> specs;
context.advance_to(visit(ArgFormatter(context, specs), arg));
} }
iterator on_format_specs(iterator it) { iterator on_format_specs(iterator it) {
@ -3347,7 +3360,7 @@ struct format_handler : internal::error_handler {
if (*it != '}') if (*it != '}')
on_error("missing '}' in format string"); on_error("missing '}' in format string");
parse_ctx.advance_to(pointer_from(it)); parse_ctx.advance_to(pointer_from(it));
context.advance_to(visit(ArgFormatter(context, specs), arg)); context.advance_to(visit(ArgFormatter(context, &specs), arg));
return it; return it;
} }

View File

@ -222,12 +222,12 @@ class printf_arg_formatter:
context_type &context_; context_type &context_;
void write_null_pointer(char) { void write_null_pointer(char) {
this->spec().type_ = 0; this->spec()->type_ = 0;
this->write("(nil)"); this->write("(nil)");
} }
void write_null_pointer(wchar_t) { void write_null_pointer(wchar_t) {
this->spec().type_ = 0; this->spec()->type_ = 0;
this->write(L"(nil)"); this->write(L"(nil)");
} }
@ -243,7 +243,7 @@ class printf_arg_formatter:
*/ */
printf_arg_formatter(internal::basic_buffer<char_type> &buffer, printf_arg_formatter(internal::basic_buffer<char_type> &buffer,
format_specs &spec, context_type &ctx) format_specs &spec, context_type &ctx)
: base(back_insert_range<internal::basic_buffer<char_type>>(buffer), spec), : base(back_insert_range<internal::basic_buffer<char_type>>(buffer), &spec),
context_(ctx) {} context_(ctx) {}
template <typename T> template <typename T>
@ -252,13 +252,13 @@ class printf_arg_formatter:
// MSVC2013 fails to compile separate overloads for bool and char_type so // MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead. // use std::is_same instead.
if (std::is_same<T, bool>::value) { if (std::is_same<T, bool>::value) {
format_specs &fmt_spec = this->spec(); format_specs &fmt_spec = *this->spec();
if (fmt_spec.type_ != 's') if (fmt_spec.type_ != 's')
return base::operator()(value ? 1 : 0); return base::operator()(value ? 1 : 0);
fmt_spec.type_ = 0; fmt_spec.type_ = 0;
this->write(value != 0); this->write(value != 0);
} else if (std::is_same<T, char_type>::value) { } else if (std::is_same<T, char_type>::value) {
format_specs &fmt_spec = this->spec(); format_specs &fmt_spec = *this->spec();
if (fmt_spec.type_ && fmt_spec.type_ != 'c') if (fmt_spec.type_ && fmt_spec.type_ != 'c')
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
fmt_spec.flags_ = 0; fmt_spec.flags_ = 0;
@ -280,7 +280,7 @@ class printf_arg_formatter:
iterator operator()(const char *value) { iterator operator()(const char *value) {
if (value) if (value)
base::operator()(value); base::operator()(value);
else if (this->spec().type_ == 'p') else if (this->spec()->type_ == 'p')
write_null_pointer(char_type()); write_null_pointer(char_type());
else else
this->write("(null)"); this->write("(null)");
@ -291,7 +291,7 @@ class printf_arg_formatter:
iterator operator()(const wchar_t *value) { iterator operator()(const wchar_t *value) {
if (value) if (value)
base::operator()(value); base::operator()(value);
else if (this->spec().type_ == 'p') else if (this->spec()->type_ == 'p')
write_null_pointer(char_type()); write_null_pointer(char_type());
else else
this->write(L"(null)"); this->write(L"(null)");
@ -310,7 +310,7 @@ class printf_arg_formatter:
iterator operator()(const void *value) { iterator operator()(const void *value) {
if (value) if (value)
return base::operator()(value); return base::operator()(value);
this->spec().type_ = 0; this->spec()->type_ = 0;
write_null_pointer(char_type()); write_null_pointer(char_type());
return this->out(); return this->out();
} }

View File

@ -19,14 +19,15 @@ class custom_arg_formatter :
typedef fmt::back_insert_range<fmt::internal::buffer> range; typedef fmt::back_insert_range<fmt::internal::buffer> range;
typedef fmt::arg_formatter<range> base; typedef fmt::arg_formatter<range> base;
custom_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s) custom_arg_formatter(
fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL)
: base(ctx, s) {} : base(ctx, s) {}
using base::operator(); using base::operator();
iterator operator()(double value) { iterator operator()(double value) {
// Comparing a float to 0.0 is safe // Comparing a float to 0.0 is safe
if (round(value * pow(10, spec().precision())) == 0.0) if (round(value * pow(10, spec()->precision())) == 0.0)
value = 0; value = 0;
return base::operator()(value); return base::operator()(value);
} }

View File

@ -1471,7 +1471,7 @@ class mock_arg_formatter:
typedef fmt::internal::arg_formatter_base<buffer_range> base; typedef fmt::internal::arg_formatter_base<buffer_range> base;
typedef buffer_range range; typedef buffer_range range;
mock_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s) mock_arg_formatter(fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL)
: base(fmt::internal::get_container(ctx.out()), s) { : base(fmt::internal::get_container(ctx.out()), s) {
EXPECT_CALL(*this, call(42)); EXPECT_CALL(*this, call(42));
} }

View File

@ -50,7 +50,7 @@ typedef fmt::back_insert_range<fmt::internal::buffer> range;
struct test_arg_formatter: fmt::arg_formatter<range> { struct test_arg_formatter: fmt::arg_formatter<range> {
test_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s) test_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s)
: fmt::arg_formatter<range>(ctx, s) {} : fmt::arg_formatter<range>(ctx, &s) {}
}; };
TEST(OStreamTest, CustomArg) { TEST(OStreamTest, CustomArg) {