diff --git a/include/fmt/core.h b/include/fmt/core.h index 312de478..939da9fd 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -200,7 +200,8 @@ class basic_buffer { std::size_t capacity_; protected: - basic_buffer() FMT_NOEXCEPT : ptr_(0), size_(0), capacity_(0) {} + basic_buffer(T *p = 0, std::size_t size = 0, std::size_t capacity = 0) + FMT_NOEXCEPT: ptr_(p), size_(size), capacity_(capacity) {} /** Sets the buffer data and capacity. */ void set(T *data, std::size_t capacity) FMT_NOEXCEPT { @@ -266,12 +267,12 @@ class basic_buffer { const T &operator[](std::size_t index) const { return ptr_[index]; } }; -using buffer = internal::basic_buffer; -using wbuffer = internal::basic_buffer; +using buffer = basic_buffer; +using wbuffer = basic_buffer; +// A container-backed buffer. template -class container_buffer - : public internal::basic_buffer { +class container_buffer : public basic_buffer { private: Container &container_; @@ -282,7 +283,9 @@ class container_buffer } public: - explicit container_buffer(Container &c) : container_(c) {} + explicit container_buffer(Container &c) + : basic_buffer(&c[0], c.size(), c.size()), + container_(c) {} }; // A helper function to suppress bogus "conditional expression is constant" @@ -771,7 +774,6 @@ class context_base { private: basic_parse_context parse_context_; - Range range_; iterator out_; basic_format_args args_; @@ -779,9 +781,9 @@ class context_base { using char_type = typename Range::value_type; using format_arg = basic_arg; - context_base(Range range, basic_string_view format_str, + context_base(Range r, basic_string_view format_str, basic_format_args args) - : parse_context_(format_str), range_(range), args_(args) {} + : parse_context_(format_str), out_(r.begin()), args_(args) {} basic_format_args args() const { return args_; } @@ -811,23 +813,33 @@ class context_base { void on_error(const char *message) { parse_context_.on_error(message); } - Range range() { return range_; } - // Returns an iterator to the beginning of the output range. - auto begin() { return std::back_inserter(range_.container()); } + auto begin() { return out_; } // Advances the begin iterator to ``it``. void advance_to(iterator it) { out_ = it; } }; -// A range that can grow dynamically. +// Extracts a reference to the container from back_insert_iterator. template -class dynamic_range { +inline Container &get_container(std::back_insert_iterator it) { + using iterator = std::back_insert_iterator; + struct accessor: iterator { + accessor(iterator it) : iterator(it) {} + using iterator::container; + }; + return *accessor(it).container; +} +} // namespace internal + +// A range where begin() returns back_insert_iterator. +template +class back_insert_range { private: Container &container_; public: - using iterator = decltype(container_.begin()); + using iterator = std::back_insert_iterator; using value_type = typename Container::value_type; struct sentinel { @@ -835,20 +847,11 @@ class dynamic_range { friend bool operator!=(iterator, sentinel) { return false; } }; - dynamic_range(Container &c) : container_(c) {} + back_insert_range(Container &c) : container_(c) {} - iterator begin() const { return container_.begin(); } + iterator begin() const { return std::back_inserter(container_); } sentinel end() const { return sentinel(); } - - friend iterator grow(dynamic_range r, size_t n) { - auto size = r.container_.size(); - r.container_.resize(size + n); - return r.container_.begin() + size; - } - - Container &container() const { return container_; } }; -} // namespace internal // Formatting context. template @@ -895,8 +898,8 @@ class basic_context : format_arg get_arg(basic_string_view name); }; -using context = basic_context>; -using wcontext = basic_context>; +using context = basic_context>; +using wcontext = basic_context>; template class arg_store { @@ -1016,7 +1019,7 @@ struct named_arg_base { // Serialized value. mutable char data[sizeof(basic_arg)]; - named_arg_base(basic_string_view name) : name(name) {} + named_arg_base(basic_string_view nm) : name(nm) {} template basic_arg deserialize() const { @@ -1082,18 +1085,21 @@ void vformat_to(internal::buffer &buf, string_view format_str, void vformat_to(internal::wbuffer &buf, wstring_view format_str, wformat_args args); -/** - Formats a string and writes the output to out. - */ template -void vformat_to(std::back_insert_iterator out, - string_view format_str, format_args args) { - using iterator = std::back_insert_iterator; - struct container_accessor : iterator { - container_accessor(iterator it) : iterator(it) {} - using iterator::container; - } accessor(out); - internal::container_buffer buf(*accessor.container); +struct is_contiguous : std::false_type {}; + +template +struct is_contiguous> : std::true_type {}; + +template +struct is_contiguous> : std::true_type {}; + +/** Formats a string and writes the output to ``out``. */ +template +typename std::enable_if::value>::type + vformat_to(std::back_insert_iterator out, + string_view format_str, format_args args) { + internal::container_buffer buf(internal::get_container(out)); vformat_to(buf, format_str, args); } diff --git a/include/fmt/format.cc b/include/fmt/format.cc index cc166d83..4d527225 100644 --- a/include/fmt/format.cc +++ b/include/fmt/format.cc @@ -326,7 +326,7 @@ FMT_FUNC void windows_error::init( } FMT_FUNC void internal::format_windows_error( - buffer &out, int error_code, string_view message) FMT_NOEXCEPT { + internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT { FMT_TRY { wmemory_buffer buf; buf.resize(INLINE_BUFFER_SIZE); diff --git a/include/fmt/format.h b/include/fmt/format.h index fc2d212a..34fd307c 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -512,6 +512,17 @@ class char_traits : public basic_char_traits { const wchar_t *format, unsigned width, int precision, T value); }; +template +inline auto reserve(std::back_insert_iterator &it, std::size_t n) { + Container &c = internal::get_container(it); + std::size_t size = c.size(); + c.resize(size + n); + return &c[size]; +} + +template +inline Iterator &reserve(Iterator &it, std::size_t) { return it; } + template class null_terminating_iterator; @@ -818,7 +829,7 @@ inline void format_decimal(Char *buffer, UInt value, unsigned num_digits, template inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) { - return format_decimal(buffer, value, num_digits, no_thousands_sep()); + format_decimal(buffer, value, num_digits, no_thousands_sep()); } #ifndef _WIN32 @@ -864,7 +875,7 @@ class utf16_to_utf8 { FMT_API int convert(wstring_view s); }; -FMT_API void format_windows_error(fmt::buffer &out, int error_code, +FMT_API void format_windows_error(fmt::internal::buffer &out, int error_code, fmt::string_view message) FMT_NOEXCEPT; #endif @@ -1218,7 +1229,7 @@ class arg_formatter_base { FMT_DISALLOW_COPY_AND_ASSIGN(arg_formatter_base); void write_char(char_type value) { - writer_.write_padded(1, specs_, [value](auto &it) { + writer_.write_padded(1, specs_, [value](auto &&it) { *it++ = internal::char_traits::cast(value); }); } @@ -1970,12 +1981,12 @@ class arg_formatter: public internal::arg_formatter_base { /** \rst Constructs an argument formatter object. - *r* is an output range, *ctx* is a reference to the formatting context, + *ctx* is a reference to the formatting context, *spec* contains format specifier information for standard argument types. \endrst */ - arg_formatter(Range r, basic_context &ctx, format_specs &spec) - : base(r, spec), ctx_(ctx) {} + arg_formatter(basic_context &ctx, format_specs &spec) + : base(Range(internal::get_container(ctx.begin())), spec), ctx_(ctx) {} using base::operator(); @@ -2048,21 +2059,8 @@ FMT_API void format_system_error(fmt::internal::buffer &out, int error_code, fmt::string_view message) FMT_NOEXCEPT; /** - \rst This template provides operations for formatting and writing data into a character range. - - You can use one of the following typedefs for common character types: - - +---------+-----------------------+ - | Type | Definition | - +=========+=======================+ - | writer | basic_writer | - +---------+-----------------------+ - | wwriter | basic_writer | - +---------+-----------------------+ - - \endrst */ template class basic_writer { @@ -2071,10 +2069,8 @@ class basic_writer { using format_specs = basic_format_specs; private: + // Output iterator. using iterator = decltype(std::declval().begin()); - - // Output range. - Range range_; iterator out_; std::unique_ptr locale_; @@ -2090,9 +2086,10 @@ class basic_writer { static char_type *get(char_type *p) { return p; } #endif - // Attempts to reserve space for n characters in the output range. - void reserve(std::size_t n) { - out_ = grow(range_, n); + // Attempts to reserve space for n extra characters in the output range. + // Returns a pointer to the reserved range or a reference to out_. + auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) { + return internal::reserve(out_, n); } // Writes a value in the format @@ -2123,7 +2120,7 @@ class basic_writer { align_spec as = spec; if (spec.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT; - write_padded(size, as, [prefix, fill, padding, f](auto &it) { + write_padded(size, as, [prefix, fill, padding, f](auto &&it) { if (prefix.size() != 0) it = std::uninitialized_copy_n(prefix.data(), prefix.size(), it); it = std::uninitialized_fill_n(it, padding, fill); @@ -2140,10 +2137,10 @@ class basic_writer { if (is_negative) abs_value = 0 - abs_value; unsigned num_digits = internal::count_digits(abs_value); - reserve((is_negative ? 1 : 0) + num_digits); + auto &&it = reserve((is_negative ? 1 : 0) + num_digits); if (is_negative) - *out_++ = '-'; - internal::format_decimal(out_, abs_value, num_digits); + *it++ = '-'; + internal::format_decimal(it, abs_value, num_digits); } // The handle_int_type_spec handler that writes an integer. @@ -2185,7 +2182,7 @@ class basic_writer { void on_dec() { unsigned num_digits = internal::count_digits(abs_value); writer.write_int(num_digits, get_prefix(), spec, - [this, num_digits](auto &it) { + [this, num_digits](auto &&it) { internal::format_decimal(it, abs_value, num_digits); it += num_digits; }); @@ -2198,7 +2195,7 @@ class basic_writer { } unsigned num_digits = count_digits<4>(); writer.write_int(num_digits, get_prefix(), spec, - [this, num_digits](auto &it) { + [this, num_digits](auto &&it) { it += num_digits; auto out = it; auto n = abs_value; @@ -2217,7 +2214,7 @@ class basic_writer { } unsigned num_digits = count_digits<1>(); writer.write_int(num_digits, get_prefix(), spec, - [this, num_digits](auto &it) { + [this, num_digits](auto &&it) { it += num_digits; auto out = it; auto n = abs_value; @@ -2236,7 +2233,7 @@ class basic_writer { prefix[prefix_size++] = '0'; } writer.write_int(num_digits, get_prefix(), spec, - [this, num_digits](auto &it) { + [this, num_digits](auto &&it) { it += num_digits; auto out = it; auto n = abs_value; @@ -2251,7 +2248,7 @@ class basic_writer { char_type sep = internal::thousands_sep(writer.locale_.get()); static constexpr unsigned SEP_SIZE = 1; unsigned size = num_digits + SEP_SIZE * ((num_digits - 1) / 3); - writer.write_int(size, get_prefix(), spec, [this, size, sep](auto &it) { + writer.write_int(size, get_prefix(), spec, [this, size, sep](auto &&it) { basic_string_view s(&sep, SEP_SIZE); internal::format_decimal(it, abs_value, size, internal::add_thousands_sep(s)); @@ -2278,7 +2275,7 @@ class basic_writer { // Writes a formatted string. template void write_str(const Char *s, std::size_t size, const align_spec &spec) { - write_padded(size, spec, [s, size](iterator &it) { + write_padded(size, spec, [s, size](auto &&it) { it = std::uninitialized_copy_n(s, size, it); }); } @@ -2300,7 +2297,7 @@ class basic_writer { public: /** Constructs a ``basic_writer`` object. */ - explicit basic_writer(Range out): range_(out), out_(out.begin()) {} + explicit basic_writer(Range out): out_(out.begin()) {} void write(int value) { write_decimal(value); @@ -2341,14 +2338,12 @@ class basic_writer { /** Writes a character to the buffer. */ void write(char value) { - reserve(1); - *out_++ = value; + *reserve(1) = value; } void write(wchar_t value) { internal::require_wchar(); - reserve(1); - *out_++ = value; + *reserve(1) = value; } /** @@ -2357,14 +2352,14 @@ class basic_writer { \endrst */ void write(string_view value) { - reserve(value.size()); - out_ = std::uninitialized_copy(value.begin(), value.end(), out_); + auto &&it = reserve(value.size()); + it = std::uninitialized_copy(value.begin(), value.end(), it); } void write(wstring_view value) { internal::require_wchar(); - reserve(value.size()); - out_ = std::uninitialized_copy(value.begin(), value.end(), out_); + auto &&it = reserve(value.size()); + it = std::uninitialized_copy(value.begin(), value.end(), it); } template @@ -2378,24 +2373,22 @@ template void basic_writer::write_padded( std::size_t size, const align_spec &spec, F f) { unsigned width = spec.width(); - if (width <= size) { - reserve(size); - return f(out_); - } - reserve(width); + if (width <= size) + return f(reserve(size)); + auto &&it = reserve(width); char_type fill = internal::char_traits::cast(spec.fill()); std::size_t padding = width - size; if (spec.align() == ALIGN_RIGHT) { - out_ = std::uninitialized_fill_n(out_, padding, fill); - f(out_); + it = std::uninitialized_fill_n(it, padding, fill); + f(it); } else if (spec.align() == ALIGN_CENTER) { std::size_t left_padding = padding / 2; - out_ = std::uninitialized_fill_n(out_, left_padding, fill); - f(out_); - out_ = std::uninitialized_fill_n(out_, padding - left_padding, fill); + it = std::uninitialized_fill_n(it, left_padding, fill); + f(it); + it = std::uninitialized_fill_n(it, padding - left_padding, fill); } else { - f(out_); - out_ = std::uninitialized_fill_n(out_, padding, fill); + f(it); + it = std::uninitialized_fill_n(it, padding, fill); } } @@ -2472,7 +2465,7 @@ void basic_writer::write_double(T value, const format_specs &spec) { auto write_inf_or_nan = [this, &spec, sign](const char *str) { enum {SIZE = 3}; // This is an enum to workaround a bug in MSVC. - write_padded(SIZE + (sign ? 1 : 0), spec, [sign, str](auto &it) { + write_padded(SIZE + (sign ? 1 : 0), spec, [sign, str](auto &&it) { if (sign) *it++ = sign; it = std::uninitialized_copy_n(str, static_cast(SIZE), it); @@ -2544,8 +2537,7 @@ void basic_writer::write_double(T value, const format_specs &spec) { align_spec as = spec; if (spec.align() == ALIGN_NUMERIC) { if (sign) { - reserve(1); - *out_++ = sign; + *reserve(1) = sign; sign = 0; --as.width_; } @@ -2556,7 +2548,7 @@ void basic_writer::write_double(T value, const format_specs &spec) { if (sign) ++n; } - write_padded(n, as, [n, sign, &buffer](auto &it) mutable { + write_padded(n, as, [n, sign, &buffer](auto &&it) mutable { if (sign) { *it++ = sign; --n; @@ -2565,8 +2557,8 @@ void basic_writer::write_double(T value, const format_specs &spec) { }); } -using writer = basic_writer>; -using wwriter = basic_writer>; +using writer = basic_writer>; +using wwriter = basic_writer>; // Reports a system error without throwing an exception. // Can be used to report errors from destructors. @@ -2791,7 +2783,7 @@ struct formatter< internal::handle_dynamic_spec( specs_.precision_, specs_.precision_ref, ctx); using range = typename FormatContext::range_type; - visit(arg_formatter(ctx.range(), ctx, specs_), + visit(arg_formatter(ctx, specs_), internal::make_arg(val)); return ctx.begin(); } @@ -2858,8 +2850,7 @@ struct dynamic_formatter { } if (specs_.precision_ != -1) checker.end_precision(); - using range = typename FormatContext::range_type; - visit(arg_formatter(ctx.range(), ctx, specs_), + visit(arg_formatter(ctx, specs_), internal::make_arg(val)); return ctx.begin(); } @@ -2897,11 +2888,14 @@ void do_vformat_to(typename ArgFormatter::range out, struct handler : internal::error_handler { handler(range r, basic_string_view str, basic_format_args format_args) - : out(r), context(r, str, format_args) {} + : context(r, str, format_args) {} void on_text(iterator begin, iterator end) { + auto out = context.begin(); size_t size = end - begin; - std::uninitialized_copy_n(begin, size, grow(out, size)); + auto &&it = internal::reserve(out, size); + it = std::uninitialized_copy_n(begin, size, it); + context.advance_to(out); } void on_arg_id() { arg = context.next_arg(); } @@ -2919,7 +2913,7 @@ void do_vformat_to(typename ArgFormatter::range out, if (visit(custom_formatter(context), arg)) return; basic_format_specs specs; - visit(ArgFormatter(out, context, specs), arg); + visit(ArgFormatter(context, specs), arg); } iterator on_format_specs(iterator it) { @@ -2936,11 +2930,10 @@ void do_vformat_to(typename ArgFormatter::range out, if (*it != '}') on_error("missing '}' in format string"); parse_ctx.advance_to(pointer_from(it)); - visit(ArgFormatter(out, context, specs), arg); + visit(ArgFormatter(context, specs), arg); return it; } - range out; Context context; basic_arg arg; }; @@ -3004,13 +2997,13 @@ std::basic_string to_string(const basic_memory_buffer &buffer) { inline void vformat_to(internal::buffer &buf, string_view format_str, format_args args) { - using range = internal::dynamic_range; + using range = back_insert_range; do_vformat_to>(buf, format_str, args); } inline void vformat_to(internal::wbuffer &buf, wstring_view format_str, wformat_args args) { - using range = internal::dynamic_range; + using range = back_insert_range; do_vformat_to>(buf, format_str, args); } @@ -3028,8 +3021,7 @@ inline void format_to(wmemory_buffer &buf, wstring_view format_str, template inline void format_to(std::back_insert_iterator out, - string_view format_str, - const Args & ... args) { + string_view format_str, const Args & ... args) { vformat_to(out, format_str, make_args(args...)); } diff --git a/include/fmt/printf.h b/include/fmt/printf.h index eefdb7bb..c1a06b34 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -289,7 +289,7 @@ struct printf_formatter { template auto format(const T &value, FormatContext &ctx) -> decltype(ctx.begin()) { - internal::format_value(ctx.range().container(), value); + internal::format_value(internal::get_container(ctx.begin()), value); return ctx.begin(); } }; @@ -336,7 +336,6 @@ class basic_printf_context : : base(range, format_str, args) {} using base::parse_context; - using base::range; using base::begin; using base::advance_to; @@ -419,7 +418,7 @@ unsigned basic_printf_context::parse_header( template void basic_printf_context::format() { - auto &buffer = this->range().container(); + auto &buffer = internal::get_container(this->begin()); auto start = iterator(this->parse_context()); auto it = start; using internal::pointer_from; @@ -528,7 +527,7 @@ void printf(internal::basic_buffer &buf, basic_string_view format, } template -using printf_context = basic_printf_context>; +using printf_context = basic_printf_context>; using printf_args = basic_format_args>; diff --git a/include/fmt/time.h b/include/fmt/time.h index 26de2666..70f28938 100644 --- a/include/fmt/time.h +++ b/include/fmt/time.h @@ -31,7 +31,7 @@ struct formatter { } auto format(const std::tm &tm, context &ctx) -> decltype(ctx.begin()) { - internal::buffer &buf = ctx.range().container(); + internal::buffer &buf = internal::get_container(ctx.begin()); std::size_t start = buf.size(); for (;;) { std::size_t size = buf.capacity() - start; diff --git a/test/custom-formatter-test.cc b/test/custom-formatter-test.cc index 25b7622c..45408dcd 100644 --- a/test/custom-formatter-test.cc +++ b/test/custom-formatter-test.cc @@ -15,15 +15,13 @@ using fmt::printf_arg_formatter; // A custom argument formatter that doesn't print `-` for floating-point values // rounded to 0. class CustomArgFormatter : - public fmt::arg_formatter< - fmt::internal::dynamic_range> { + public fmt::arg_formatter> { public: - using range = fmt::internal::dynamic_range; + using range = fmt::back_insert_range; using base = fmt::arg_formatter; - CustomArgFormatter(range r, fmt::basic_context &ctx, - fmt::format_specs &s) - : base(r, ctx, s) {} + CustomArgFormatter(fmt::basic_context &ctx, fmt::format_specs &s) + : base(ctx, s) {} using base::operator(); diff --git a/test/format-test.cc b/test/format-test.cc index e83c3af8..94bdc642 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -88,7 +88,7 @@ void std_format(long double value, std::wstring &result) { template ::testing::AssertionResult check_write(const T &value, const char *type) { fmt::basic_memory_buffer buffer; - using range = fmt::internal::dynamic_range>; + using range = fmt::back_insert_range>; fmt::basic_writer writer(buffer); writer.write(value); std::basic_string actual = to_string(buffer); @@ -1481,7 +1481,7 @@ TEST(FormatTest, Enum) { EXPECT_EQ("0", fmt::format("{}", A)); } -using buffer_range = fmt::internal::dynamic_range; +using buffer_range = fmt::back_insert_range; class mock_arg_formatter : public fmt::internal::arg_formatter_base { @@ -1492,8 +1492,8 @@ class mock_arg_formatter : using base = fmt::internal::arg_formatter_base; using range = buffer_range; - mock_arg_formatter(buffer_range r, fmt::context &, fmt::format_specs &s) - : base(r, s) { + mock_arg_formatter(fmt::context &ctx, fmt::format_specs &s) + : base(fmt::internal::get_container(ctx.begin()), s) { EXPECT_CALL(*this, call(42)); } @@ -1905,6 +1905,6 @@ TEST(FormatTest, FormatStringErrors) { int, int); } -TEST(StringTest, ToString) { +TEST(FormatTest, ToString) { EXPECT_EQ("42", fmt::to_string(42)); } diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 5dca8109..41323330 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -59,17 +59,15 @@ TEST(OStreamTest, Enum) { } struct test_arg_formatter : fmt::arg_formatter { - using base = fmt::arg_formatter; - test_arg_formatter(fmt::internal::buffer &buf, fmt::context &ctx, - fmt::format_specs &s) - : base(buf, ctx, s) {} + test_arg_formatter(fmt::context &ctx, fmt::format_specs &s) + : fmt::arg_formatter(ctx, s) {} }; TEST(OStreamTest, CustomArg) { fmt::memory_buffer buffer; fmt::context ctx(buffer, "", fmt::format_args()); fmt::format_specs spec; - test_arg_formatter af(buffer, ctx, spec); + test_arg_formatter af(ctx, spec); visit(af, fmt::internal::make_arg(TestEnum())); EXPECT_EQ("TestEnum", std::string(buffer.data(), buffer.size())); } diff --git a/test/util-test.cc b/test/util-test.cc index 04f3916e..d3127199 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -81,7 +81,7 @@ struct formatter { return ctx.begin(); } - using range = fmt::internal::dynamic_range>; + using range = fmt::back_insert_range>; auto format(Test, basic_context &ctx) -> decltype(ctx.begin()) { const Char *test = "test"; @@ -521,7 +521,7 @@ VISIT_TYPE(float, double); #define CHECK_ARG_(Char, expected, value) { \ testing::StrictMock> visitor; \ EXPECT_CALL(visitor, visit(expected)); \ - using range = fmt::internal::dynamic_range>; \ + using range = fmt::back_insert_range>; \ fmt::visit(visitor, make_arg>(value)); \ }