diff --git a/include/fmt/format.h b/include/fmt/format.h index 5d36a682..a5199b67 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -473,8 +473,8 @@ inline size_t count_code_points(basic_string_view s) { } // Counts the number of code points in a UTF-8 string. -inline size_t count_code_points(basic_string_view s) { - const char8_t* data = s.data(); +inline size_t count_code_points(basic_string_view s) { + const char* data = s.data(); size_t num_code_points = 0; for (size_t i = 0, size = s.size(); i != size; ++i) { if ((data[i] & 0xc0) != 0x80) ++num_code_points; @@ -482,6 +482,11 @@ inline size_t count_code_points(basic_string_view s) { return num_code_points; } +inline size_t count_code_points(basic_string_view s) { + return count_code_points(basic_string_view( + reinterpret_cast(s.data()), s.size())); +} + template inline size_t code_point_index(basic_string_view s, size_t n) { size_t size = s.size(); @@ -1603,6 +1608,18 @@ template class basic_writer { } }; + struct bytes_writer { + string_view bytes; + + size_t size() const { return bytes.size(); } + size_t width() const { return bytes.size(); } + + template void operator()(It&& it) const { + const char* data = bytes.data(); + it = copy_str(data, data + size(), it); + } + }; + template struct pointer_writer { UIntPtr value; int num_digits; @@ -1761,6 +1778,10 @@ template class basic_writer { write(data, size, specs); } + void write_bytes(string_view bytes, const format_specs& specs) { + write_padded(specs, bytes_writer{bytes}); + } + template void write_pointer(UIntPtr value, const format_specs* specs) { int num_digits = count_digits<4>(value); @@ -3150,11 +3171,32 @@ class bytes { explicit bytes(string_view data) : data_(data) {} }; -template <> struct formatter : formatter { +template <> struct formatter { + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + using handler_type = internal::dynamic_specs_handler; + internal::specs_checker handler(handler_type(specs_, ctx), + internal::type::string_type); + auto it = parse_format_specs(ctx.begin(), ctx.end(), handler); + internal::check_string_type_spec(specs_.type, ctx.error_handler()); + return it; + } + template auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) { - return formatter::format(b.data_, ctx); + internal::handle_dynamic_spec( + specs_.width, specs_.width_ref, ctx); + internal::handle_dynamic_spec( + specs_.precision, specs_.precision_ref, ctx); + using range_type = + internal::output_range; + internal::basic_writer writer(range_type(ctx.out())); + writer.write_bytes(b.data_, specs_); + return writer.out(); } + + private: + internal::dynamic_format_specs specs_; }; template struct arg_join : internal::view { diff --git a/test/format-test.cc b/test/format-test.cc index b1730630..070c5033 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -6,6 +6,7 @@ // For the license information refer to format.h. #include + #include #include #include @@ -987,7 +988,9 @@ TEST(FormatterTest, Width) { EXPECT_EQ(" 0xcafe", format("{0:10}", reinterpret_cast(0xcafe))); EXPECT_EQ("x ", format("{0:11}", 'x')); EXPECT_EQ("str ", format("{0:12}", "str")); + EXPECT_EQ(fmt::format("{:*^5}", "🤡"), "**🤡**"); } + template inline T const_check(T value) { return value; } TEST(FormatterTest, RuntimeWidth) {