Skip to content

Commit

Permalink
Add xchar support for write_escaped_string.
Browse files Browse the repository at this point in the history
Signed-off-by: Vladislav Shchapov <vladislav@shchapov.ru>
  • Loading branch information
phprus committed May 22, 2022
1 parent ce246aa commit cc85396
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 41 deletions.
86 changes: 51 additions & 35 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -1570,91 +1570,107 @@ inline auto find_escape(const char* begin, const char* end)
*/
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string, )

template <typename Char, typename OutputIt>
auto write_escaped_string(OutputIt out, basic_string_view<Char> str)
-> OutputIt {
return copy_str<Char>(str.data(), str.data() + str.size(), out);
}

template <typename OutputIt, typename Char>
auto write_escaped_cp(OutputIt out, const find_escape_result<Char>& escape)
-> OutputIt {
auto c = static_cast<Char>(escape.cp);
switch (escape.cp) {
case '\n':
*out++ = '\\';
c = 'n';
*out++ = Char('\\');
c = Char('n');
break;
case '\r':
*out++ = '\\';
c = 'r';
*out++ = Char('\\');
c = Char('r');
break;
case '\t':
*out++ = '\\';
c = 't';
*out++ = Char('\\');
c = Char('t');
break;
case '"':
FMT_FALLTHROUGH;
case '\'':
FMT_FALLTHROUGH;
case '\\':
*out++ = '\\';
*out++ = Char('\\');
break;
default:
if (is_utf8()) {
if (escape.cp < 0x100) {
return format_to(out, FMT_STRING("\\x{:02x}"), escape.cp);
*out++ = Char('\\');
*out++ = Char('x');
constexpr size_t len = 2;
Char buf[len];
fill_n(buf, len, Char('0'));
format_uint<4>(buf, escape.cp, len);
out = copy_str<Char>(buf, buf + len, out);
return out;
}
if (escape.cp < 0x10000) {
return format_to(out, FMT_STRING("\\u{:04x}"), escape.cp);
*out++ = Char('\\');
*out++ = Char('u');
constexpr size_t len = 4;
Char buf[len];
fill_n(buf, len, Char('0'));
format_uint<4>(buf, escape.cp, len);
out = copy_str<Char>(buf, buf + len, out);
return out;
}
if (escape.cp < 0x110000) {
return format_to(out, FMT_STRING("\\U{:08x}"), escape.cp);
*out++ = Char('\\');
*out++ = Char('U');

constexpr size_t len = 8;
Char buf[len];
fill_n(buf, len, Char('0'));
format_uint<4>(buf, escape.cp, len);
out = copy_str<Char>(buf, buf + len, out);
return out;
}
}
for (char escape_char : basic_string_view<Char>(
for (Char escape_char : basic_string_view<Char>(
escape.begin, to_unsigned(escape.end - escape.begin))) {
out = format_to(out, FMT_STRING("\\x{:02x}"),
static_cast<make_unsigned_char<Char>>(escape_char));
*out++ = Char('\\');
*out++ = Char('x');
constexpr size_t len = 2;
Char buf[len];
fill_n(buf, len, Char('0'));
format_uint<4>(buf, static_cast<uint32_t>(escape_char), len);
out = copy_str<Char>(buf, buf + len, out);
}
return out;
}
*out++ = c;
return out;
}

template <typename OutputIt>
auto write_escaped_string(OutputIt out, basic_string_view<char> str)
template <typename Char, typename OutputIt>
auto write_escaped_string(OutputIt out, basic_string_view<Char> str)
-> OutputIt {
*out++ = '"';
*out++ = Char('"');
auto begin = str.begin(), end = str.end();
do {
auto escape = find_escape(begin, end);
out = copy_str<char>(begin, escape.begin, out);
out = copy_str<Char>(begin, escape.begin, out);
begin = escape.end;
if (!begin) break;
out = write_escaped_cp(out, escape);
out = write_escaped_cp<OutputIt, Char>(out, escape);
} while (begin != end);
*out++ = '"';
*out++ = Char('"');
return out;
}

template <typename Char, typename OutputIt>
auto write_escaped_char(OutputIt out, Char v) -> OutputIt {
*out++ = v;
return out;
}

template <typename OutputIt>
auto write_escaped_char(OutputIt out, char v) -> OutputIt {
*out++ = '\'';
if ((needs_escape(static_cast<uint32_t>(v)) && v != '"') || v == '\'') {
*out++ = Char('\'');
if ((needs_escape(static_cast<uint32_t>(v)) && v != Char('"')) ||
v == Char('\'')) {
out = write_escaped_cp(
out, find_escape_result<char>{&v, &v + 1, static_cast<uint32_t>(v)});
out, find_escape_result<Char>{&v, &v + 1, static_cast<uint32_t>(v)});
} else {
*out++ = v;
}
*out++ = '\'';
*out++ = Char('\'');
return out;
}

Expand Down
9 changes: 3 additions & 6 deletions test/ranges-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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{"понедельник"}), "[\"понедельник\"]");
}
}

Expand Down
11 changes: 11 additions & 0 deletions test/xchar-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,17 @@ TEST(xchar_test, ostream) {
#endif
}

TEST(xchar_test, format_map) {
auto m = std::map<std::wstring, int>{{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<std::wstring>;
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
Expand Down

0 comments on commit cc85396

Please sign in to comment.