From ded0a3bb3d7bc817d83c8f2dd602a677c96ed438 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 3 Jul 2019 09:52:09 -0700 Subject: [PATCH] Internalize undocumented basic_writer --- include/fmt/chrono.h | 2 +- include/fmt/format-inl.h | 9 +- include/fmt/format.h | 875 +++++++++++++++++++-------------------- test/format-test.cc | 14 +- 4 files changed, 447 insertions(+), 453 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index edbf68d7..ee5a8c27 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -803,7 +803,7 @@ struct formatter, Char> { basic_memory_buffer buf; auto out = std::back_inserter(buf); using range = internal::output_range; - basic_writer w(range(ctx.out())); + internal::basic_writer w(range(ctx.out())); internal::handle_dynamic_spec( spec.width_, width_ref, ctx, format_str.begin()); internal::handle_dynamic_spec( diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 498948fa..b60149fa 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -164,7 +164,7 @@ FMT_FUNC void format_error_code(internal::buffer& out, int error_code, ++error_code_size; } error_code_size += internal::to_unsigned(internal::count_digits(abs_value)); - writer w(out); + internal::writer w(out); if (message.size() <= inline_buffer_size - error_code_size) { w.write(message); w.write(SEP); @@ -245,10 +245,9 @@ template int format_float(char* buf, std::size_t size, const char* format, int precision, T value) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (precision > 100000) { + if (precision > 100000) throw std::runtime_error( "fuzz mode - avoid large allocation inside snprintf"); - } #endif // Suppress the warning about nonliteral format string. auto snprintf_ptr = FMT_SNPRINTF; @@ -898,7 +897,7 @@ FMT_FUNC void internal::format_windows_error(internal::buffer& out, if (result != 0) { utf16_to_utf8 utf8_message; if (utf8_message.convert(system_message) == ERROR_SUCCESS) { - writer w(out); + internal::writer w(out); w.write(message); w.write(": "); w.write(utf8_message); @@ -927,7 +926,7 @@ FMT_FUNC void format_system_error(internal::buffer& out, int error_code, int result = internal::safe_strerror(error_code, system_message, buf.size()); if (result == 0) { - writer w(out); + internal::writer w(out); w.write(message); w.write(": "); w.write(system_message); diff --git a/include/fmt/format.h b/include/fmt/format.h index a6b928c4..b61f6d56 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -300,7 +300,7 @@ enum { inline_buffer_size = 500 }; A dynamically growing memory buffer for trivially copyable/constructible types with the first ``SIZE`` elements stored in the object itself. - You can use one of the following typedefs for common character types: + You can use one of the following type aliases for common character types: +----------------+------------------------------+ | Type | Definition | @@ -340,8 +340,8 @@ class basic_memory_buffer : private Allocator, public internal::buffer { void grow(std::size_t size) FMT_OVERRIDE; public: - typedef T value_type; - typedef const T& const_reference; + using value_type = T; + using const_reference = const T&; explicit basic_memory_buffer(const Allocator& alloc = Allocator()) : Allocator(alloc) { @@ -397,9 +397,7 @@ class basic_memory_buffer : private Allocator, public internal::buffer { template void basic_memory_buffer::grow(std::size_t size) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (size > 1000) { - throw std::runtime_error("fuzz mode - won't grow that much"); - } + if (size > 1000) throw std::runtime_error("fuzz mode - won't grow that much"); #endif std::size_t old_capacity = this->capacity(); std::size_t new_capacity = old_capacity + old_capacity / 2; @@ -416,8 +414,8 @@ void basic_memory_buffer::grow(std::size_t size) { if (old_data != store_) Allocator::deallocate(old_data, old_capacity); } -typedef basic_memory_buffer memory_buffer; -typedef basic_memory_buffer wmemory_buffer; +using memory_buffer = basic_memory_buffer; +using wmemory_buffer = basic_memory_buffer; /** A formatting error such as invalid format string. */ class FMT_API format_error : public std::runtime_error { @@ -428,10 +426,6 @@ class FMT_API format_error : public std::runtime_error { ~format_error() FMT_NOEXCEPT; }; -template class basic_writer; -using writer = basic_writer>; -using wwriter = basic_writer>; - namespace internal { // A workaround for std::string not having mutable data() until C++17. @@ -1069,9 +1063,8 @@ It grisu_prettify(const char* digits, int size, int exp, It it, if (params.trailing_zeros) { *it++ = static_cast('.'); #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (num_zeros > 1000) { + if (num_zeros > 1000) throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); - } #endif it = std::fill_n(it, num_zeros, static_cast('0')); } @@ -1271,6 +1264,428 @@ void arg_map::init(const basic_format_args& args) { } } +/** + This template provides operations for formatting and writing data into a + character range. + */ +template class basic_writer { + public: + typedef typename Range::value_type char_type; + typedef decltype(std::declval().begin()) iterator; + typedef basic_format_specs format_specs; + + private: + iterator out_; // Output iterator. + internal::locale_ref locale_; + + // 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); + } + + template struct padded_int_writer { + size_t size_; + string_view prefix; + char_type fill; + std::size_t padding; + F f; + + size_t size() const { return size_; } + size_t width() const { return size_; } + + template void operator()(It&& it) const { + if (prefix.size() != 0) + it = internal::copy_str(prefix.begin(), prefix.end(), it); + it = std::fill_n(it, padding, fill); + f(it); + } + }; + + // Writes an integer in the format + // + // where are written by f(it). + template + void write_int(int num_digits, string_view prefix, const Spec& spec, F f) { + std::size_t size = prefix.size() + internal::to_unsigned(num_digits); + char_type fill = static_cast(spec.fill()); + std::size_t 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() + internal::to_unsigned(spec.precision); + padding = internal::to_unsigned(spec.precision - num_digits); + fill = static_cast('0'); + } + align_spec as = spec; + if (spec.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT; + write_padded(as, padded_int_writer{size, prefix, fill, padding, f}); + } + + // Writes a decimal integer. + template void write_decimal(Int value) { + typedef typename internal::int_traits::main_type main_type; + main_type abs_value = static_cast(value); + bool is_negative = internal::is_negative(value); + if (is_negative) abs_value = 0 - abs_value; + int num_digits = internal::count_digits(abs_value); + auto&& it = + reserve((is_negative ? 1 : 0) + static_cast(num_digits)); + if (is_negative) *it++ = static_cast('-'); + it = internal::format_decimal(it, abs_value, num_digits); + } + + // The handle_int_type_spec handler that writes an integer. + template struct int_writer { + typedef typename internal::int_traits::main_type unsigned_type; + + basic_writer& writer; + const Spec& spec; + unsigned_type abs_value; + char prefix[4]; + unsigned prefix_size; + + string_view get_prefix() const { return string_view(prefix, prefix_size); } + + int_writer(basic_writer& w, Int value, const Spec& s) + : writer(w), + spec(s), + abs_value(static_cast(value)), + prefix_size(0) { + if (internal::is_negative(value)) { + prefix[0] = '-'; + ++prefix_size; + abs_value = 0 - abs_value; + } else if (spec.has(SIGN_FLAG)) { + prefix[0] = spec.has(PLUS_FLAG) ? '+' : ' '; + ++prefix_size; + } + } + + struct dec_writer { + unsigned_type abs_value; + int num_digits; + + template void operator()(It&& it) const { + it = internal::format_decimal(it, abs_value, num_digits); + } + }; + + void on_dec() { + int num_digits = internal::count_digits(abs_value); + writer.write_int(num_digits, get_prefix(), spec, + dec_writer{abs_value, num_digits}); + } + + struct hex_writer { + int_writer& self; + int num_digits; + + template void operator()(It&& it) const { + it = internal::format_uint<4, char_type>(it, self.abs_value, num_digits, + self.spec.type != 'x'); + } + }; + + void on_hex() { + if (spec.has(HASH_FLAG)) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = static_cast(spec.type); + } + int num_digits = internal::count_digits<4>(abs_value); + writer.write_int(num_digits, get_prefix(), spec, + hex_writer{*this, num_digits}); + } + + template struct bin_writer { + unsigned_type abs_value; + int num_digits; + + template void operator()(It&& it) const { + it = internal::format_uint(it, abs_value, num_digits); + } + }; + + void on_bin() { + if (spec.has(HASH_FLAG)) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = static_cast(spec.type); + } + int num_digits = internal::count_digits<1>(abs_value); + writer.write_int(num_digits, get_prefix(), spec, + bin_writer<1>{abs_value, num_digits}); + } + + void on_oct() { + int num_digits = internal::count_digits<3>(abs_value); + if (spec.has(HASH_FLAG) && spec.precision <= num_digits) { + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + prefix[prefix_size++] = '0'; + } + writer.write_int(num_digits, get_prefix(), spec, + bin_writer<3>{abs_value, num_digits}); + } + + enum { SEP_SIZE = 1 }; + + struct num_writer { + unsigned_type abs_value; + int size; + char_type sep; + + template void operator()(It&& it) const { + basic_string_view s(&sep, SEP_SIZE); + it = internal::format_decimal( + it, abs_value, size, internal::add_thousands_sep(s)); + } + }; + + void on_num() { + int num_digits = internal::count_digits(abs_value); + char_type sep = internal::thousands_sep(writer.locale_); + int size = num_digits + SEP_SIZE * ((num_digits - 1) / 3); + writer.write_int(size, get_prefix(), spec, + num_writer{abs_value, size, sep}); + } + + FMT_NORETURN void on_error() { + FMT_THROW(format_error("invalid type specifier")); + } + }; + + enum { INF_SIZE = 3 }; // This is an enum to workaround a bug in MSVC. + + struct inf_or_nan_writer { + char sign; + bool as_percentage; + const char* str; + + size_t size() const { + return static_cast(INF_SIZE + (sign ? 1 : 0) + + (as_percentage ? 1 : 0)); + } + size_t width() const { return size(); } + + template void operator()(It&& it) const { + if (sign) *it++ = static_cast(sign); + it = internal::copy_str( + str, str + static_cast(INF_SIZE), it); + if (as_percentage) *it++ = static_cast('%'); + } + }; + + struct double_writer { + char sign; + internal::buffer& buffer; + + size_t size() const { return buffer.size() + (sign ? 1 : 0); } + size_t width() const { return size(); } + + template void operator()(It&& it) { + if (sign) *it++ = static_cast(sign); + it = internal::copy_str(buffer.begin(), buffer.end(), it); + } + }; + + class grisu_writer { + private: + internal::buffer& digits_; + size_t size_; + char sign_; + int exp_; + internal::gen_digits_params params_; + + public: + grisu_writer(char sign, internal::buffer& digits, int exp, + const internal::gen_digits_params& params) + : digits_(digits), sign_(sign), exp_(exp), params_(params) { + int num_digits = static_cast(digits.size()); + int full_exp = num_digits + exp - 1; + int precision = params.num_digits > 0 ? params.num_digits : 11; + params_.fixed |= full_exp >= -4 && full_exp < precision; + auto it = internal::grisu_prettify( + digits.data(), num_digits, exp, internal::counting_iterator(), + params_); + size_ = it.count(); + } + + size_t size() const { return size_ + (sign_ ? 1 : 0); } + size_t width() const { return size(); } + + template void operator()(It&& it) { + if (sign_) *it++ = static_cast(sign_); + int num_digits = static_cast(digits_.size()); + it = internal::grisu_prettify(digits_.data(), num_digits, exp_, + it, params_); + } + }; + + template struct str_writer { + const Char* s; + size_t size_; + + size_t size() const { return size_; } + size_t width() const { + return internal::count_code_points(basic_string_view(s, size_)); + } + + template void operator()(It&& it) const { + it = internal::copy_str(s, s + size_, it); + } + }; + + template struct pointer_writer { + UIntPtr value; + int num_digits; + + size_t size() const { return num_digits + 2; } + size_t width() const { return size(); } + + template void operator()(It&& it) const { + *it++ = static_cast('0'); + *it++ = static_cast('x'); + it = internal::format_uint<4, char_type>(it, value, num_digits); + } + }; + + public: + /** Constructs a ``basic_writer`` object. */ + explicit basic_writer(Range out, + internal::locale_ref loc = internal::locale_ref()) + : out_(out.begin()), locale_(loc) {} + + iterator out() const { return out_; } + + // Writes a value in the format + // + // where is written by f(it). + template void write_padded(const align_spec& spec, F&& f) { + unsigned width = spec.width(); // User-perceived width (in code points). + size_t size = f.size(); // The number of code units. + size_t num_code_points = width != 0 ? f.width() : size; + if (width <= num_code_points) return f(reserve(size)); + auto&& it = reserve(width + (size - num_code_points)); + char_type fill = static_cast(spec.fill()); + std::size_t padding = width - num_code_points; + if (spec.align() == ALIGN_RIGHT) { + it = std::fill_n(it, padding, fill); + f(it); + } else if (spec.align() == ALIGN_CENTER) { + std::size_t left_padding = padding / 2; + it = std::fill_n(it, left_padding, fill); + f(it); + it = std::fill_n(it, padding - left_padding, fill); + } else { + f(it); + it = std::fill_n(it, padding, fill); + } + } + + void write(int value) { write_decimal(value); } + void write(long value) { write_decimal(value); } + void write(long long value) { write_decimal(value); } + + void write(unsigned value) { write_decimal(value); } + void write(unsigned long value) { write_decimal(value); } + void write(unsigned long long value) { write_decimal(value); } + + // Writes a formatted integer. + template + void write_int(T value, const Spec& spec) { + internal::handle_int_type_spec(spec.type, + int_writer(*this, value, spec)); + } + + /** + \rst + Formats *value* and writes it to the buffer. + \endrst + */ + template ::value)> + void write(T value, FormatSpec spec, FormatSpecs... specs) { + format_specs s(spec, specs...); + s.align_ = ALIGN_RIGHT; + write_int(value, s); + } + + void write(double value, const format_specs& spec = format_specs()) { + write_double(value, spec); + } + + /** + \rst + Formats *value* using the general format for floating-point numbers + (``'g'``) and writes it to the buffer. + \endrst + */ + void write(long double value, const format_specs& spec = format_specs()) { + write_double(value, spec); + } + + // Formats a floating-point number (double or long double). + template void write_double(T value, const format_specs& spec); + + /** Writes a character to the buffer. */ + void write(char value) { + auto&& it = reserve(1); + *it++ = value; + } + + template ::value)> + void write(Char value) { + auto&& it = reserve(1); + *it++ = value; + } + + /** + \rst + Writes *value* to the buffer. + \endrst + */ + void write(string_view value) { + auto&& it = reserve(value.size()); + it = internal::copy_str(value.begin(), value.end(), it); + } + void write(wstring_view value) { + static_assert(std::is_same::value, ""); + auto&& it = reserve(value.size()); + it = std::copy(value.begin(), value.end(), it); + } + + // Writes a formatted string. + template + void write(const Char* s, std::size_t size, const align_spec& spec) { + write_padded(spec, str_writer{s, size}); + } + + template + void write(basic_string_view s, + const format_specs& spec = format_specs()) { + const Char* data = s.data(); + std::size_t size = s.size(); + if (spec.precision >= 0 && internal::to_unsigned(spec.precision) < size) + size = internal::to_unsigned(spec.precision); + write(data, size, spec); + } + + template + void write_pointer(UIntPtr value, const align_spec* spec) { + int num_digits = internal::count_digits<4>(value); + auto pw = pointer_writer{value, num_digits}; + if (!spec) return pw(reserve(num_digits + 2)); + align_spec as = *spec; + if (as.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT; + write_padded(as, pw); + } +}; + +using writer = basic_writer>; + template class arg_formatter_base { public: @@ -2162,6 +2577,12 @@ void handle_dynamic_spec(Spec& value, arg_ref ref, } } // namespace internal +template +using basic_writer FMT_DEPRECATED = internal::basic_writer; +using writer FMT_DEPRECATED = internal::writer; +using wwriter FMT_DEPRECATED = + internal::basic_writer>; + /** The default argument formatter. */ template class arg_formatter : public internal::arg_formatter_base { @@ -2265,429 +2686,6 @@ class FMT_API system_error : public std::runtime_error { FMT_API void format_system_error(internal::buffer& out, int error_code, fmt::string_view message) FMT_NOEXCEPT; -/** - This template provides operations for formatting and writing data into a - character range. - */ -template class basic_writer { - public: - typedef typename Range::value_type char_type; - typedef decltype(std::declval().begin()) iterator; - typedef basic_format_specs format_specs; - - private: - iterator out_; // Output iterator. - internal::locale_ref locale_; - - // 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 - // - // where is written by f(it). - template void write_padded(const align_spec& spec, F&& f) { - unsigned width = spec.width(); // User-perceived width (in code points). - size_t size = f.size(); // The number of code units. - size_t num_code_points = width != 0 ? f.width() : size; - if (width <= num_code_points) return f(reserve(size)); - auto&& it = reserve(width + (size - num_code_points)); - char_type fill = static_cast(spec.fill()); - std::size_t padding = width - num_code_points; - if (spec.align() == ALIGN_RIGHT) { - it = std::fill_n(it, padding, fill); - f(it); - } else if (spec.align() == ALIGN_CENTER) { - std::size_t left_padding = padding / 2; - it = std::fill_n(it, left_padding, fill); - f(it); - it = std::fill_n(it, padding - left_padding, fill); - } else { - f(it); - it = std::fill_n(it, padding, fill); - } - } - - template struct padded_int_writer { - size_t size_; - string_view prefix; - char_type fill; - std::size_t padding; - F f; - - size_t size() const { return size_; } - size_t width() const { return size_; } - - template void operator()(It&& it) const { - if (prefix.size() != 0) - it = internal::copy_str(prefix.begin(), prefix.end(), it); - it = std::fill_n(it, padding, fill); - f(it); - } - }; - - // Writes an integer in the format - // - // where are written by f(it). - template - void write_int(int num_digits, string_view prefix, const Spec& spec, F f) { - std::size_t size = prefix.size() + internal::to_unsigned(num_digits); - char_type fill = static_cast(spec.fill()); - std::size_t 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() + internal::to_unsigned(spec.precision); - padding = internal::to_unsigned(spec.precision - num_digits); - fill = static_cast('0'); - } - align_spec as = spec; - if (spec.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT; - write_padded(as, padded_int_writer{size, prefix, fill, padding, f}); - } - - // Writes a decimal integer. - template void write_decimal(Int value) { - typedef typename internal::int_traits::main_type main_type; - main_type abs_value = static_cast(value); - bool is_negative = internal::is_negative(value); - if (is_negative) abs_value = 0 - abs_value; - int num_digits = internal::count_digits(abs_value); - auto&& it = - reserve((is_negative ? 1 : 0) + static_cast(num_digits)); - if (is_negative) *it++ = static_cast('-'); - it = internal::format_decimal(it, abs_value, num_digits); - } - - // The handle_int_type_spec handler that writes an integer. - template struct int_writer { - typedef typename internal::int_traits::main_type unsigned_type; - - basic_writer& writer; - const Spec& spec; - unsigned_type abs_value; - char prefix[4]; - unsigned prefix_size; - - string_view get_prefix() const { return string_view(prefix, prefix_size); } - - int_writer(basic_writer& w, Int value, const Spec& s) - : writer(w), - spec(s), - abs_value(static_cast(value)), - prefix_size(0) { - if (internal::is_negative(value)) { - prefix[0] = '-'; - ++prefix_size; - abs_value = 0 - abs_value; - } else if (spec.has(SIGN_FLAG)) { - prefix[0] = spec.has(PLUS_FLAG) ? '+' : ' '; - ++prefix_size; - } - } - - struct dec_writer { - unsigned_type abs_value; - int num_digits; - - template void operator()(It&& it) const { - it = internal::format_decimal(it, abs_value, num_digits); - } - }; - - void on_dec() { - int num_digits = internal::count_digits(abs_value); - writer.write_int(num_digits, get_prefix(), spec, - dec_writer{abs_value, num_digits}); - } - - struct hex_writer { - int_writer& self; - int num_digits; - - template void operator()(It&& it) const { - it = internal::format_uint<4, char_type>(it, self.abs_value, num_digits, - self.spec.type != 'x'); - } - }; - - void on_hex() { - if (spec.has(HASH_FLAG)) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = static_cast(spec.type); - } - int num_digits = internal::count_digits<4>(abs_value); - writer.write_int(num_digits, get_prefix(), spec, - hex_writer{*this, num_digits}); - } - - template struct bin_writer { - unsigned_type abs_value; - int num_digits; - - template void operator()(It&& it) const { - it = internal::format_uint(it, abs_value, num_digits); - } - }; - - void on_bin() { - if (spec.has(HASH_FLAG)) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = static_cast(spec.type); - } - int num_digits = internal::count_digits<1>(abs_value); - writer.write_int(num_digits, get_prefix(), spec, - bin_writer<1>{abs_value, num_digits}); - } - - void on_oct() { - int num_digits = internal::count_digits<3>(abs_value); - if (spec.has(HASH_FLAG) && spec.precision <= num_digits) { - // Octal prefix '0' is counted as a digit, so only add it if precision - // is not greater than the number of digits. - prefix[prefix_size++] = '0'; - } - writer.write_int(num_digits, get_prefix(), spec, - bin_writer<3>{abs_value, num_digits}); - } - - enum { SEP_SIZE = 1 }; - - struct num_writer { - unsigned_type abs_value; - int size; - char_type sep; - - template void operator()(It&& it) const { - basic_string_view s(&sep, SEP_SIZE); - it = internal::format_decimal( - it, abs_value, size, internal::add_thousands_sep(s)); - } - }; - - void on_num() { - int num_digits = internal::count_digits(abs_value); - char_type sep = internal::thousands_sep(writer.locale_); - int size = num_digits + SEP_SIZE * ((num_digits - 1) / 3); - writer.write_int(size, get_prefix(), spec, - num_writer{abs_value, size, sep}); - } - - FMT_NORETURN void on_error() { - FMT_THROW(format_error("invalid type specifier")); - } - }; - - // Writes a formatted integer. - template - void write_int(T value, const Spec& spec) { - internal::handle_int_type_spec(spec.type, - int_writer(*this, value, spec)); - } - - enum { INF_SIZE = 3 }; // This is an enum to workaround a bug in MSVC. - - struct inf_or_nan_writer { - char sign; - bool as_percentage; - const char* str; - - size_t size() const { - return static_cast(INF_SIZE + (sign ? 1 : 0) + - (as_percentage ? 1 : 0)); - } - size_t width() const { return size(); } - - template void operator()(It&& it) const { - if (sign) *it++ = static_cast(sign); - it = internal::copy_str( - str, str + static_cast(INF_SIZE), it); - if (as_percentage) *it++ = static_cast('%'); - } - }; - - struct double_writer { - char sign; - internal::buffer& buffer; - - size_t size() const { return buffer.size() + (sign ? 1 : 0); } - size_t width() const { return size(); } - - template void operator()(It&& it) { - if (sign) *it++ = static_cast(sign); - it = internal::copy_str(buffer.begin(), buffer.end(), it); - } - }; - - class grisu_writer { - private: - internal::buffer& digits_; - size_t size_; - char sign_; - int exp_; - internal::gen_digits_params params_; - - public: - grisu_writer(char sign, internal::buffer& digits, int exp, - const internal::gen_digits_params& params) - : digits_(digits), sign_(sign), exp_(exp), params_(params) { - int num_digits = static_cast(digits.size()); - int full_exp = num_digits + exp - 1; - int precision = params.num_digits > 0 ? params.num_digits : 11; - params_.fixed |= full_exp >= -4 && full_exp < precision; - auto it = internal::grisu_prettify( - digits.data(), num_digits, exp, internal::counting_iterator(), - params_); - size_ = it.count(); - } - - size_t size() const { return size_ + (sign_ ? 1 : 0); } - size_t width() const { return size(); } - - template void operator()(It&& it) { - if (sign_) *it++ = static_cast(sign_); - int num_digits = static_cast(digits_.size()); - it = internal::grisu_prettify(digits_.data(), num_digits, exp_, - it, params_); - } - }; - - // Formats a floating-point number (double or long double). - template void write_double(T value, const format_specs& spec); - - template struct str_writer { - const Char* s; - size_t size_; - - size_t size() const { return size_; } - size_t width() const { - return internal::count_code_points(basic_string_view(s, size_)); - } - - template void operator()(It&& it) const { - it = internal::copy_str(s, s + size_, it); - } - }; - - template struct pointer_writer { - UIntPtr value; - int num_digits; - - size_t size() const { return num_digits + 2; } - size_t width() const { return size(); } - - template void operator()(It&& it) const { - *it++ = static_cast('0'); - *it++ = static_cast('x'); - it = internal::format_uint<4, char_type>(it, value, num_digits); - } - }; - - template - friend class internal::arg_formatter_base; - - public: - /** Constructs a ``basic_writer`` object. */ - explicit basic_writer(Range out, - internal::locale_ref loc = internal::locale_ref()) - : out_(out.begin()), locale_(loc) {} - - iterator out() const { return out_; } - - void write(int value) { write_decimal(value); } - void write(long value) { write_decimal(value); } - void write(long long value) { write_decimal(value); } - - void write(unsigned value) { write_decimal(value); } - void write(unsigned long value) { write_decimal(value); } - void write(unsigned long long value) { write_decimal(value); } - - /** - \rst - Formats *value* and writes it to the buffer. - \endrst - */ - template ::value)> - void write(T value, FormatSpec spec, FormatSpecs... specs) { - format_specs s(spec, specs...); - s.align_ = ALIGN_RIGHT; - write_int(value, s); - } - - void write(double value, const format_specs& spec = format_specs()) { - write_double(value, spec); - } - - /** - \rst - Formats *value* using the general format for floating-point numbers - (``'g'``) and writes it to the buffer. - \endrst - */ - void write(long double value, const format_specs& spec = format_specs()) { - write_double(value, spec); - } - - /** Writes a character to the buffer. */ - void write(char value) { - auto&& it = reserve(1); - *it++ = value; - } - - template ::value)> - void write(Char value) { - auto&& it = reserve(1); - *it++ = value; - } - - /** - \rst - Writes *value* to the buffer. - \endrst - */ - void write(string_view value) { - auto&& it = reserve(value.size()); - it = internal::copy_str(value.begin(), value.end(), it); - } - void write(wstring_view value) { - static_assert(std::is_same::value, ""); - auto&& it = reserve(value.size()); - it = std::copy(value.begin(), value.end(), it); - } - - // Writes a formatted string. - template - void write(const Char* s, std::size_t size, const align_spec& spec) { - write_padded(spec, str_writer{s, size}); - } - - template - void write(basic_string_view s, - const format_specs& spec = format_specs()) { - const Char* data = s.data(); - std::size_t size = s.size(); - if (spec.precision >= 0 && internal::to_unsigned(spec.precision) < size) - size = internal::to_unsigned(spec.precision); - write(data, size, spec); - } - - template - void write_pointer(UIntPtr value, const align_spec* spec) { - int num_digits = internal::count_digits<4>(value); - auto pw = pointer_writer{value, num_digits}; - if (!spec) return pw(reserve(num_digits + 2)); - align_spec as = *spec; - if (as.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT; - write_padded(as, pw); - } -}; - struct float_spec_handler { char type; bool upper; @@ -2726,7 +2724,8 @@ struct float_spec_handler { template template -void basic_writer::write_double(T value, const format_specs& spec) { +void internal::basic_writer::write_double(T value, + const format_specs& spec) { // Check type. float_spec_handler handler(static_cast(spec.type)); internal::handle_float_type_spec(handler.type, handler); diff --git a/test/format-test.cc b/test/format-test.cc index c5523deb..2906168f 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -34,7 +34,7 @@ using std::size_t; using fmt::basic_memory_buffer; -using fmt::basic_writer; +using fmt::internal::basic_writer; using fmt::format; using fmt::format_error; using fmt::memory_buffer; @@ -100,7 +100,7 @@ template ::testing::AssertionResult check_write(const T& value, const char* type) { fmt::basic_memory_buffer buffer; using range = fmt::internal::buffer_range; - fmt::basic_writer writer(buffer); + basic_writer writer(buffer); writer.write(value); std::basic_string actual = to_string(buffer); std::basic_string expected; @@ -582,7 +582,7 @@ TEST(StringViewTest, Ctor) { TEST(WriterTest, Data) { memory_buffer buf; - fmt::writer w(buf); + fmt::internal::writer w(buf); w.write(42); EXPECT_EQ("42", to_string(buf)); } @@ -649,13 +649,13 @@ TEST(WriterTest, WriteLongDouble) { TEST(WriterTest, WriteDoubleAtBufferBoundary) { memory_buffer buf; - fmt::writer writer(buf); + fmt::internal::writer writer(buf); for (int i = 0; i < 100; ++i) writer.write(1.23456789); } TEST(WriterTest, WriteDoubleWithFilledBuffer) { memory_buffer buf; - fmt::writer writer(buf); + fmt::internal::writer writer(buf); // Fill the buffer. for (int i = 0; i < fmt::inline_buffer_size; ++i) writer.write(' '); writer.write(1.2); @@ -671,14 +671,10 @@ TEST(WriterTest, WriteWideChar) { CHECK_WRITE_WCHAR(L'a'); } TEST(WriterTest, WriteString) { CHECK_WRITE_CHAR("abc"); CHECK_WRITE_WCHAR("abc"); - // The following line shouldn't compile: - // std::declval>().write(L"abc"); } TEST(WriterTest, WriteWideString) { CHECK_WRITE_WCHAR(L"abc"); - // The following line shouldn't compile: - // std::declval>().write("abc"); } TEST(FormatToTest, FormatWithoutArgs) {