From 38325248e5310ddbea41390974e496e8495f7324 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 3 Oct 2018 18:22:26 -0700 Subject: [PATCH] Count width in code points (#628) --- include/fmt/core.h | 9 +++++---- include/fmt/format-inl.h | 5 +++-- include/fmt/format.h | 41 ++++++++++++++++++++++++++++++---------- test/format-test.cc | 1 + 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 0ec3504c..e3c152cb 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1342,10 +1342,11 @@ struct is_contiguous >: std::true_type {}; /** Formats a string and writes the output to ``out``. */ template typename std::enable_if< - is_contiguous::value, std::back_insert_iterator>::type - vformat_to(std::back_insert_iterator out, - const S &format_str, - basic_format_args::type> args) { + is_contiguous::value, std::back_insert_iterator>::type + vformat_to( + std::back_insert_iterator out, + const S &format_str, + basic_format_args::type> args) { internal::container_buffer buf(internal::get_container(out)); vformat_to(buf, internal::to_string_view(format_str), args); return out; diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 2703763e..51f4d2e8 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -192,7 +192,7 @@ void report_error(FormatFunc func, int error_code, } } // namespace -FMT_FUNC size_t internal::count_code_points(u8string_view s) { +FMT_FUNC size_t internal::count_code_points(basic_string_view s) { const char8_t *data = s.data(); size_t num_code_points = 0; for (size_t i = 0, size = s.size(); i != size; ++i) { @@ -845,7 +845,8 @@ FMT_FUNC void report_windows_error( FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) { memory_buffer buffer; - vformat_to(buffer, format_str, basic_format_args::type>(args)); + vformat_to(buffer, format_str, + basic_format_args::type>(args)); std::fwrite(buffer.data(), 1, buffer.size(), f); } diff --git a/include/fmt/format.h b/include/fmt/format.h index b5089128..0b774d33 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -922,8 +922,11 @@ inline unsigned count_digits(uint64_t n) { } #endif +template +inline size_t count_code_points(basic_string_view s) { return s.size(); } + // Counts the number of code points in a UTF-8 string. -FMT_API size_t count_code_points(u8string_view s); +FMT_API size_t count_code_points(basic_string_view s); inline char8_t to_char8_t(char c) { return static_cast(c); } @@ -1456,6 +1459,9 @@ class arg_formatter_base { struct char_writer { char_type value; + + size_t size() const { return 1; } + template void operator()(It &&it) const { *it++ = value; } }; @@ -2457,11 +2463,14 @@ class basic_writer { 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_; } + template void operator()(It &&it) const { if (prefix.size() != 0) @@ -2493,7 +2502,7 @@ class basic_writer { align_spec as = spec; if (spec.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT; - write_padded(size, as, padded_int_writer{prefix, fill, padding, f}); + write_padded(size, as, padded_int_writer{size, prefix, fill, padding, f}); } // Writes a decimal integer. @@ -2659,6 +2668,8 @@ class basic_writer { char sign; const char *str; + size_t size() const { return static_cast(INF_SIZE); } + template void operator()(It &&it) const { if (sign) @@ -2673,6 +2684,8 @@ class basic_writer { char sign; internal::buffer &buffer; + size_t size() const { return buffer.size() + (sign ? 1 : 0); } + template void operator()(It &&it) { if (sign) { @@ -2693,11 +2706,15 @@ class basic_writer { template struct str_writer { const Char *s; - std::size_t size; + size_t size_; + + size_t size() 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); + it = internal::copy_str(s, s + size_, it); } }; @@ -2796,11 +2813,12 @@ template void basic_writer::write_padded( std::size_t size, const align_spec &spec, F &&f) { unsigned width = spec.width(); - if (width <= size) + size_t num_code_points = width != 0 ? f.size() : size; + if (width <= num_code_points) return f(reserve(size)); - auto &&it = reserve(width); + auto &&it = reserve(width + (size - num_code_points)); char_type fill = static_cast(spec.fill()); - std::size_t padding = width - size; + std::size_t padding = width - num_code_points; if (spec.align() == ALIGN_RIGHT) { it = std::fill_n(it, padding, fill); f(it); @@ -3533,12 +3551,14 @@ using format_to_n_context = typename fmt::format_context_t< fmt::internal::truncating_iterator, Char>::type; template -using format_to_n_args = fmt::basic_format_args>; +using format_to_n_args = + fmt::basic_format_args>; template inline format_arg_store, Args...> make_format_to_n_args(const Args &... args) { - return format_arg_store, Args...>(args...); + return format_arg_store< + format_to_n_context, Args...>(args...); } template @@ -3561,7 +3581,8 @@ template inline typename std::enable_if< internal::is_format_string::value, format_to_n_result>::type format_to_n( - OutputIt out, std::size_t n, const String &format_str, const Args &... args) { + OutputIt out, std::size_t n, const String &format_str, + const Args &... args) { internal::check_format_string(format_str); typedef FMT_CHAR(String) Char; format_arg_store, Args...> as{ args... }; diff --git a/test/format-test.cc b/test/format-test.cc index 766befd2..e48dedea 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2448,6 +2448,7 @@ TEST(FormatTest, U8StringViewLiteral) { const fmt::char8_t *data = s.data(); EXPECT_EQ(data[0], 'a'); EXPECT_EQ(data[1], 'b'); + EXPECT_EQ(format("{:*^5}"_u, "🤡"_u), "**🤡**"_u); } #endif