From c3d6c5fc4c8e012375a30449a458b01884cfea91 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 30 Dec 2017 07:42:56 -0800 Subject: [PATCH] Replace buffer with range --- include/fmt/core.h | 20 +- include/fmt/format.h | 634 +++++++++++++++++-------------------------- 2 files changed, 259 insertions(+), 395 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 31fa3365..3174b173 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -52,8 +52,7 @@ // Check if exceptions are disabled. #if defined(__GNUC__) && !defined(__EXCEPTIONS) # define FMT_EXCEPTIONS 0 -#endif -#if FMT_MSC_VER && !_HAS_EXCEPTIONS +#elif FMT_MSC_VER && !_HAS_EXCEPTIONS # define FMT_EXCEPTIONS 0 #endif #ifndef FMT_EXCEPTIONS @@ -68,7 +67,7 @@ #ifndef FMT_NOEXCEPT # if FMT_EXCEPTIONS # if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900 + FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900 # define FMT_NOEXCEPT noexcept # else # define FMT_NOEXCEPT throw() @@ -565,12 +564,9 @@ class basic_arg { public: class handle { public: - explicit handle(internal::custom_value custom) - : custom_(custom) {} + explicit handle(internal::custom_value custom): custom_(custom) {} - void format(basic_buffer &buf, Context &ctx) { - custom_.format(buf, custom_.value, ctx); - } + void format(Context &ctx) { custom_.format(custom_.value, ctx); } private: internal::custom_value custom_; @@ -681,7 +677,7 @@ class arg_map { using char_type = typename Context::char_type; struct arg { - fmt::basic_string_view name; + basic_string_view name; basic_arg value; }; @@ -699,7 +695,7 @@ class arg_map { ~arg_map() { delete [] map_; } const basic_arg - *find(const fmt::basic_string_view &name) const { + *find(const basic_string_view &name) const { // The list is unsorted, so just return the first matching name. for (auto it = map_, end = map_ + size_; it != end; ++it) { if (it->name == name) @@ -924,8 +920,8 @@ enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; FMT_API void vprint_colored(Color c, string_view format, format_args args); /** - Formats a string and prints it to stdout using ANSI escape sequences - to specify color (experimental). + Formats a string and prints it to stdout using ANSI escape sequences to + specify color (experimental). Example: print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23); */ diff --git a/include/fmt/format.h b/include/fmt/format.h index d72e933a..0f015ad9 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -342,6 +342,9 @@ class basic_buffer { virtual ~basic_buffer() {} + T *begin() FMT_NOEXCEPT { return ptr_; } + T *end() FMT_NOEXCEPT { return ptr_ + capacity_; } + /** Returns the size of this buffer. */ std::size_t size() const FMT_NOEXCEPT { return size_; } @@ -1307,26 +1310,9 @@ class arg_formatter_base { FMT_DISALLOW_COPY_AND_ASSIGN(arg_formatter_base); void write_char(Char value) { - using pointer_type = typename writer_type::pointer_type; - Char fill = internal::char_traits::cast(specs_.fill()); - pointer_type out = pointer_type(); - const unsigned character_width = 1; - if (specs_.width_ > character_width) { - out = writer_.grow_buffer(specs_.width_); - if (specs_.align_ == ALIGN_RIGHT) { - std::uninitialized_fill_n(out, specs_.width_ - character_width, fill); - out += specs_.width_ - character_width; - } else if (specs_.align_ == ALIGN_CENTER) { - out = writer_.fill_padding(out, specs_.width_, - internal::const_check(character_width), fill); - } else { - std::uninitialized_fill_n(out + character_width, - specs_.width_ - character_width, fill); - } - } else { - out = writer_.grow_buffer(character_width); - } - *out = internal::char_traits::cast(value); + writer_.write_padded(1, specs_, [value](auto &it) { + *it++ = internal::char_traits::cast(value); + }); } void write_pointer(const void *p) { @@ -2159,7 +2145,7 @@ FMT_API void format_system_error(fmt::buffer &out, int error_code, /** \rst This template provides operations for formatting and writing data into a - character buffer. + character range. You can use one of the following typedefs for common character types: @@ -2173,79 +2159,202 @@ FMT_API void format_system_error(fmt::buffer &out, int error_code, \endrst */ -template +template class basic_writer { public: - using char_type = typename Buffer::value_type; - typedef basic_format_specs format_specs; + using char_type = typename Range::value_type; + using format_specs = basic_format_specs; private: - // Output buffer. - Buffer &buffer_; + using iterator = decltype(std::declval().begin()); + + // Output range. + iterator begin_; + decltype(std::declval().end()) end_; + std::unique_ptr locale_; FMT_DISALLOW_COPY_AND_ASSIGN(basic_writer); #if FMT_SECURE_SCL - typedef stdext::checked_array_iterator pointer_type; + using pointer_type = stdext::checked_array_iterator; // Returns pointer value. static char_type *get(pointer_type p) { return p.base(); } #else - typedef char_type *pointer_type; + using pointer_type = char_type*; static char_type *get(char_type *p) { return p; } #endif - // Fills the padding around the content and returns the pointer to the - // content area. - static pointer_type fill_padding(pointer_type buffer, - unsigned total_size, std::size_t content_size, wchar_t fill); + template + void do_reserve(std::size_t, Category) {} - // Grows the buffer by n characters and returns a pointer to the newly - // allocated area. - pointer_type grow_buffer(std::size_t n) { - std::size_t size = buffer_.size(); - buffer_.resize(size + n); - return internal::make_ptr(&buffer_[size], n); + void do_reserve(std::size_t n, std::random_access_iterator_tag) { + (void)(begin_ + n); } - // Writes an unsigned decimal integer. - template - char_type *write_unsigned_decimal(UInt value, unsigned prefix_size = 0) { - unsigned num_digits = internal::count_digits(value); - char_type *ptr = get(grow_buffer(prefix_size + num_digits)); - internal::format_decimal(ptr + prefix_size, value, num_digits); - return ptr; + // Attempts to reserve space for n characters in the output range. + void reserve(std::size_t n) { + using category = typename std::iterator_traits::iterator_category; + do_reserve(n, category()); + } + + // Writes a value in the format + // + // where is written by f(it). + template + void write_padded(std::size_t size, const align_spec &spec, F f); + + // Writes an integer in the format + // + // where are written by f(it). + template + void write_int(unsigned num_digits, string_view prefix, + const Spec &spec, F f) { + unsigned size = prefix.size() + num_digits; + auto fill = spec.fill(); + unsigned padding = 0; + if (spec.align() == ALIGN_NUMERIC) { + if (spec.width() > size) { + padding = spec.width() - size; + size = spec.width(); + } + } else if (spec.precision() > num_digits) { + size = prefix.size() + spec.precision(); + padding = spec.precision() - num_digits; + fill = '0'; + } + write_padded(size, spec, [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); + f(it); + }); } // Writes a decimal integer. template void write_decimal(Int value) { - typedef typename internal::int_traits::main_type main_type; + using main_type = typename internal::int_traits::main_type; main_type abs_value = static_cast(value); - if (internal::is_negative(value)) { + bool is_negative = internal::is_negative(value); + if (is_negative) abs_value = 0 - abs_value; - *write_unsigned_decimal(abs_value, 1) = '-'; - } else { - write_unsigned_decimal(abs_value, 0); + unsigned num_digits = internal::count_digits(abs_value); + reserve((is_negative ? 1 : 0) + num_digits); + if (is_negative) + *begin_++ = '-'; + internal::format_decimal(begin_, abs_value, num_digits); + } + + // The handle_int_type_spec handler that writes an integer. + template + struct int_writer { + using unsigned_type = typename internal::int_traits::main_type; + + basic_writer &writer; + const Spec &spec; + unsigned_type abs_value; + char prefix[4]; + unsigned prefix_size = 0; + + string_view get_prefix() const { return string_view(prefix, prefix_size); } + + // Counts the number of digits in abs_value. BITS = log2(radix). + template + unsigned count_digits() const { + unsigned_type n = abs_value; + unsigned num_digits = 0; + do { + ++num_digits; + } while ((n >>= BITS) != 0); + return num_digits; } - } - // Prepare a buffer for integer formatting. - pointer_type prepare_int_buffer(unsigned num_digits, - const empty_spec &, const char *prefix, unsigned prefix_size) { - unsigned size = prefix_size + num_digits; - pointer_type p = grow_buffer(size); - std::uninitialized_copy(prefix, prefix + prefix_size, p); - return p + size - 1; - } + int_writer(basic_writer &w, Int value, const Spec &s) + : writer(w), spec(s), abs_value(static_cast(value)) { + if (internal::is_negative(value)) { + prefix[0] = '-'; + ++prefix_size; + abs_value = 0 - abs_value; + } else if (spec.flag(SIGN_FLAG)) { + prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; + ++prefix_size; + } + } - template - pointer_type prepare_int_buffer(unsigned num_digits, - const Spec &spec, const char *prefix, unsigned prefix_size); + void on_dec() { + unsigned num_digits = internal::count_digits(abs_value); + writer.write_int(num_digits, get_prefix(), spec, + [this, num_digits](auto &it) { + internal::format_decimal(it, abs_value, 0); + it += num_digits; + }); + } + + void on_hex() { + if (spec.flag(HASH_FLAG)) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = spec.type(); + } + writer.write_int(count_digits<4>(), get_prefix(), spec, [this](auto &it) { + auto n = abs_value; + const char *digits = spec.type() == 'x' ? + "0123456789abcdef" : "0123456789ABCDEF"; + do { + *it-- = digits[n & 0xf]; + } while ((n >>= 4) != 0); + }); + } + + void on_bin() { + if (spec.flag(HASH_FLAG)) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = spec.type(); + } + writer.write_int(count_digits<1>(), get_prefix(), spec, [this](auto &it) { + auto n = abs_value; + do { + *it-- = static_cast('0' + (n & 1)); + } while ((n >>= 1) != 0); + }); + } + + void on_oct() { + if (spec.flag(HASH_FLAG)) + prefix[prefix_size++] = '0'; + writer.write_int(count_digits<3>(), get_prefix(), spec, [this](auto &it) { + auto n = abs_value; + do { + *it-- = static_cast('0' + (n & 7)); + } while ((n >>= 3) != 0); + }); + } + + void on_num() { + unsigned num_digits = internal::count_digits(abs_value); + char_type thousands_sep = + internal::thousands_sep(writer.locale_.get()); + basic_string_view sep(&thousands_sep, 1); + unsigned size = static_cast( + num_digits + sep.size() * ((num_digits - 1) / 3)); + writer.write_int(size, get_prefix(), spec, [this, size, sep](auto &it) { + internal::format_decimal(it, abs_value, 0, + internal::add_thousands_sep(sep)); + it += size; + }); + } + + void on_error() { + FMT_THROW(format_error("invalid type specifier")); + } + }; // Writes a formatted integer. template - void write_int(T value, const Spec& spec); + void write_int(T value, const Spec &spec) { + internal::handle_int_type_spec(spec.type(), + int_writer(*this, value, spec)); + } // Formats a floating-point number (double or long double). template @@ -2253,8 +2362,11 @@ class basic_writer { // Writes a formatted string. template - pointer_type write_str( - const Char *s, std::size_t size, const align_spec &spec); + void write_str(const Char *s, std::size_t size, const align_spec &spec) { + write_padded(size, spec, [s, size](iterator &it) { + it = std::uninitialized_copy_n(s, size, it); + }); + } template void write_str(basic_string_view str, const format_specs &spec); @@ -2272,48 +2384,8 @@ class basic_writer { friend class internal::arg_formatter_base; public: - /** - Constructs a ``basic_writer`` object. - */ - explicit basic_writer(Buffer &b) : buffer_(b) {} - - /** - \rst - Destroys the ``basic_writer`` object. - \endrst - */ - virtual ~basic_writer() {} - - /** - Returns the total number of characters written. - */ - std::size_t size() const { return buffer_.size(); } - - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - const char_type *data() const FMT_NOEXCEPT { return &buffer_[0]; } - - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - const char_type *c_str() const { - std::size_t size = buffer_.size(); - buffer_.reserve(size + 1); - buffer_[size] = '\0'; - return &buffer_[0]; - } - - /** - \rst - Returns the content of the output buffer as an `std::string`. - \endrst - */ - std::basic_string str() const { - return std::basic_string(&buffer_[0], buffer_.size()); - } + /** Constructs a ``basic_writer`` object. */ + explicit basic_writer(Range &r) : begin_(r.begin()), end_(r.end()) {} void write(int value) { write_decimal(value); @@ -2350,16 +2422,14 @@ class basic_writer { write_double(value, format_specs()); } - /** - Writes a character to the buffer. - */ + /** Writes a character to the buffer. */ void write(char value) { - buffer_.push_back(value); + *begin_++ = value; } void write(wchar_t value) { internal::require_wchar(); - buffer_.push_back(value); + *begin_++ = value; } /** @@ -2368,251 +2438,66 @@ class basic_writer { \endrst */ void write(string_view value) { - const char *str = value.data(); - buffer_.append(str, str + value.size()); + begin_ = std::uninitialized_copy(value.begin(), value.end(), begin_); } - void write(basic_string_view value) { + void write(wstring_view value) { internal::require_wchar(); - const wchar_t *str = value.data(); - buffer_.append(str, str + value.size()); + begin_ = std::uninitialized_copy(value.begin(), value.end(), begin_); } template void write(basic_string_view str, FormatSpecs... specs) { write_str(str, format_specs(specs...)); } - - void clear() FMT_NOEXCEPT { buffer_.resize(0); } - - Buffer &buffer() FMT_NOEXCEPT { return buffer_; } }; -template -template -typename basic_writer::pointer_type basic_writer::write_str( - const Char *s, std::size_t size, const align_spec &spec) { - pointer_type out = pointer_type(); - if (spec.width() > size) { - out = grow_buffer(spec.width()); - char_type fill = internal::char_traits::cast(spec.fill()); - if (spec.align() == ALIGN_RIGHT) { - std::uninitialized_fill_n(out, spec.width() - size, fill); - out += spec.width() - size; - } else if (spec.align() == ALIGN_CENTER) { - out = fill_padding(out, spec.width(), size, fill); - } else { - std::uninitialized_fill_n(out + size, spec.width() - size, fill); - } - } else { - out = grow_buffer(size); +template +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(begin_); + } + reserve(width); + char_type fill = internal::char_traits::cast(spec.fill()); + std::size_t padding = width - size; + if (spec.align() == ALIGN_RIGHT) { + begin_ = std::uninitialized_fill_n(begin_, padding, fill); + f(begin_); + } else if (spec.align() == ALIGN_CENTER) { + std::size_t left_padding = padding / 2; + begin_ = std::uninitialized_fill_n(begin_, left_padding, fill); + f(begin_); + begin_ = std::uninitialized_fill_n(begin_, padding - left_padding, fill); + } else { + f(begin_); + std::uninitialized_fill_n(begin_, padding, fill); } - std::uninitialized_copy(s, s + size, out); - return out; } -template + +template template -void basic_writer::write_str( +void basic_writer::write_str( basic_string_view s, const format_specs &spec) { // Check if Char is convertible to char_type. internal::char_traits::convert(Char()); - const Char *str_value = s.data(); - std::size_t str_size = s.size(); - if (str_size == 0 && !str_value) + const Char *data = s.data(); + std::size_t size = s.size(); + if (size == 0 && !data) FMT_THROW(format_error("string pointer is null")); std::size_t precision = static_cast(spec.precision_); - if (spec.precision_ >= 0 && precision < str_size) - str_size = precision; - write_str(str_value, str_size, spec); + if (spec.precision_ >= 0 && precision < size) + size = precision; + write_str(data, size, spec); } -template -typename basic_writer::pointer_type basic_writer::fill_padding( - pointer_type buffer, unsigned total_size, - std::size_t content_size, wchar_t fill) { - std::size_t padding = total_size - content_size; - std::size_t left_padding = padding / 2; - char_type fill_char = internal::char_traits::cast(fill); - std::uninitialized_fill_n(buffer, left_padding, fill_char); - buffer += left_padding; - pointer_type content = buffer; - std::uninitialized_fill_n(buffer + content_size, - padding - left_padding, fill_char); - return content; -} - -template -template -typename basic_writer::pointer_type - basic_writer::prepare_int_buffer( - unsigned num_digits, const Spec &spec, - const char *prefix, unsigned prefix_size) { - unsigned width = spec.width(); - alignment align = spec.align(); - char_type fill = internal::char_traits::cast(spec.fill()); - if (spec.precision() > static_cast(num_digits)) { - // Octal prefix '0' is counted as a digit, so ignore it if precision - // is specified. - if (prefix_size > 0 && prefix[prefix_size - 1] == '0') - --prefix_size; - unsigned number_size = - prefix_size + internal::to_unsigned(spec.precision()); - align_spec subspec(number_size, '0', ALIGN_NUMERIC); - if (number_size >= width) - return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); - buffer_.reserve(width); - unsigned fill_size = width - number_size; - if (align != ALIGN_LEFT) { - pointer_type p = grow_buffer(fill_size); - std::uninitialized_fill(p, p + fill_size, fill); - } - pointer_type result = prepare_int_buffer( - num_digits, subspec, prefix, prefix_size); - if (align == ALIGN_LEFT) { - pointer_type p = grow_buffer(fill_size); - std::uninitialized_fill(p, p + fill_size, fill); - } - return result; - } - unsigned size = prefix_size + num_digits; - if (width <= size) { - pointer_type p = grow_buffer(size); - std::uninitialized_copy(prefix, prefix + prefix_size, p); - return p + size - 1; - } - pointer_type p = grow_buffer(width); - pointer_type end = p + width; - if (align == ALIGN_LEFT) { - std::uninitialized_copy(prefix, prefix + prefix_size, p); - p += size; - std::uninitialized_fill(p, end, fill); - } else if (align == ALIGN_CENTER) { - p = fill_padding(p, width, size, fill); - std::uninitialized_copy(prefix, prefix + prefix_size, p); - p += size; - } else { - if (align == ALIGN_NUMERIC) { - if (prefix_size != 0) { - p = std::uninitialized_copy(prefix, prefix + prefix_size, p); - size -= prefix_size; - } - } else { - std::uninitialized_copy(prefix, prefix + prefix_size, end - size); - } - std::uninitialized_fill(p, end - size, fill); - p = end; - } - return p - 1; -} - -template -template -void basic_writer::write_int(T value, const Spec& spec) { - using unsigned_type = typename internal::int_traits::main_type; - struct spec_handler { - basic_writer &writer; - const Spec& spec; - unsigned prefix_size = 0; - unsigned_type abs_value; - char prefix[4] = ""; - - spec_handler(basic_writer &w, T val, const Spec& s) - : writer(w), spec(s), abs_value(static_cast(val)) { - if (internal::is_negative(val)) { - prefix[0] = '-'; - ++prefix_size; - abs_value = 0 - abs_value; - } else if (spec.flag(SIGN_FLAG)) { - prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; - ++prefix_size; - } - } - - void on_dec() { - unsigned num_digits = internal::count_digits(abs_value); - pointer_type p = - writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size) + 1; - internal::format_decimal(get(p), abs_value, 0); - } - - void on_hex() { - unsigned_type n = abs_value; - if (spec.flag(HASH_FLAG)) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type(); - } - unsigned num_digits = 0; - do { - ++num_digits; - } while ((n >>= 4) != 0); - char_type *p = - get(writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size)); - n = abs_value; - const char *digits = spec.type() == 'x' ? - "0123456789abcdef" : "0123456789ABCDEF"; - do { - *p-- = digits[n & 0xf]; - } while ((n >>= 4) != 0); - } - - void on_bin() { - unsigned_type n = abs_value; - if (spec.flag(HASH_FLAG)) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type(); - } - unsigned num_digits = 0; - do { - ++num_digits; - } while ((n >>= 1) != 0); - char_type *p = - get(writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size)); - n = abs_value; - do { - *p-- = static_cast('0' + (n & 1)); - } while ((n >>= 1) != 0); - } - - void on_oct() { - unsigned_type n = abs_value; - if (spec.flag(HASH_FLAG)) - prefix[prefix_size++] = '0'; - unsigned num_digits = 0; - do { - ++num_digits; - } while ((n >>= 3) != 0); - char_type *p = - get(writer.prepare_int_buffer(num_digits, spec, prefix, prefix_size)); - n = abs_value; - do { - *p-- = static_cast('0' + (n & 7)); - } while ((n >>= 3) != 0); - } - - void on_num() { - unsigned num_digits = internal::count_digits(abs_value); - char_type thousands_sep = - internal::thousands_sep(writer.locale_.get()); - fmt::basic_string_view sep(&thousands_sep, 1); - unsigned size = static_cast( - num_digits + sep.size() * ((num_digits - 1) / 3)); - pointer_type p = - writer.prepare_int_buffer(size, spec, prefix, prefix_size) + 1; - internal::format_decimal(get(p), abs_value, 0, - internal::add_thousands_sep(sep)); - } - - void on_error() { - FMT_THROW(format_error("invalid type specifier")); - } - }; - internal::handle_int_type_spec(spec.type(), spec_handler(*this, value, spec)); -} - -template +template template -void basic_writer::write_double(T value, const format_specs &spec) { +void basic_writer::write_double(T value, const format_specs &spec) { // Check type. struct spec_handler { char type; @@ -2667,37 +2552,35 @@ void basic_writer::write_double(T value, const format_specs &spec) { if (internal::fputil::isnotanumber(value)) { // Format NaN ourselves because sprintf's output is not consistent // across platforms. - std::size_t nan_size = 4; - const char *nan = handler.upper ? " NAN" : " nan"; - if (!sign) { - --nan_size; - ++nan; - } - pointer_type out = write_str(nan, nan_size, spec); + const char *nan = handler.upper ? "NAN" : "nan"; + std::size_t nan_size = 3; + reserve(nan_size + (sign ? 1 : 0)); if (sign) - *out = sign; + *begin_++ = sign; + begin_ = std::uninitialized_copy_n(nan, nan_size, begin_); return; } if (internal::fputil::isinfinity(value)) { // Format infinity ourselves because sprintf's output is not consistent // across platforms. - std::size_t inf_size = 4; - const char *inf = handler.upper ? " INF" : " inf"; - if (!sign) { - --inf_size; - ++inf; - } - pointer_type out = write_str(inf, inf_size, spec); + const char *inf = handler.upper ? "INF" : "inf"; + std::size_t inf_size = 3; + reserve(inf_size + (sign ? 1 : 0)); if (sign) - *out = sign; + *begin_++ = sign; + begin_ = std::uninitialized_copy_n(inf, inf_size, begin_); return; } - std::size_t offset = buffer_.size(); + // TODO: buffered_range that wraps a range and adds necessary buffering if the + // latter is not contiguous. + basic_memory_buffer buffer; + + std::size_t offset = 0; unsigned width = spec.width(); if (sign) { - buffer_.reserve(buffer_.size() + (width > 1u ? width : 1u)); + buffer.reserve(width > 1u ? width : 1u); if (width > 0) --width; ++offset; @@ -2733,54 +2616,39 @@ void basic_writer::write_double(T value, const format_specs &spec) { unsigned n = 0; char_type *start = 0; for (;;) { - std::size_t buffer_size = buffer_.capacity() - offset; + std::size_t buffer_size = buffer.capacity() - offset; #if FMT_MSC_VER // MSVC's vsnprintf_s doesn't work with zero size, so reserve // space for at least one extra character to make the size non-zero. - // Note that the buffer's capacity will increase by more than 1. + // Note that the buffer's capacity may increase by more than 1. if (buffer_size == 0) { - buffer_.reserve(offset + 1); - buffer_size = buffer_.capacity() - offset; + buffer.reserve(offset + 1); + buffer_size = buffer.capacity() - offset; } #endif - start = &buffer_[offset]; + start = &buffer[offset]; int result = internal::char_traits::format_float( start, buffer_size, format, width_for_sprintf, spec.precision(), value); if (result >= 0) { n = internal::to_unsigned(result); - if (offset + n < buffer_.capacity()) + if (offset + n < buffer.capacity()) break; // The buffer is large enough - continue with formatting. - buffer_.reserve(offset + n + 1); + buffer.reserve(offset + n + 1); } else { // If result is negative we ask to increase the capacity by at least 1, // but as std::vector, the buffer grows exponentially. - buffer_.reserve(buffer_.capacity() + 1); + buffer.reserve(buffer.capacity() + 1); } } - if (sign) { - if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || - *start != ' ') { - *(start - 1) = sign; - sign = 0; - } else { - *(start - 1) = fill; - } + if (sign) ++n; - } - if (spec.align() == ALIGN_CENTER && spec.width() > n) { - width = spec.width(); - pointer_type p = grow_buffer(width); - std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(char_type)); - fill_padding(p, spec.width(), n, fill); - return; - } - if (spec.fill() != ' ' || sign) { - while (*start == ' ') - *start++ = fill; - if (sign) - *(start - 1) = sign; - } - grow_buffer(n); + write_padded(n, spec, [n, sign, &buffer](auto &it) mutable { + if (sign) { + *it++ = sign; + --n; + } + it = std::uninitialized_copy_n(buffer.begin(), n, it); + }); } // Reports a system error without throwing an exception.