From ddde013bd899702a1e78dd9e31a3e703bbb173b7 Mon Sep 17 00:00:00 2001 From: Martijn Vels Date: Mon, 12 Dec 2022 18:40:32 -0800 Subject: [PATCH] Add Cord based Parse and Serialize logic to MessageLite PiperOrigin-RevId: 494887062 --- src/google/protobuf/lite_unittest.cc | 363 ++++++++++++++------------- src/google/protobuf/message_lite.cc | 115 +++++++++ src/google/protobuf/message_lite.h | 31 +++ 3 files changed, 334 insertions(+), 175 deletions(-) diff --git a/src/google/protobuf/lite_unittest.cc b/src/google/protobuf/lite_unittest.cc index 75f5446bccd0..c4a1f15ce225 100644 --- a/src/google/protobuf/lite_unittest.cc +++ b/src/google/protobuf/lite_unittest.cc @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -56,6 +57,7 @@ namespace google { namespace protobuf { +namespace { // Helper methods to test parsing merge behavior. void ExpectMessageMerged(const unittest::TestAllTypesLite& message) { @@ -73,13 +75,39 @@ void AssignParsingMergeMessages(unittest::TestAllTypesLite* msg1, msg3->set_optional_string("hello"); } +// Declare and define the template `T SerializeAs()` function which we +// specialize for the typed tests serializing to a string or Cord. +template +T SerializeAs(const MessageLite& msg); + +template <> +std::string SerializeAs(const MessageLite& msg) { + return msg.SerializeAsString(); +} + +template <> +absl::Cord SerializeAs(const MessageLite& msg) { + return msg.SerializeAsCord(); +} + +// `ParseFrom()` is overloaded for string and Cord and used in the +// typed tests parsing from string and Cord values. +bool ParseFrom(const std::string& data, MessageLite& msg) { + return msg.ParseFromString(data); +} + +bool ParseFrom(const absl::Cord& data, MessageLite& msg) { + return msg.ParseFromCord(data); +} + +template void SetAllTypesInEmptyMessageUnknownFields( unittest::TestEmptyMessageLite* empty_message) { protobuf_unittest::TestAllTypesLite message; TestUtilLite::ExpectClear(message); TestUtilLite::SetAllFields(&message); - std::string data = message.SerializeAsString(); - empty_message->ParseFromString(data); + T data = SerializeAs(message); + ParseFrom(data, *empty_message); } void SetSomeTypesInEmptyMessageUnknownFields( @@ -149,16 +177,27 @@ TEST(ParseVarintTest, Varint64) { test_value(std::numeric_limits::max(), 10); } -TEST(Lite, AllLite1) { - std::string data; +template +class LiteTest : public ::testing::Test {}; + +struct TypedTestName { + template + static std::string GetName(int /*i*/) { + return std::is_same::value ? "Cord" : "String"; + } +}; + +using SerializedDataTypes = ::testing::Types; +TYPED_TEST_SUITE(LiteTest, SerializedDataTypes, TypedTestName); +TYPED_TEST(LiteTest, AllLite1) { { protobuf_unittest::TestAllTypesLite message, message2, message3; TestUtilLite::ExpectClear(message); TestUtilLite::SetAllFields(&message); - message2.CopyFrom(message); - data = message.SerializeAsString(); - message3.ParseFromString(data); + message2 = message; + TypeParam data = SerializeAs(message2); + ParseFrom(data, message3); TestUtilLite::ExpectAllFieldsSet(message); TestUtilLite::ExpectAllFieldsSet(message2); TestUtilLite::ExpectAllFieldsSet(message3); @@ -169,15 +208,14 @@ TEST(Lite, AllLite1) { } } -TEST(Lite, AllLite2) { - std::string data; +TYPED_TEST(LiteTest, AllLite2) { { protobuf_unittest::TestAllExtensionsLite message, message2, message3; TestUtilLite::ExpectExtensionsClear(message); TestUtilLite::SetAllExtensions(&message); - message2.CopyFrom(message); - std::string extensions_data = message.SerializeAsString(); - message3.ParseFromString(extensions_data); + message2 = message; + TypeParam extensions_data = SerializeAs(message); + ParseFrom(extensions_data, message3); TestUtilLite::ExpectAllExtensionsSet(message); TestUtilLite::ExpectAllExtensionsSet(message2); TestUtilLite::ExpectAllExtensionsSet(message3); @@ -188,16 +226,16 @@ TEST(Lite, AllLite2) { } } -TEST(Lite, AllLite3) { - std::string data, packed_data; +TYPED_TEST(LiteTest, AllLite3) { + TypeParam data, packed_data; { protobuf_unittest::TestPackedTypesLite message, message2, message3; TestUtilLite::ExpectPackedClear(message); TestUtilLite::SetPackedFields(&message); - message2.CopyFrom(message); - packed_data = message.SerializeAsString(); - message3.ParseFromString(packed_data); + message2 = message; + packed_data = SerializeAs(message); + ParseFrom(packed_data, message3); TestUtilLite::ExpectPackedFieldsSet(message); TestUtilLite::ExpectPackedFieldsSet(message2); TestUtilLite::ExpectPackedFieldsSet(message3); @@ -211,10 +249,10 @@ TEST(Lite, AllLite3) { protobuf_unittest::TestPackedExtensionsLite message, message2, message3; TestUtilLite::ExpectPackedExtensionsClear(message); TestUtilLite::SetPackedExtensions(&message); - message2.CopyFrom(message); - std::string packed_extensions_data = message.SerializeAsString(); + message2 = message; + TypeParam packed_extensions_data = SerializeAs(message); EXPECT_EQ(packed_extensions_data, packed_data); - message3.ParseFromString(packed_extensions_data); + ParseFrom(packed_extensions_data, message3); TestUtilLite::ExpectPackedExtensionsSet(message); TestUtilLite::ExpectPackedExtensionsSet(message2); TestUtilLite::ExpectPackedExtensionsSet(message3); @@ -225,8 +263,8 @@ TEST(Lite, AllLite3) { } } -TEST(Lite, AllLite5) { - std::string data; +TYPED_TEST(LiteTest, AllLite5) { + TypeParam data; { // Test that if an optional or required message/group field appears multiple @@ -260,10 +298,11 @@ TEST(Lite, AllLite5) { #undef ASSIGN_REPEATED_GROUP - std::string buffer; - generator.SerializeToString(&buffer); + TypeParam buffer; + buffer = SerializeAs(generator); + // generator.SerializeToString(&buffer); unittest::TestParsingMergeLite parsing_merge; - parsing_merge.ParseFromString(buffer); + ParseFrom(buffer, parsing_merge); // Required and optional fields should be merged. ExpectMessageMerged(parsing_merge.required_all_types()); @@ -282,8 +321,8 @@ TEST(Lite, AllLite5) { } } -TEST(Lite, AllLite6) { - std::string data; +TYPED_TEST(LiteTest, AllLite6) { + TypeParam data; // Test unknown fields support for lite messages. { @@ -291,51 +330,48 @@ TEST(Lite, AllLite6) { protobuf_unittest::TestEmptyMessageLite empty_message; TestUtilLite::ExpectClear(message); TestUtilLite::SetAllFields(&message); - data = message.SerializeAsString(); - ASSERT_TRUE(empty_message.ParseFromString(data)); - data.clear(); - data = empty_message.SerializeAsString(); - EXPECT_TRUE(message2.ParseFromString(data)); - data = message2.SerializeAsString(); + data = SerializeAs(message); + ASSERT_TRUE(ParseFrom(data, empty_message)); + data = SerializeAs(empty_message); + EXPECT_TRUE(ParseFrom(data, message2)); + data = SerializeAs(message2); TestUtilLite::ExpectAllFieldsSet(message2); message.Clear(); TestUtilLite::ExpectClear(message); } } -TEST(Lite, AllLite7) { - std::string data; +TYPED_TEST(LiteTest, AllLite7) { + TypeParam data; { protobuf_unittest::TestAllExtensionsLite message, message2; protobuf_unittest::TestEmptyMessageLite empty_message; TestUtilLite::ExpectExtensionsClear(message); TestUtilLite::SetAllExtensions(&message); - data = message.SerializeAsString(); - empty_message.ParseFromString(data); - data.clear(); + data = SerializeAs(message); + ParseFrom(data, empty_message); data = empty_message.SerializeAsString(); - message2.ParseFromString(data); - data = message2.SerializeAsString(); + ParseFrom(data, message2); + data = SerializeAs(message2); TestUtilLite::ExpectAllExtensionsSet(message2); message.Clear(); TestUtilLite::ExpectExtensionsClear(message); } } -TEST(Lite, AllLite8) { - std::string data; +TYPED_TEST(LiteTest, AllLite8) { + TypeParam data; { protobuf_unittest::TestPackedTypesLite message, message2; protobuf_unittest::TestEmptyMessageLite empty_message; TestUtilLite::ExpectPackedClear(message); TestUtilLite::SetPackedFields(&message); - data = message.SerializeAsString(); - empty_message.ParseFromString(data); - data.clear(); - data = empty_message.SerializeAsString(); - message2.ParseFromString(data); + data = SerializeAs(message); + ParseFrom(data, empty_message); + data = SerializeAs(empty_message); + ParseFrom(data, message2); data = message2.SerializeAsString(); TestUtilLite::ExpectPackedFieldsSet(message2); message.Clear(); @@ -343,57 +379,56 @@ TEST(Lite, AllLite8) { } } -TEST(Lite, AllLite9) { - std::string data; +TYPED_TEST(LiteTest, AllLite9) { + TypeParam data; { protobuf_unittest::TestPackedExtensionsLite message, message2; protobuf_unittest::TestEmptyMessageLite empty_message; TestUtilLite::ExpectPackedExtensionsClear(message); TestUtilLite::SetPackedExtensions(&message); - data = message.SerializeAsString(); - empty_message.ParseFromString(data); - data.clear(); - data = empty_message.SerializeAsString(); - message2.ParseFromString(data); - data = message2.SerializeAsString(); + data = SerializeAs(message); + ParseFrom(data, empty_message); + data = SerializeAs(empty_message); + ParseFrom(data, message2); + data = SerializeAs(message2); TestUtilLite::ExpectPackedExtensionsSet(message2); message.Clear(); TestUtilLite::ExpectPackedExtensionsClear(message); } } -TEST(Lite, AllLite10) { - std::string data; +TYPED_TEST(LiteTest, AllLite10) { + TypeParam data; { // Test Unknown fields swap protobuf_unittest::TestEmptyMessageLite empty_message, empty_message2; - SetAllTypesInEmptyMessageUnknownFields(&empty_message); + SetAllTypesInEmptyMessageUnknownFields(&empty_message); SetSomeTypesInEmptyMessageUnknownFields(&empty_message2); - data = empty_message.SerializeAsString(); - std::string data2 = empty_message2.SerializeAsString(); + data = SerializeAs(empty_message); + auto data2 = SerializeAs(empty_message2); empty_message.Swap(&empty_message2); - EXPECT_EQ(data, empty_message2.SerializeAsString()); - EXPECT_EQ(data2, empty_message.SerializeAsString()); + EXPECT_EQ(data, SerializeAs(empty_message2)); + EXPECT_EQ(data2, SerializeAs(empty_message)); } } -TEST(Lite, AllLite11) { - std::string data; +TYPED_TEST(LiteTest, AllLite11) { + TypeParam data; { // Test unknown fields swap with self protobuf_unittest::TestEmptyMessageLite empty_message; - SetAllTypesInEmptyMessageUnknownFields(&empty_message); - data = empty_message.SerializeAsString(); + SetAllTypesInEmptyMessageUnknownFields(&empty_message); + data = SerializeAs(empty_message); empty_message.Swap(&empty_message); EXPECT_EQ(data, empty_message.SerializeAsString()); } } -TEST(Lite, AllLite12) { - std::string data; +TYPED_TEST(LiteTest, AllLite12) { + TypeParam data; { // Test MergeFrom with unknown fields @@ -406,24 +441,25 @@ TEST(Lite, AllLite12) { message2.add_repeated_int64(202); message2.set_optional_foreign_enum(unittest::FOREIGN_LITE_BAZ); - data = message.SerializeAsString(); - empty_message.ParseFromString(data); + data = SerializeAs(message); + ParseFrom(data, empty_message); data = message2.SerializeAsString(); - empty_message2.ParseFromString(data); + ParseFrom(data, empty_message2); message.MergeFrom(message2); empty_message.MergeFrom(empty_message2); - data = empty_message.SerializeAsString(); - message2.ParseFromString(data); + data = SerializeAs(empty_message); + ParseFrom(data, message2); // We do not compare the serialized output of a normal message and a lite // message because the order of fields do not match. We convert lite message // back into normal message, then compare. - EXPECT_EQ(message.SerializeAsString(), message2.SerializeAsString()); + EXPECT_EQ(SerializeAs(message), + SerializeAs(message2)); } } -TEST(Lite, AllLite13) { - std::string data; +TYPED_TEST(LiteTest, AllLite13StringStream) { + TypeParam data; { // Test unknown enum value @@ -442,18 +478,41 @@ TEST(Lite, AllLite13) { coded_output.WriteVarint32(20); } message.ParseFromString(buffer); - data = message.SerializeAsString(); + data = SerializeAs(message); EXPECT_EQ(data, buffer); } } -TEST(Lite, AllLite14) { - std::string data; +TYPED_TEST(LiteTest, AllLite13CordStream) { + TypeParam data; + + { + // Test unknown enum value + protobuf_unittest::TestAllTypesLite message; + io::CordOutputStream output_stream; + { + io::CodedOutputStream coded_output(&output_stream); + internal::WireFormatLite::WriteTag( + protobuf_unittest::TestAllTypesLite::kOptionalNestedEnumFieldNumber, + internal::WireFormatLite::WIRETYPE_VARINT, &coded_output); + coded_output.WriteVarint32(10); + internal::WireFormatLite::WriteTag( + protobuf_unittest::TestAllTypesLite::kRepeatedNestedEnumFieldNumber, + internal::WireFormatLite::WIRETYPE_VARINT, &coded_output); + coded_output.WriteVarint32(20); + } + absl::Cord buffer = output_stream.Consume(); + message.ParseFromCord(buffer); + data = SerializeAs(message); + EXPECT_EQ(data, buffer); + } +} +TYPED_TEST(LiteTest, AllLite14) { { // Test Clear with unknown fields protobuf_unittest::TestEmptyMessageLite empty_message; - SetAllTypesInEmptyMessageUnknownFields(&empty_message); + SetAllTypesInEmptyMessageUnknownFields(&empty_message); empty_message.Clear(); EXPECT_EQ(0, empty_message.unknown_fields().size()); } @@ -461,9 +520,7 @@ TEST(Lite, AllLite14) { // Tests for map lite ============================================= -TEST(Lite, AllLite15) { - std::string data; - +TEST(LiteBasicTest, AllLite15) { { // Accessors protobuf_unittest::TestMapLite message; @@ -476,9 +533,7 @@ TEST(Lite, AllLite15) { } } -TEST(Lite, AllLite16) { - std::string data; - +TYPED_TEST(LiteTest, AllLite16) { { // SetMapFieldsInitialized protobuf_unittest::TestMapLite message; @@ -488,9 +543,7 @@ TEST(Lite, AllLite16) { } } -TEST(Lite, AllLite17) { - std::string data; - +TEST(LiteBasicTest, AllLite17) { { // Clear protobuf_unittest::TestMapLite message; @@ -501,9 +554,7 @@ TEST(Lite, AllLite17) { } } -TEST(Lite, AllLite18) { - std::string data; - +TEST(LiteBasicTest, AllLite18) { { // ClearMessageMap protobuf_unittest::TestMessageMapLite message; @@ -513,26 +564,22 @@ TEST(Lite, AllLite18) { } } -TEST(Lite, AllLite19) { - std::string data; - +TEST(LiteBasicTest, AllLite19) { { // CopyFrom protobuf_unittest::TestMapLite message1, message2; MapLiteTestUtil::SetMapFields(&message1); - message2.CopyFrom(message1); + message2.CopyFrom(message1); // NOLINT MapLiteTestUtil::ExpectMapFieldsSet(message2); // Copying from self should be a no-op. - message2.CopyFrom(message2); + message2.CopyFrom(message2); // NOLINT MapLiteTestUtil::ExpectMapFieldsSet(message2); } } -TEST(Lite, AllLite20) { - std::string data; - +TEST(LiteBasicTest, AllLite20) { { // CopyFromMessageMap protobuf_unittest::TestMessageMapLite message1, message2; @@ -540,7 +587,7 @@ TEST(Lite, AllLite20) { (*message1.mutable_map_int32_message())[0].add_repeated_int32(100); (*message2.mutable_map_int32_message())[0].add_repeated_int32(101); - message1.CopyFrom(message2); + message1.CopyFrom(message2); // NOLINT // Checks repeated field is overwritten. EXPECT_EQ(1, message1.map_int32_message().at(0).repeated_int32_size()); @@ -548,9 +595,7 @@ TEST(Lite, AllLite20) { } } -TEST(Lite, AllLite21) { - std::string data; - +TEST(LiteBasicTest, AllLite21) { { // SwapWithEmpty protobuf_unittest::TestMapLite message1, message2; @@ -565,9 +610,7 @@ TEST(Lite, AllLite21) { } } -TEST(Lite, AllLite22) { - std::string data; - +TEST(LiteBasicTest, AllLite22) { { // SwapWithSelf protobuf_unittest::TestMapLite message; @@ -580,9 +623,7 @@ TEST(Lite, AllLite22) { } } -TEST(Lite, AllLite23) { - std::string data; - +TEST(LiteBasicTest, AllLite23) { { // SwapWithOther protobuf_unittest::TestMapLite message1, message2; @@ -597,9 +638,7 @@ TEST(Lite, AllLite23) { } } -TEST(Lite, AllLite24) { - std::string data; - +TEST(LiteBasicTest, AllLite24) { { // CopyConstructor protobuf_unittest::TestMapLite message1; @@ -610,9 +649,7 @@ TEST(Lite, AllLite24) { } } -TEST(Lite, AllLite25) { - std::string data; - +TEST(LiteBasicTest, AllLite25) { { // CopyAssignmentOperator protobuf_unittest::TestMapLite message1; @@ -628,9 +665,7 @@ TEST(Lite, AllLite25) { } } -TEST(Lite, AllLite26) { - std::string data; - +TEST(LiteBasicTest, AllLite26) { { // NonEmptyMergeFrom protobuf_unittest::TestMapLite message1, message2; @@ -650,9 +685,7 @@ TEST(Lite, AllLite26) { } } -TEST(Lite, AllLite27) { - std::string data; - +TYPED_TEST(LiteTest, AllLite27) { { // MergeFromMessageMap protobuf_unittest::TestMessageMapLite message1, message2; @@ -668,9 +701,7 @@ TEST(Lite, AllLite27) { } } -TEST(Lite, AllLite28) { - std::string data; - +TEST(LiteStringTest, AllLite28) { { // Test the generated SerializeWithCachedSizesToArray() protobuf_unittest::TestMapLite message1, message2; @@ -686,9 +717,7 @@ TEST(Lite, AllLite28) { } } -TEST(Lite, AllLite29) { - std::string data; - +TEST(LiteStreamTest, AllLite29) { { // Test the generated SerializeWithCachedSizes() protobuf_unittest::TestMapLite message1, message2; @@ -710,9 +739,7 @@ TEST(Lite, AllLite29) { } -TEST(Lite, AllLite32) { - std::string data; - +TYPED_TEST(LiteTest, AllLite32) { { // Proto2UnknownEnum protobuf_unittest::TestEnumMapPlusExtraLite from; @@ -720,21 +747,20 @@ TEST(Lite, AllLite32) { protobuf_unittest::E_PROTO2_MAP_ENUM_FOO_LITE; (*from.mutable_unknown_map_field())[0] = protobuf_unittest::E_PROTO2_MAP_ENUM_EXTRA_LITE; - std::string data; - from.SerializeToString(&data); + TypeParam data; + data = SerializeAs(from); protobuf_unittest::TestEnumMapLite to; - EXPECT_TRUE(to.ParseFromString(data)); + EXPECT_TRUE(ParseFrom(data, to)); EXPECT_EQ(0, to.unknown_map_field().size()); EXPECT_FALSE(to.mutable_unknown_fields()->empty()); ASSERT_EQ(1, to.known_map_field().size()); EXPECT_EQ(protobuf_unittest::PROTO2_MAP_ENUM_FOO_LITE, to.known_map_field().at(0)); - data.clear(); from.Clear(); - to.SerializeToString(&data); - EXPECT_TRUE(from.ParseFromString(data)); + data = SerializeAs(to); + EXPECT_TRUE(ParseFrom(data, from)); ASSERT_EQ(1, from.known_map_field().size()); EXPECT_EQ(protobuf_unittest::E_PROTO2_MAP_ENUM_FOO_LITE, from.known_map_field().at(0)); @@ -744,9 +770,7 @@ TEST(Lite, AllLite32) { } } -TEST(Lite, AllLite33) { - std::string data; - +TYPED_TEST(LiteTest, AllLite33) { { // StandardWireFormat protobuf_unittest::TestMapLite message; @@ -758,9 +782,7 @@ TEST(Lite, AllLite33) { } } -TEST(Lite, AllLite34) { - std::string data; - +TYPED_TEST(LiteTest, AllLite34) { { // UnorderedWireFormat protobuf_unittest::TestMapLite message; @@ -776,7 +798,7 @@ TEST(Lite, AllLite34) { } } -TEST(Lite, AllLite35) { +TYPED_TEST(LiteTest, AllLite35) { std::string data; { @@ -792,7 +814,7 @@ TEST(Lite, AllLite35) { } } -TEST(Lite, AllLite36) { +TYPED_TEST(LiteTest, AllLite36) { std::string data; { @@ -808,9 +830,7 @@ TEST(Lite, AllLite36) { } } -TEST(Lite, AllLite37) { - std::string data; - +TYPED_TEST(LiteTest, AllLite37) { { // MissedKeyWireFormat protobuf_unittest::TestMapLite message; @@ -826,7 +846,7 @@ TEST(Lite, AllLite37) { } } -TEST(Lite, AllLite38) { +TYPED_TEST(LiteTest, AllLite38) { std::string data; { @@ -844,9 +864,7 @@ TEST(Lite, AllLite38) { } } -TEST(Lite, AllLite39) { - std::string data; - +TYPED_TEST(LiteTest, AllLite39) { { // UnknownFieldWireFormat protobuf_unittest::TestMapLite message; @@ -860,9 +878,7 @@ TEST(Lite, AllLite39) { } } -TEST(Lite, AllLite40) { - std::string data; - +TYPED_TEST(LiteTest, AllLite40) { { // CorruptedWireFormat protobuf_unittest::TestMapLite message; @@ -874,9 +890,7 @@ TEST(Lite, AllLite40) { } } -TEST(Lite, AllLite41) { - std::string data; - +TYPED_TEST(LiteTest, AllLite41) { { // IsInitialized protobuf_unittest::TestRequiredMessageMapLite map_message; @@ -893,9 +907,7 @@ TEST(Lite, AllLite41) { } } -TEST(Lite, AllLite42) { - std::string data; - +TYPED_TEST(LiteTest, AllLite42) { { // Check that adding more values to enum does not corrupt message // when passed through an old client. @@ -926,7 +938,7 @@ TEST(Lite, AllLite42) { // Test that when parsing a oneof, we can successfully clear whatever already // happened to be stored in the oneof. -TEST(Lite, AllLite43) { +TYPED_TEST(LiteTest, AllLite43) { protobuf_unittest::TestOneofParsingLite message1; message1.set_oneof_int32(17); @@ -970,7 +982,7 @@ TEST(Lite, AllLite43) { // Verify that we can successfully parse fields of various types within oneof // fields. We also verify that we can parse the same data twice into the same // message. -TEST(Lite, AllLite44) { +TYPED_TEST(LiteTest, AllLite44) { // Int32 { protobuf_unittest::TestOneofParsingLite original; @@ -1054,7 +1066,7 @@ TEST(Lite, AllLite44) { std::cout << "PASS" << std::endl; } -TEST(Lite, AllLite45) { +TYPED_TEST(LiteTest, AllLite45) { // Test unknown fields are not discarded upon parsing. std::string data = "\20\1"; // varint 1 with field number 2 @@ -1076,7 +1088,7 @@ TEST(Lite, AllLite45) { // unpacked) state we expect. These tests specifically check for that issue by // making sure we can parse repeated fields when the tag is higher than we would // expect. -TEST(Lite, AllLite46) { +TYPED_TEST(LiteTest, AllLite46) { protobuf_unittest::PackedInt32 packed; packed.add_repeated_int32(42); std::string serialized; @@ -1088,7 +1100,7 @@ TEST(Lite, AllLite46) { EXPECT_EQ(42, non_packed.repeated_int32(0)); } -TEST(Lite, AllLite47) { +TYPED_TEST(LiteTest, AllLite47) { protobuf_unittest::NonPackedFixed32 non_packed; non_packed.add_repeated_fixed32(42); std::string serialized; @@ -1100,7 +1112,7 @@ TEST(Lite, AllLite47) { EXPECT_EQ(42, packed.repeated_fixed32(0)); } -TEST(Lite, MapCrash) { +TYPED_TEST(LiteTest, MapCrash) { // See b/113635730 Arena arena; auto msg = Arena::CreateMessage(&arena); @@ -1114,7 +1126,7 @@ TEST(Lite, MapCrash) { "\202\1\15\10\1\200\200\200\200\200\200\200\200\200\200\1")); } -TEST(Lite, CorrectEnding) { +TYPED_TEST(LiteTest, CorrectEnding) { protobuf_unittest::TestAllTypesLite msg; { // All proto wireformat parsers should act the same on parsing data in as @@ -1144,7 +1156,7 @@ TEST(Lite, CorrectEnding) { } } -TEST(Lite, DebugString) { +TYPED_TEST(LiteTest, DebugString) { protobuf_unittest::TestAllTypesLite message1, message2; EXPECT_TRUE(absl::StartsWith(message1.DebugString(), "MessageLite at 0x")); EXPECT_TRUE(absl::StartsWith(message2.DebugString(), "MessageLite at 0x")); @@ -1159,7 +1171,7 @@ TEST(Lite, DebugString) { EXPECT_NE(message1.DebugString(), message2.DebugString()); } -TEST(Lite, EnumValueToName) { +TYPED_TEST(LiteTest, EnumValueToName) { EXPECT_EQ("FOREIGN_LITE_FOO", protobuf_unittest::ForeignEnumLite_Name( protobuf_unittest::FOREIGN_LITE_FOO)); EXPECT_EQ("FOREIGN_LITE_BAR", protobuf_unittest::ForeignEnumLite_Name( @@ -1170,7 +1182,7 @@ TEST(Lite, EnumValueToName) { EXPECT_EQ("", protobuf_unittest::ForeignEnumLite_Name(999)); } -TEST(Lite, NestedEnumValueToName) { +TYPED_TEST(LiteTest, NestedEnumValueToName) { EXPECT_EQ("FOO", protobuf_unittest::TestAllTypesLite::NestedEnum_Name( protobuf_unittest::TestAllTypesLite::FOO)); EXPECT_EQ("BAR", protobuf_unittest::TestAllTypesLite::NestedEnum_Name( @@ -1181,7 +1193,7 @@ TEST(Lite, NestedEnumValueToName) { EXPECT_EQ("", protobuf_unittest::TestAllTypesLite::NestedEnum_Name(999)); } -TEST(Lite, EnumNameToValue) { +TYPED_TEST(LiteTest, EnumNameToValue) { protobuf_unittest::ForeignEnumLite value; ASSERT_TRUE( @@ -1203,7 +1215,7 @@ TEST(Lite, EnumNameToValue) { EXPECT_FALSE(protobuf_unittest::ForeignEnumLite_Parse("G", &value)); } -TEST(Lite, NestedEnumNameToValue) { +TYPED_TEST(LiteTest, NestedEnumNameToValue) { protobuf_unittest::TestAllTypesLite::NestedEnum value; ASSERT_TRUE( @@ -1227,7 +1239,7 @@ TEST(Lite, NestedEnumNameToValue) { protobuf_unittest::TestAllTypesLite::NestedEnum_Parse("G", &value)); } -TEST(Lite, AliasedEnum) { +TYPED_TEST(LiteTest, AliasedEnum) { // Enums with allow_alias = true can have multiple entries with the same // value. EXPECT_EQ("FOO1", protobuf_unittest::DupEnum::TestEnumWithDupValueLite_Name( @@ -1254,7 +1266,7 @@ TEST(Lite, AliasedEnum) { } -TEST(Lite, CodedInputStreamRollback) { +TEST(LiteBasicTest, CodedInputStreamRollback) { { protobuf_unittest::TestAllTypesLite m; m.set_optional_bytes(std::string(30, 'a')); @@ -1332,5 +1344,6 @@ TEST(Lite, CodedInputStreamRollback) { } } +} // namespace } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/message_lite.cc b/src/google/protobuf/message_lite.cc index 5e0ab8a3832a..fc0f56ac546f 100644 --- a/src/google/protobuf/message_lite.cc +++ b/src/google/protobuf/message_lite.cc @@ -36,6 +36,7 @@ #include "google/protobuf/message_lite.h" #include +#include #include #include #include @@ -47,10 +48,12 @@ #include "google/protobuf/stubs/logging.h" #include "google/protobuf/stubs/logging.h" #include "absl/strings/cord.h" +#include "absl/strings/cord_buffer.h" #include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" +#include "absl/types/optional.h" #include "google/protobuf/io/coded_stream.h" #include "google/protobuf/io/zero_copy_stream.h" #include "google/protobuf/io/zero_copy_stream_impl.h" @@ -200,6 +203,9 @@ class ZeroCopyCodedInputStream : public io::ZeroCopyInputStream { bool aliasing_enabled() { return cis_->aliasing_enabled_; } + bool ReadCord(absl::Cord* cord, int count) final { + return cis_->ReadCord(cord, count); + } private: io::CodedInputStream* cis_; }; @@ -317,6 +323,43 @@ bool MessageLite::MergeFromString(absl::string_view data) { } +namespace internal { + +template <> +struct SourceWrapper { + explicit SourceWrapper(const absl::Cord* c) : cord(c) {} + template + bool MergeInto(MessageLite* msg, MessageLite::ParseFlags parse_flags) const { + absl::optional flat = cord->TryFlat(); + if (flat && flat->size() <= ParseContext::kMaxCordBytesToCopy) { + return MergeFromImpl(*flat, msg, parse_flags); + } else { + io::CordInputStream input(cord); + return MergeFromImpl(&input, msg, parse_flags); + } + } + + const absl::Cord* const cord; +}; + +} // namespace internal + +bool MessageLite::MergeFromCord(const absl::Cord& cord) { + return ParseFrom(internal::SourceWrapper(&cord)); +} + +bool MessageLite::MergePartialFromCord(const absl::Cord& cord) { + return ParseFrom(internal::SourceWrapper(&cord)); +} + +bool MessageLite::ParseFromCord(const absl::Cord& cord) { + return ParseFrom(internal::SourceWrapper(&cord)); +} + +bool MessageLite::ParsePartialFromCord(const absl::Cord& cord) { + return ParseFrom(internal::SourceWrapper(&cord)); +} + // =================================================================== inline uint8_t* SerializeToArrayImpl(const MessageLite& msg, uint8_t* target, @@ -498,6 +541,78 @@ std::string MessageLite::SerializePartialAsString() const { return output; } +bool MessageLite::AppendToCord(absl::Cord* output) const { + GOOGLE_ABSL_DCHECK(IsInitialized()) + << InitializationErrorMessage("serialize", *this); + return AppendPartialToCord(output); +} + +bool MessageLite::AppendPartialToCord(absl::Cord* output) const { + // For efficiency, we'd like to pass a size hint to CordOutputStream with + // the exact total size expected. + const size_t size = ByteSizeLong(); + const size_t total_size = size + output->size(); + if (size > INT_MAX) { + GOOGLE_ABSL_LOG(ERROR) << "Exceeded maximum protobuf size of 2GB."; + return false; + } + + + // Allocate a CordBuffer (which may utilize private capacity in 'output'). + absl::CordBuffer buffer = output->GetAppendBuffer(size); + absl::Span available = buffer.available(); + auto target = reinterpret_cast(available.data()); + if (available.size() >= size) { + // Use EpsCopyOutputStream with full available capacity, as serialization + // may in the future use the extra slop bytes if available. + io::EpsCopyOutputStream out( + target, static_cast(available.size()), + io::CodedOutputStream::IsDefaultSerializationDeterministic()); + auto res = _InternalSerialize(target, &out); + GOOGLE_ABSL_DCHECK_EQ(res, target + size); + buffer.IncreaseLengthBy(size); + output->Append(std::move(buffer)); + GOOGLE_ABSL_DCHECK_EQ(output->size(), total_size); + return true; + } + + // Donate the buffer to the CordOutputStream with length := capacity. + // This follows the eager `EpsCopyOutputStream` initialization logic. + buffer.SetLength(buffer.capacity()); + io::CordOutputStream output_stream(std::move(*output), std::move(buffer), + total_size); + io::EpsCopyOutputStream out( + target, static_cast(available.size()), &output_stream, + io::CodedOutputStream::IsDefaultSerializationDeterministic(), &target); + target = _InternalSerialize(target, &out); + out.Trim(target); + if (out.HadError()) return false; + *output = output_stream.Consume(); + GOOGLE_ABSL_DCHECK_EQ(output->size(), total_size); + return true; +} + +bool MessageLite::SerializeToCord(absl::Cord* output) const { + output->Clear(); + return AppendToCord(output); +} + +bool MessageLite::SerializePartialToCord(absl::Cord* output) const { + output->Clear(); + return AppendPartialToCord(output); +} + +absl::Cord MessageLite::SerializeAsCord() const { + absl::Cord output; + if (!AppendToCord(&output)) output.Clear(); + return output; +} + +absl::Cord MessageLite::SerializePartialAsCord() const { + absl::Cord output; + if (!AppendPartialToCord(&output)) output.Clear(); + return output; +} namespace internal { diff --git a/src/google/protobuf/message_lite.h b/src/google/protobuf/message_lite.h index 7f266b3d649c..fade4bbaf814 100644 --- a/src/google/protobuf/message_lite.h +++ b/src/google/protobuf/message_lite.h @@ -49,6 +49,7 @@ #include "google/protobuf/port.h" #include "absl/base/call_once.h" #include "google/protobuf/stubs/logging.h" +#include "absl/strings/cord.h" #include "absl/strings/string_view.h" #include "google/protobuf/explicitly_constructed.h" #include "google/protobuf/io/coded_stream.h" @@ -371,6 +372,36 @@ class PROTOBUF_EXPORT MessageLite { // Like AppendToString(), but allows missing required fields. bool AppendPartialToString(std::string* output) const; + // Reads a protocol buffer from a Cord and merges it into this message. + bool MergeFromCord(const absl::Cord& cord); + // Like MergeFromCord(), but accepts messages that are missing + // required fields. + bool MergePartialFromCord(const absl::Cord& cord); + // Parse a protocol buffer contained in a Cord. + PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParseFromCord(const absl::Cord& cord); + // Like ParseFromCord(), but accepts messages that are missing + // required fields. + PROTOBUF_ATTRIBUTE_REINITIALIZES bool ParsePartialFromCord( + const absl::Cord& cord); + + // Serialize the message and store it in the given Cord. All required + // fields must be set. + bool SerializeToCord(absl::Cord* output) const; + // Like SerializeToCord(), but allows missing required fields. + bool SerializePartialToCord(absl::Cord* output) const; + + // Make a Cord encoding the message. Is equivalent to calling + // SerializeToCord() on a Cord and using that. Returns an empty + // Cord if SerializeToCord() would have returned an error. + absl::Cord SerializeAsCord() const; + // Like SerializeAsCord(), but allows missing required fields. + absl::Cord SerializePartialAsCord() const; + + // Like SerializeToCord(), but appends to the data to the Cord's existing + // contents. All required fields must be set. + bool AppendToCord(absl::Cord* output) const; + // Like AppendToCord(), but allows missing required fields. + bool AppendPartialToCord(absl::Cord* output) const; // Computes the serialized size of the message. This recursively calls // ByteSizeLong() on all embedded messages.