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>
FMT_CONSTEXPR void handle_char_specs(
const basic_format_specs<Char> &specs, Handler &&handler) {
if (specs.type() && specs.type() != 'c') {
handler.on_int();
return;
}
if (specs.align() == ALIGN_NUMERIC || specs.flag(~0u) != 0)
const basic_format_specs<Char> *specs, Handler &&handler) {
if (!specs) return handler.on_char();
if (specs->type() && specs->type() != 'c') return handler.on_int();
if (specs->align() == ALIGN_NUMERIC || specs->flag(~0u) != 0)
handler.on_error("invalid format specifier for char");
handler.on_char();
}
@ -1470,7 +1468,7 @@ class arg_formatter_base {
private:
typedef basic_writer<Range> writer_type;
writer_type writer_;
format_specs &specs_;
format_specs *specs_;
struct char_writer {
char_type value;
@ -1479,11 +1477,14 @@ class arg_formatter_base {
};
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) {
format_specs specs = specs_;
format_specs specs = specs_ ? *specs_ : format_specs();
specs.flags_ = HASH_FLAG;
specs.type_ = 'x';
writer_.write_int(reinterpret_cast<uintptr_t>(p), specs);
@ -1491,22 +1492,24 @@ class arg_formatter_base {
protected:
writer_type &writer() { return writer_; }
format_specs &spec() { return specs_; }
format_specs *spec() { return specs_; }
iterator out() { return writer_.out(); }
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) {
if (!value)
FMT_THROW(format_error("string pointer is null"));
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:
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) {
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
// use std::is_same instead.
if (std::is_same<T, bool>::value) {
if (specs_.type_)
if (specs_ && specs_->type_)
return (*this)(value ? 1 : 0);
write(value != 0);
} else if (std::is_same<T, char_type>::value) {
internal::handle_char_specs(
specs_, char_spec_handler(*this, static_cast<char_type>(value)));
} else {
writer_.write_int(value, specs_);
specs_ ? writer_.write_int(value, *specs_) : writer_.write(value);
}
return out();
}
@ -1534,7 +1537,7 @@ class arg_formatter_base {
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, iterator>::type
operator()(T value) {
writer_.write_double(value, specs_);
writer_.write_double(value, specs_ ? *specs_ : format_specs());
return out();
}
@ -1545,7 +1548,12 @@ class arg_formatter_base {
char_spec_handler(arg_formatter_base& f, char_type 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); }
};
@ -1561,19 +1569,26 @@ class arg_formatter_base {
};
iterator operator()(const char_type *value) {
if (!specs_) return write(value), out();
internal::handle_cstring_type_spec(
specs_.type_, cstring_spec_handler(*this, value));
specs_->type_, cstring_spec_handler(*this, value));
return out();
}
iterator operator()(basic_string_view<char_type> value) {
internal::check_string_type_spec(specs_.type_, internal::error_handler());
writer_.write_str(value, specs_);
if (specs_) {
internal::check_string_type_spec(
specs_->type_, internal::error_handler());
writer_.write_str(value, *specs_);
} else {
writer_.write(value);
}
return out();
}
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);
return out();
}
@ -2346,7 +2361,7 @@ class arg_formatter:
*spec* contains format specifier information for standard argument types.
\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) {}
using base::operator();
@ -2500,7 +2515,7 @@ class basic_writer {
auto &&it = reserve((is_negative ? 1 : 0) + num_digits);
if (is_negative)
*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.
@ -2775,7 +2790,7 @@ class basic_writer {
void write(wstring_view value) {
internal::require_wchar<char_type>();
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>
@ -3176,7 +3191,7 @@ struct formatter<
break;
case internal::char_type:
handle_char_specs(
specs_,
&specs_,
internal::char_specs_checker<decltype(eh), decltype(type_spec)>(
type_spec, eh));
break;
@ -3211,7 +3226,7 @@ struct formatter<
specs_.precision_, specs_.precision_ref, ctx);
typedef output_range<typename FormatContext::iterator,
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));
}
@ -3272,7 +3287,7 @@ class dynamic_formatter {
checker.end_precision();
typedef output_range<typename FormatContext::iterator,
typename FormatContext::char_type> range;
visit(arg_formatter<range>(ctx, specs_),
visit(arg_formatter<range>(ctx, &specs_),
internal::make_arg<FormatContext>(val));
return ctx.out();
}
@ -3328,10 +3343,8 @@ struct format_handler : internal::error_handler {
void on_replacement_field(const Char *p) {
context.parse_context().advance_to(p);
if (visit(internal::custom_formatter<Char, Context>(context), arg))
return;
basic_format_specs<Char> specs;
context.advance_to(visit(ArgFormatter(context, specs), arg));
if (!visit(internal::custom_formatter<Char, Context>(context), arg))
context.advance_to(visit(ArgFormatter(context), arg));
}
iterator on_format_specs(iterator it) {
@ -3347,7 +3360,7 @@ struct format_handler : internal::error_handler {
if (*it != '}')
on_error("missing '}' in format string");
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;
}

View File

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

View File

@ -19,14 +19,15 @@ class custom_arg_formatter :
typedef fmt::back_insert_range<fmt::internal::buffer> range;
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) {}
using base::operator();
iterator operator()(double value) {
// 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;
return base::operator()(value);
}

View File

@ -1471,7 +1471,7 @@ class mock_arg_formatter:
typedef fmt::internal::arg_formatter_base<buffer_range> base;
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) {
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> {
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) {