diff --git a/src/google/protobuf/lite_unittest.cc b/src/google/protobuf/lite_unittest.cc index 7693a497c8..4038a13b1a 100644 --- a/src/google/protobuf/lite_unittest.cc +++ b/src/google/protobuf/lite_unittest.cc @@ -36,6 +36,7 @@ #include #include +#include #include #include "absl/log/absl_check.h" #include "absl/strings/match.h" diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h index 5f511db330..3d2b58c87d 100644 --- a/src/google/protobuf/message.h +++ b/src/google/protobuf/message.h @@ -181,10 +181,10 @@ class CelMapReflectionFriend; // field_backed_map_impl.cc } namespace internal { +PROTOBUF_EXPORT enum class Option { None, Short, UTF8 }; class MapFieldPrinterHelper; // text_format.cc -PROTOBUF_EXPORT void PerformAbslStringify( - const Message& message, - absl::FunctionRef append); // text_format.cc +PROTOBUF_EXPORT std::string StringifyMessage(const Message& message, + Option option); // text_format.cc } // namespace internal namespace util { class MessageDifferencer; @@ -211,6 +211,14 @@ struct Metadata { const Reflection* reflection; }; +inline std::string ShortFormat(const Message& message) { + return internal::StringifyMessage(message, internal::Option::Short); +} + +inline std::string Utf8Format(const Message& message) { + return internal::StringifyMessage(message, internal::Option::UTF8); +} + namespace internal { template inline To* GetPointerAtOffset(void* message, uint32_t offset) { @@ -340,8 +348,7 @@ class PROTOBUF_EXPORT Message : public MessageLite { // Do not rely on exact format. template friend void AbslStringify(Sink& sink, const google::protobuf::Message& message) { - internal::PerformAbslStringify( - message, [&](absl::string_view content) { sink.Append(content); }); + sink.Append(StringifyMessage(message, internal::Option::None)); } // Reflection-based methods ---------------------------------------- diff --git a/src/google/protobuf/message_lite.h b/src/google/protobuf/message_lite.h index 519aa10aec..3ecc6da911 100644 --- a/src/google/protobuf/message_lite.h +++ b/src/google/protobuf/message_lite.h @@ -617,6 +617,15 @@ T* OnShutdownDelete(T* p) { } } // namespace internal + +inline std::string ShortFormat(const MessageLite& message_lite) { + return message_lite.DebugString(); +} + +inline std::string Utf8Format(const MessageLite& message_lite) { + return message_lite.DebugString(); +} + } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc index c223931f20..b1d6874e05 100644 --- a/src/google/protobuf/text_format.cc +++ b/src/google/protobuf/text_format.cc @@ -99,6 +99,13 @@ inline void IncrementRedactedFieldCounter() { num_redacted_field.fetch_add(1, std::memory_order_relaxed); } +inline void TrimTrailingSpace(std::string& debug_string) { + // Single line mode currently might have an extra space at the end. + if (!debug_string.empty() && debug_string.back() == ' ') { + debug_string.pop_back(); + } +} + } // namespace namespace internal { @@ -140,10 +147,7 @@ std::string Message::ShortDebugString() const { std::memory_order_relaxed)); printer.PrintToString(*this, &debug_string); - // Single line mode currently might have an extra space at the end. - if (!debug_string.empty() && debug_string[debug_string.size() - 1] == ' ') { - debug_string.resize(debug_string.size() - 1); - } + TrimTrailingSpace(debug_string); return debug_string; } @@ -168,20 +172,34 @@ void Message::PrintDebugString() const { printf("%s", DebugString().c_str()); } namespace internal { -PROTOBUF_EXPORT void PerformAbslStringify( - const Message& message, absl::FunctionRef append) { +PROTOBUF_EXPORT std::string StringifyMessage(const Message& message, + Option option) { // Indicate all scoped reflection calls are from DebugString function. ScopedReflectionMode scope(ReflectionMode::kDebugString); - // TODO(b/249835002): consider using the single line version for short TextFormat::Printer printer; + switch (option) { + case Option::Short: + printer.SetSingleLineMode(true); + break; + case Option::UTF8: + printer.SetUseUtf8StringEscaping(true); + break; + case Option::None: + break; + } printer.SetExpandAny(true); printer.SetRedactDebugString(true); printer.SetRandomizeDebugString(true); printer.SetRootMessageFullName(message.GetDescriptor()->full_name()); std::string result; printer.PrintToString(message, &result); - append(result); + + if (option == Option::Short) { + TrimTrailingSpace(result); + } + + return result; } } // namespace internal diff --git a/src/google/protobuf/text_format.h b/src/google/protobuf/text_format.h index 71488247a3..2bb3a46038 100644 --- a/src/google/protobuf/text_format.h +++ b/src/google/protobuf/text_format.h @@ -421,9 +421,8 @@ class PROTOBUF_EXPORT TextFormat { friend std::string Message::DebugString() const; friend std::string Message::ShortDebugString() const; friend std::string Message::Utf8DebugString() const; - friend void internal::PerformAbslStringify( - const Message& message, - absl::FunctionRef append); + friend std::string internal::StringifyMessage(const Message& message, + internal::Option option); // Sets whether silent markers will be inserted. void SetInsertSilentMarker(bool v) { insert_silent_marker_ = v; } diff --git a/src/google/protobuf/text_format_unittest.cc b/src/google/protobuf/text_format_unittest.cc index 373fc9766b..c85c014326 100644 --- a/src/google/protobuf/text_format_unittest.cc +++ b/src/google/protobuf/text_format_unittest.cc @@ -163,6 +163,105 @@ TEST_F(TextFormatTest, ShortDebugString) { } +TEST_F(TextFormatTest, ShortFormat) { + unittest::RedactedFields proto; + unittest::TestNestedMessageRedaction redacted_nested_proto; + unittest::TestNestedMessageRedaction unredacted_nested_proto; + redacted_nested_proto.set_optional_nested_string("hello"); + unredacted_nested_proto.set_optional_nested_string("world"); + proto.set_optional_redacted_string("foo"); + proto.set_optional_unredacted_string("bar"); + proto.add_repeated_redacted_string("1"); + proto.add_repeated_redacted_string("2"); + proto.add_repeated_unredacted_string("3"); + proto.add_repeated_unredacted_string("4"); + *proto.mutable_optional_redacted_message() = redacted_nested_proto; + *proto.mutable_optional_unredacted_message() = unredacted_nested_proto; + unittest::TestNestedMessageRedaction* redacted_message_1 = + proto.add_repeated_redacted_message(); + unittest::TestNestedMessageRedaction* redacted_message_2 = + proto.add_repeated_redacted_message(); + unittest::TestNestedMessageRedaction* unredacted_message_1 = + proto.add_repeated_unredacted_message(); + unittest::TestNestedMessageRedaction* unredacted_message_2 = + proto.add_repeated_unredacted_message(); + redacted_message_1->set_optional_nested_string("5"); + redacted_message_2->set_optional_nested_string("6"); + unredacted_message_1->set_optional_nested_string("7"); + unredacted_message_2->set_optional_nested_string("8"); + (*proto.mutable_map_redacted_string())["abc"] = "def"; + (*proto.mutable_map_unredacted_string())["ghi"] = "jkl"; + + EXPECT_THAT( + google::protobuf::ShortFormat(proto), + testing::MatchesRegex( + "optional_redacted_string: \\[REDACTED\\] " + "optional_unredacted_string: \"bar\" " + "repeated_redacted_string: \\[REDACTED\\] " + "repeated_redacted_string: \\[REDACTED\\] " + "repeated_unredacted_string: \"3\" " + "repeated_unredacted_string: \"4\" " + "optional_redacted_message: \\[REDACTED\\] " + "optional_unredacted_message \\{ " + "optional_nested_string: \"world\" \\} " + "repeated_redacted_message: \\[REDACTED\\] " + "repeated_unredacted_message \\{ optional_nested_string: \"7\" \\} " + "repeated_unredacted_message \\{ optional_nested_string: \"8\" \\} " + "map_redacted_string: \\[REDACTED\\] " + "map_unredacted_string \\{ key: \"ghi\" value: \"jkl\" \\}")); +} + +TEST_F(TextFormatTest, Utf8Format) { + unittest::RedactedFields proto; + unittest::TestNestedMessageRedaction redacted_nested_proto; + unittest::TestNestedMessageRedaction unredacted_nested_proto; + redacted_nested_proto.set_optional_nested_string("\350\260\267\346\255\214"); + unredacted_nested_proto.set_optional_nested_string( + "\350\260\267\346\255\214"); + proto.set_optional_redacted_string("foo"); + proto.set_optional_unredacted_string("bar"); + proto.add_repeated_redacted_string("1"); + proto.add_repeated_redacted_string("2"); + proto.add_repeated_unredacted_string("3"); + proto.add_repeated_unredacted_string("4"); + *proto.mutable_optional_redacted_message() = redacted_nested_proto; + *proto.mutable_optional_unredacted_message() = unredacted_nested_proto; + unittest::TestNestedMessageRedaction* redacted_message_1 = + proto.add_repeated_redacted_message(); + unittest::TestNestedMessageRedaction* redacted_message_2 = + proto.add_repeated_redacted_message(); + unittest::TestNestedMessageRedaction* unredacted_message_1 = + proto.add_repeated_unredacted_message(); + unittest::TestNestedMessageRedaction* unredacted_message_2 = + proto.add_repeated_unredacted_message(); + redacted_message_1->set_optional_nested_string("5"); + redacted_message_2->set_optional_nested_string("6"); + unredacted_message_1->set_optional_nested_string("7"); + unredacted_message_2->set_optional_nested_string("8"); + (*proto.mutable_map_redacted_string())["abc"] = "def"; + (*proto.mutable_map_unredacted_string())["ghi"] = "jkl"; + + EXPECT_THAT(google::protobuf::Utf8Format(proto), + testing::MatchesRegex( + "optional_redacted_string: \\[REDACTED\\]\n" + "optional_unredacted_string: \"bar\"\n" + "repeated_redacted_string: \\[REDACTED\\]\n" + "repeated_redacted_string: \\[REDACTED\\]\n" + "repeated_unredacted_string: \"3\"\n" + "repeated_unredacted_string: \"4\"\n" + "optional_redacted_message: \\[REDACTED\\]\n" + "optional_unredacted_message \\{\n " + "optional_nested_string: \"\xE8\xB0\xB7\xE6\xAD\x8C\"\n\\}\n" + "repeated_redacted_message: \\[REDACTED\\]\n" + "repeated_unredacted_message \\{\n " + "optional_nested_string: \"7\"\n\\}\n" + "repeated_unredacted_message \\{\n " + "optional_nested_string: \"8\"\n\\}\n" + "map_redacted_string: \\[REDACTED\\]\n" + "map_unredacted_string \\{\n " + "key: \"ghi\"\n value: \"jkl\"\n\\}\n")); +} + TEST_F(TextFormatTest, ShortPrimitiveRepeateds) { proto_.set_optional_int32(123); proto_.add_repeated_int32(456); diff --git a/src/google/protobuf/unittest.proto b/src/google/protobuf/unittest.proto index deb9d3119f..b7d4182c6e 100644 --- a/src/google/protobuf/unittest.proto +++ b/src/google/protobuf/unittest.proto @@ -1663,8 +1663,22 @@ message BadFieldNames{ optional int32 for = 2; } -message RedactedFields{ +message TestNestedMessageRedaction { + optional string optional_nested_string = 1; +} + +message RedactedFields { optional string optional_redacted_string = 1 [debug_redact = true]; + optional string optional_unredacted_string = 2; + repeated string repeated_redacted_string = 3 [debug_redact = true]; + repeated string repeated_unredacted_string = 4; + optional TestNestedMessageRedaction optional_redacted_message = 5 [debug_redact = true]; + optional TestNestedMessageRedaction optional_unredacted_message = 6; + repeated TestNestedMessageRedaction repeated_redacted_message = 7 + [debug_redact = true]; + repeated TestNestedMessageRedaction repeated_unredacted_message = 8; + map map_redacted_string = 9 [debug_redact = true]; + map map_unredacted_string = 10; } message TestCord{