diff --git a/src/json_reporter.cc b/src/json_reporter.cc index 37da17ba03..ef5117c259 100644 --- a/src/json_reporter.cc +++ b/src/json_reporter.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include // for setprecision #include #include @@ -60,9 +61,18 @@ std::string StrEscape(const std::string& s) { case '"': tmp += "\\\""; break; - default: - tmp += c; + default: { + auto uc = static_cast(c); + if (uc < 0x20) { + char buf[7]; + std::snprintf(buf, sizeof(buf), "\\u%04x", + static_cast(uc)); + tmp += buf; + } else { + tmp += c; + } break; + } } } return tmp; diff --git a/test/reporter_list_gtest.cc b/test/reporter_list_gtest.cc index 74d5d02dd6..4358f4ae05 100644 --- a/test/reporter_list_gtest.cc +++ b/test/reporter_list_gtest.cc @@ -82,6 +82,36 @@ TEST(ReporterListTest, CSVListsNameColumn) { BENCHMARK_RESTORE_DEPRECATED_WARNING } +const std::vector& ListControlCharBenchmarks() { + static const std::vector* const benchmarks = [] { + std::string name = "BM_ControlChar_"; + name += '\x01'; + name += "_"; + name += '\x1f'; + RegisterBenchmark(name, BM_ReporterListDummy); + auto* result = new std::vector(); + std::ostringstream err_stream; + FindBenchmarksInternal("^BM_ControlChar.*", result, &err_stream); + return result; + }(); + return *benchmarks; +} + +TEST(ReporterListTest, JSONEscapesC0ControlCharacters) { + JSONReporter reporter; + std::ostringstream out; + reporter.SetOutputStream(&out); + reporter.List(ListControlCharBenchmarks()); + EXPECT_EQ(out.str(), + "{\n" + " \"benchmarks\": [\n" + " {\n" + " \"name\": \"BM_ControlChar_\\u0001_\\u001f\"\n" + " }\n" + " ]\n" + "}\n"); +} + } // namespace } // namespace internal } // namespace benchmark