diff --git a/include/fmt/format.h b/include/fmt/format.h index 500fd40275da..43403027ba1c 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1570,10 +1570,14 @@ inline auto find_escape(const char* begin, const char* end) */ #define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string, ) -template -auto write_escaped_string(OutputIt out, basic_string_view str) - -> OutputIt { - return copy_str(str.data(), str.data() + str.size(), out); +template +auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { + *out++ = static_cast('\\'); + *out++ = static_cast(prefix); + Char buf[width]; + fill_n(buf, width, static_cast('0')); + format_uint<4>(buf, cp, width); + return copy_str(buf, buf + width, out); } template @@ -1582,40 +1586,40 @@ auto write_escaped_cp(OutputIt out, const find_escape_result& escape) auto c = static_cast(escape.cp); switch (escape.cp) { case '\n': - *out++ = '\\'; - c = 'n'; + *out++ = static_cast('\\'); + c = static_cast('n'); break; case '\r': - *out++ = '\\'; - c = 'r'; + *out++ = static_cast('\\'); + c = static_cast('r'); break; case '\t': - *out++ = '\\'; - c = 't'; + *out++ = static_cast('\\'); + c = static_cast('t'); break; case '"': FMT_FALLTHROUGH; case '\'': FMT_FALLTHROUGH; case '\\': - *out++ = '\\'; + *out++ = static_cast('\\'); break; default: if (is_utf8()) { if (escape.cp < 0x100) { - return format_to(out, FMT_STRING("\\x{:02x}"), escape.cp); + return write_codepoint<2, Char>(out, 'x', escape.cp); } if (escape.cp < 0x10000) { - return format_to(out, FMT_STRING("\\u{:04x}"), escape.cp); + return write_codepoint<4, Char>(out, 'u', escape.cp); } if (escape.cp < 0x110000) { - return format_to(out, FMT_STRING("\\U{:08x}"), escape.cp); + return write_codepoint<8, Char>(out, 'U', escape.cp); } } - for (char escape_char : basic_string_view( + for (Char escape_char : basic_string_view( escape.begin, to_unsigned(escape.end - escape.begin))) { - out = format_to(out, FMT_STRING("\\x{:02x}"), - static_cast>(escape_char)); + out = write_codepoint<2, Char>(out, 'x', + static_cast(escape_char) & 0xFF); } return out; } @@ -1623,38 +1627,33 @@ auto write_escaped_cp(OutputIt out, const find_escape_result& escape) return out; } -template -auto write_escaped_string(OutputIt out, basic_string_view str) +template +auto write_escaped_string(OutputIt out, basic_string_view str) -> OutputIt { - *out++ = '"'; + *out++ = static_cast('"'); auto begin = str.begin(), end = str.end(); do { auto escape = find_escape(begin, end); - out = copy_str(begin, escape.begin, out); + out = copy_str(begin, escape.begin, out); begin = escape.end; if (!begin) break; - out = write_escaped_cp(out, escape); + out = write_escaped_cp(out, escape); } while (begin != end); - *out++ = '"'; + *out++ = static_cast('"'); return out; } template auto write_escaped_char(OutputIt out, Char v) -> OutputIt { - *out++ = v; - return out; -} - -template -auto write_escaped_char(OutputIt out, char v) -> OutputIt { - *out++ = '\''; - if ((needs_escape(static_cast(v)) && v != '"') || v == '\'') { + *out++ = static_cast('\''); + if ((needs_escape(static_cast(v)) && v != static_cast('"')) || + v == static_cast('\'')) { out = write_escaped_cp( - out, find_escape_result{&v, &v + 1, static_cast(v)}); + out, find_escape_result{&v, &v + 1, static_cast(v)}); } else { *out++ = v; } - *out++ = '\''; + *out++ = static_cast('\''); return out; } diff --git a/test/ranges-test.cc b/test/ranges-test.cc index aa910e794022..c4a7712469c1 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -70,13 +70,9 @@ struct box { int value; }; -auto begin(const box& b) -> const int* { - return &b.value; -} +auto begin(const box& b) -> const int* { return &b.value; } -auto end(const box& b) -> const int* { - return &b.value + 1; -} +auto end(const box& b) -> const int* { return &b.value + 1; } } // namespace adl TEST(ranges_test, format_adl_begin_end) { @@ -370,6 +366,7 @@ TEST(ranges_test, escape_string) { EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]"); EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}), "[\"\\xf4\\x8f\\xbf\\xc0\"]"); + EXPECT_EQ(fmt::format("{}", vec{"понедельник"}), "[\"понедельник\"]"); } } diff --git a/test/xchar-test.cc b/test/xchar-test.cc index 0b0df9e39404..c695739b1673 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -337,6 +337,17 @@ TEST(xchar_test, ostream) { #endif } +TEST(xchar_test, format_map) { + auto m = std::map{{L"one", 1}, {L"t\"wo", 2}}; + EXPECT_EQ(fmt::format(L"{}", m), L"{\"one\": 1, \"t\\\"wo\": 2}"); +} + +TEST(xchar_test, escape_string) { + using vec = std::vector; + EXPECT_EQ(fmt::format(L"{}", vec{L"\n\r\t\"\\"}), L"[\"\\n\\r\\t\\\"\\\\\"]"); + EXPECT_EQ(fmt::format(L"{}", vec{L"понедельник"}), L"[\"понедельник\"]"); +} + TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } #ifndef FMT_STATIC_THOUSANDS_SEPARATOR