From 6ecb5d097e8d9bfafeb5ec8d251827f0d444f2ce Mon Sep 17 00:00:00 2001 From: Jie Luo Date: Fri, 24 Mar 2023 15:01:14 -0700 Subject: [PATCH] Added Reflection::GetCord() method in C++ PiperOrigin-RevId: 519246549 --- .../java/com/google/protobuf/TestUtil.java | 3 + .../cpp/field_generators/string_field.cc | 4 +- src/google/protobuf/compiler/cpp/helpers.cc | 22 +- src/google/protobuf/compiler/cpp/helpers.h | 12 +- src/google/protobuf/compiler/cpp/unittest.inc | 3 + src/google/protobuf/descriptor.h | 15 ++ .../protobuf/generated_message_reflection.cc | 130 ++++++++-- .../generated_message_reflection_unittest.cc | 239 ++++++++++++++++++ src/google/protobuf/message.h | 8 +- src/google/protobuf/port_def.inc | 4 - src/google/protobuf/test_util.inc | 2 + src/google/protobuf/unittest.proto | 2 +- 12 files changed, 392 insertions(+), 52 deletions(-) diff --git a/java/core/src/test/java/com/google/protobuf/TestUtil.java b/java/core/src/test/java/com/google/protobuf/TestUtil.java index 71a540a0c488..a83a62c3790a 100644 --- a/java/core/src/test/java/com/google/protobuf/TestUtil.java +++ b/java/core/src/test/java/com/google/protobuf/TestUtil.java @@ -2653,6 +2653,9 @@ public static void assertAtMostOneFieldSetOneof(TestOneof2 message) { case FOO_LAZY_MESSAGE: Assert.assertTrue(message.hasFooLazyMessage()); break; + case FOO_BYTES_CORD: + Assert.assertTrue(message.hasFooBytesCord()); + break; case FOO_NOT_SET: break; // TODO(b/18683919): go/enum-switch-lsc diff --git a/src/google/protobuf/compiler/cpp/field_generators/string_field.cc b/src/google/protobuf/compiler/cpp/field_generators/string_field.cc index 21d01ccff4a9..bdefe09eca27 100644 --- a/src/google/protobuf/compiler/cpp/field_generators/string_field.cc +++ b/src/google/protobuf/compiler/cpp/field_generators/string_field.cc @@ -206,7 +206,7 @@ void SingularString::GenerateAccessorDeclarations(io::Printer* p) const { // reflection interface since the reflection interface is independent of // the string's underlying representation. bool unknown_ctype = - field_->options().ctype() != EffectiveStringCType(field_, options_); + field_->options().ctype() != internal::cpp::EffectiveStringCType(field_); if (unknown_ctype) { p->Emit(R"cc( @@ -759,7 +759,7 @@ class RepeatedString : public FieldGeneratorBase { void RepeatedString::GenerateAccessorDeclarations(io::Printer* p) const { bool unknown_ctype = - field_->options().ctype() != EffectiveStringCType(field_, options_); + field_->options().ctype() != internal::cpp::EffectiveStringCType(field_); if (unknown_ctype) { p->Emit(R"cc( diff --git a/src/google/protobuf/compiler/cpp/helpers.cc b/src/google/protobuf/compiler/cpp/helpers.cc index 3970389a6f31..830846e05088 100644 --- a/src/google/protobuf/compiler/cpp/helpers.cc +++ b/src/google/protobuf/compiler/cpp/helpers.cc @@ -978,7 +978,8 @@ bool HasRepeatedFields(const FileDescriptor* file) { static bool IsStringPieceField(const FieldDescriptor* field, const Options& options) { return field->cpp_type() == FieldDescriptor::CPPTYPE_STRING && - EffectiveStringCType(field, options) == FieldOptions::STRING_PIECE; + internal::cpp::EffectiveStringCType(field) == + FieldOptions::STRING_PIECE; } static bool HasStringPieceFields(const Descriptor* descriptor, @@ -1001,7 +1002,7 @@ bool HasStringPieceFields(const FileDescriptor* file, const Options& options) { static bool IsCordField(const FieldDescriptor* field, const Options& options) { return field->cpp_type() == FieldDescriptor::CPPTYPE_STRING && - EffectiveStringCType(field, options) == FieldOptions::CORD; + internal::cpp::EffectiveStringCType(field) == FieldOptions::CORD; } static bool HasCordFields(const Descriptor* descriptor, @@ -1118,23 +1119,6 @@ bool IsStringOrMessage(const FieldDescriptor* field) { return false; } -FieldOptions::CType EffectiveStringCType(const FieldDescriptor* field, - const Options& options) { - ABSL_DCHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_STRING); - if (options.opensource_runtime) { - // Open-source protobuf release only supports STRING ctype and CORD for - // sinuglar bytes. - if (field->type() == FieldDescriptor::TYPE_BYTES && !field->is_repeated() && - field->options().ctype() == FieldOptions::CORD) { - return FieldOptions::CORD; - } - return FieldOptions::STRING; - } else { - // Google-internal supports all ctypes. - return field->options().ctype(); - } -} - bool IsAnyMessage(const FileDescriptor* descriptor, const Options& options) { return descriptor->name() == kAnyProtoFile; } diff --git a/src/google/protobuf/compiler/cpp/helpers.h b/src/google/protobuf/compiler/cpp/helpers.h index fd2ea7b210a3..56b599ab34b5 100644 --- a/src/google/protobuf/compiler/cpp/helpers.h +++ b/src/google/protobuf/compiler/cpp/helpers.h @@ -346,25 +346,21 @@ bool IsProfileDriven(const Options& options); bool IsStringInlined(const FieldDescriptor* descriptor, const Options& options); -// For a string field, returns the effective ctype. If the actual ctype is -// not supported, returns the default of STRING. -FieldOptions::CType EffectiveStringCType(const FieldDescriptor* field, - const Options& options); - inline bool IsCord(const FieldDescriptor* field, const Options& options) { return field->cpp_type() == FieldDescriptor::CPPTYPE_STRING && - EffectiveStringCType(field, options) == FieldOptions::CORD; + internal::cpp::EffectiveStringCType(field) == FieldOptions::CORD; } inline bool IsString(const FieldDescriptor* field, const Options& options) { return field->cpp_type() == FieldDescriptor::CPPTYPE_STRING && - EffectiveStringCType(field, options) == FieldOptions::STRING; + internal::cpp::EffectiveStringCType(field) == FieldOptions::STRING; } inline bool IsStringPiece(const FieldDescriptor* field, const Options& options) { return field->cpp_type() == FieldDescriptor::CPPTYPE_STRING && - EffectiveStringCType(field, options) == FieldOptions::STRING_PIECE; + internal::cpp::EffectiveStringCType(field) == + FieldOptions::STRING_PIECE; } // Does the given FileDescriptor use lazy fields? diff --git a/src/google/protobuf/compiler/cpp/unittest.inc b/src/google/protobuf/compiler/cpp/unittest.inc index dd5829cedeec..bf434f55ddf8 100644 --- a/src/google/protobuf/compiler/cpp/unittest.inc +++ b/src/google/protobuf/compiler/cpp/unittest.inc @@ -1452,6 +1452,9 @@ class OneofTest : public testing::Test { case UNITTEST::TestOneof2::kFooLazyMessage: EXPECT_TRUE(message.has_foo_lazy_message()); break; + case UNITTEST::TestOneof2::kFooBytesCord: + EXPECT_TRUE(message.has_foo_bytes_cord()); + break; case UNITTEST::TestOneof2::FOO_NOT_SET: break; } diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h index 87ae9edb28c9..68434eed3514 100644 --- a/src/google/protobuf/descriptor.h +++ b/src/google/protobuf/descriptor.h @@ -2645,6 +2645,21 @@ PROTOBUF_EXPORT bool HasPreservingUnknownEnumSemantics( PROTOBUF_EXPORT bool HasHasbit(const FieldDescriptor* field); +// For a string field, returns the effective ctype. If the actual ctype is +// not supported, returns the default of STRING. +template +typename FO::CType EffectiveStringCType(const FD* field) { + ABSL_DCHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_STRING); + ABSL_DCHECK(!field->is_extension()); + // Open-source protobuf release only supports STRING ctype and CORD for + // sinuglar bytes. + if (field->type() == FieldDescriptor::TYPE_BYTES && !field->is_repeated() && + field->options().ctype() == FO::CORD) { + return FO::CORD; + } + return FO::STRING; +} + #ifndef SWIG enum class Utf8CheckMode { kStrict = 0, // Parsing will fail if non UTF-8 data is in string fields. diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc index e7cb11f2f770..626672b515e6 100644 --- a/src/google/protobuf/generated_message_reflection.cc +++ b/src/google/protobuf/generated_message_reflection.cc @@ -403,8 +403,20 @@ size_t Reflection::SpaceUsedLong(const Message& message) const { break; case FieldDescriptor::CPPTYPE_STRING: { - switch (field->options().ctype()) { - default: // TODO(kenton): Support other string reps. + switch (internal::cpp::EffectiveStringCType(field)) { + case FieldOptions::CORD: + if (schema_.InRealOneof(field)) { + total_size += GetField(message, field) + ->EstimatedMemoryUsage(); + + } else { + // sizeof(absl::Cord) is included to self. + total_size += GetField(message, field) + .EstimatedMemoryUsage() - + sizeof(absl::Cord); + } + break; + default: case FieldOptions::STRING: if (IsInlined(field)) { const std::string* ptr = @@ -501,7 +513,10 @@ struct OneofFieldMover { to->SetString(from->GetString()); break; } - switch (field->options().ctype()) { + switch (internal::cpp::EffectiveStringCType(field)) { + case FieldOptions::CORD: + to->SetCord(from->GetCord()); + break; default: case FieldOptions::STRING: { to->SetArenaStringPtr(from->GetArenaStringPtr()); @@ -635,7 +650,12 @@ template void SwapFieldHelper::SwapStringField(const Reflection* r, Message* lhs, Message* rhs, const FieldDescriptor* field) { - switch (field->options().ctype()) { + switch (internal::cpp::EffectiveStringCType(field)) { + case FieldOptions::CORD: + // Always shallow swap for Cord. + std::swap(*r->MutableRaw(lhs, field), + *r->MutableRaw(rhs, field)); + break; default: case FieldOptions::STRING: { if (r->IsInlined(field)) { @@ -891,6 +911,7 @@ void Reflection::SwapOneofField(Message* lhs, Message* rhs, LOCAL_VAR_ACCESSOR(int, enum, Enum); LOCAL_VAR_ACCESSOR(Message*, message, Message); LOCAL_VAR_ACCESSOR(ArenaStringPtr, arena_string_ptr, ArenaStringPtr); + LOCAL_VAR_ACCESSOR(absl::Cord*, cord, Cord); const std::string& GetString() const { return string_val; } void SetString(const std::string& v) { string_val = v; } Message* UnsafeGetMessage() const { return GetMessage(); } @@ -908,6 +929,7 @@ void Reflection::SwapOneofField(Message* lhs, Message* rhs, int type_enum; Message* type_message; internal::ArenaStringPtr type_arena_string_ptr; + absl::Cord* type_cord; } oneof_val; // std::string cannot be in union. @@ -931,6 +953,7 @@ void Reflection::SwapOneofField(Message* lhs, Message* rhs, MESSAGE_FIELD_ACCESSOR(bool, bool, Bool); MESSAGE_FIELD_ACCESSOR(int, enum, Enum); MESSAGE_FIELD_ACCESSOR(ArenaStringPtr, arena_string_ptr, ArenaStringPtr); + MESSAGE_FIELD_ACCESSOR(absl::Cord*, cord, Cord); std::string GetString() const { return reflection->GetString(*message, field); } @@ -1321,8 +1344,16 @@ void Reflection::ClearField(Message* message, break; case FieldDescriptor::CPPTYPE_STRING: { - switch (field->options().ctype()) { - default: // TODO(kenton): Support other string reps. + switch (internal::cpp::EffectiveStringCType(field)) { + case FieldOptions::CORD: + if (field->has_default_value()) { + *MutableRaw(message, field) = + field->default_value_string(); + } else { + MutableRaw(message, field)->Clear(); + } + break; + default: case FieldOptions::STRING: if (IsInlined(field)) { // Currently, string with default value can't be inlined. So we @@ -1718,8 +1749,14 @@ std::string Reflection::GetString(const Message& message, if (schema_.InRealOneof(field) && !HasOneofField(message, field)) { return field->default_value_string(); } - switch (field->options().ctype()) { - default: // TODO(kenton): Support other string reps. + switch (internal::cpp::EffectiveStringCType(field)) { + case FieldOptions::CORD: + if (schema_.InRealOneof(field)) { + return std::string(*GetField(message, field)); + } else { + return std::string(GetField(message, field)); + } + default: case FieldOptions::STRING: if (IsInlined(field)) { return GetField(message, field).GetNoArena(); @@ -1743,8 +1780,16 @@ const std::string& Reflection::GetStringReference(const Message& message, if (schema_.InRealOneof(field) && !HasOneofField(message, field)) { return field->default_value_string(); } - switch (field->options().ctype()) { - default: // TODO(kenton): Support other string reps. + switch (internal::cpp::EffectiveStringCType(field)) { + case FieldOptions::CORD: + if (schema_.InRealOneof(field)) { + absl::CopyCordToString(*GetField(message, field), + scratch); + } else { + absl::CopyCordToString(GetField(message, field), scratch); + } + return *scratch; + default: case FieldOptions::STRING: if (IsInlined(field)) { return GetField(message, field).GetNoArena(); @@ -1756,6 +1801,39 @@ const std::string& Reflection::GetStringReference(const Message& message, } } +absl::Cord Reflection::GetCord(const Message& message, + const FieldDescriptor* field) const { + USAGE_CHECK_ALL(GetCord, SINGULAR, STRING); + if (field->is_extension()) { + return absl::Cord(GetExtensionSet(message).GetString( + field->number(), field->default_value_string())); + } else { + if (schema_.InRealOneof(field) && !HasOneofField(message, field)) { + return absl::Cord(field->default_value_string()); + } + switch (internal::cpp::EffectiveStringCType(field)) { + case FieldOptions::CORD: + if (schema_.InRealOneof(field)) { + return *GetField(message, field); + } else { + return GetField(message, field); + } + default: + case FieldOptions::STRING: + if (IsInlined(field)) { + return absl::Cord( + GetField(message, field).GetNoArena()); + } else { + const auto& str = GetField(message, field); + return absl::Cord(str.IsDefault() ? field->default_value_string() + : str.Get()); + } + } + + ABSL_LOG(FATAL) << "Can't get here."; + return absl::Cord(); // Make compiler happy. + } +} void Reflection::SetString(Message* message, const FieldDescriptor* field, @@ -1765,8 +1843,20 @@ void Reflection::SetString(Message* message, const FieldDescriptor* field, return MutableExtensionSet(message)->SetString( field->number(), field->type(), std::move(value), field); } else { - switch (field->options().ctype()) { - default: // TODO(kenton): Support other string reps. + switch (internal::cpp::EffectiveStringCType(field)) { + case FieldOptions::CORD: + if (schema_.InRealOneof(field)) { + if (!HasOneofField(*message, field)) { + ClearOneof(message, field->containing_oneof()); + *MutableField(message, field) = + Arena::Create(message->GetArenaForAllocation()); + } + *(*MutableField(message, field)) = value; + break; + } + *MutableField(message, field) = value; + break; + default: case FieldOptions::STRING: { if (IsInlined(field)) { const uint32_t index = schema_.InlinedStringIndex(field); @@ -1805,7 +1895,7 @@ void Reflection::SetString(Message* message, const FieldDescriptor* field, MutableExtensionSet(message)->MutableString( field->number(), field->type(), field)); } else { - switch (field->options().ctype()) { + switch (internal::cpp::EffectiveStringCType(field)) { case FieldOptions::CORD: if (schema_.InRealOneof(field)) { if (!HasOneofField(*message, field)) { @@ -2721,8 +2811,11 @@ bool Reflection::HasBit(const Message& message, // (which uses HasField()) needs to be consistent with this. switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_STRING: - switch (field->options().ctype()) { - default: { + switch (internal::cpp::EffectiveStringCType(field)) { + case FieldOptions::CORD: + return !GetField(message, field).empty(); + default: + case FieldOptions::STRING: { if (IsInlined(field)) { return !GetField(message, field) .GetNoArena() @@ -2833,8 +2926,11 @@ void Reflection::ClearOneof(Message* message, if (message->GetArenaForAllocation() == nullptr) { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_STRING: { - switch (field->options().ctype()) { - default: // TODO(kenton): Support other string reps. + switch (internal::cpp::EffectiveStringCType(field)) { + case FieldOptions::CORD: + delete *MutableRaw(message, field); + break; + default: case FieldOptions::STRING: { // Oneof string fields are never set as a default instance. // We just need to pass some arbitrary default string to make it diff --git a/src/google/protobuf/generated_message_reflection_unittest.cc b/src/google/protobuf/generated_message_reflection_unittest.cc index 3de242c2316c..4c8e0ab7098f 100644 --- a/src/google/protobuf/generated_message_reflection_unittest.cc +++ b/src/google/protobuf/generated_message_reflection_unittest.cc @@ -165,6 +165,22 @@ TEST(GeneratedMessageReflectionTest, GetStringReference) { "a reference to the underlying string."; } +TEST(GeneratedMessageReflectionTest, GetStringReferenceCopy) { + // Test that GetStringReference() returns the scratch string when the + // underlying representation is not a normal string. + unittest::TestCord cord_message; + cord_message.set_optional_bytes_cord("bytes_cord"); + + const Reflection* cord_reflection = cord_message.GetReflection(); + const Descriptor* descriptor = unittest::TestCord::descriptor(); + std::string cord_scratch; + EXPECT_EQ( + &cord_scratch, + &cord_reflection->GetStringReference( + cord_message, descriptor->FindFieldByName("optional_bytes_cord"), + &cord_scratch)); +} + class GeneratedMessageReflectionSwapTest : public testing::TestWithParam { protected: @@ -981,6 +997,8 @@ TEST(GeneratedMessageReflectionTest, Oneof) { EXPECT_NE(&unittest::TestOneof2::FooGroup::default_instance(), &reflection->GetMessage( message, descriptor->FindFieldByName("foo_lazy_message"))); + EXPECT_EQ("", reflection->GetString( + message, descriptor->FindFieldByName("foo_bytes_cord"))); EXPECT_EQ( 5, reflection->GetInt32(message, descriptor->FindFieldByName("bar_int"))); EXPECT_EQ("STRING", reflection->GetString( @@ -1009,6 +1027,11 @@ TEST(GeneratedMessageReflectionTest, Oneof) { "bytes"); EXPECT_EQ("bytes", reflection->GetString( message, descriptor->FindFieldByName("foo_bytes"))); + reflection->SetString(&message, descriptor->FindFieldByName("foo_bytes_cord"), + "bytes_cord"); + EXPECT_EQ("bytes_cord", + reflection->GetString( + message, descriptor->FindFieldByName("foo_bytes_cord"))); reflection->SetString(&message, descriptor->FindFieldByName("bar_cord"), "change_cord"); EXPECT_EQ( @@ -1321,6 +1344,222 @@ TEST(GeneratedMessageReflectionTest, UsageErrors) { #endif // GTEST_HAS_DEATH_TEST +class GeneratedMessageReflectionCordAccessorsTest : public testing::Test { + protected: + const FieldDescriptor* optional_string_; + const FieldDescriptor* optional_string_piece_; + const FieldDescriptor* optional_cord_; + const FieldDescriptor* repeated_string_; + const FieldDescriptor* repeated_string_piece_; + const FieldDescriptor* repeated_cord_; + const FieldDescriptor* default_string_; + const FieldDescriptor* default_string_piece_; + const FieldDescriptor* default_cord_; + + const FieldDescriptor* optional_string_extension_; + const FieldDescriptor* repeated_string_extension_; + + unittest::TestAllTypes message_; + const Reflection* reflection_; + unittest::TestAllExtensions extensions_message_; + const Reflection* extensions_reflection_; + + void SetUp() override { + const Descriptor* descriptor = unittest::TestAllTypes::descriptor(); + + optional_string_ = descriptor->FindFieldByName("optional_string"); + optional_string_piece_ = + descriptor->FindFieldByName("optional_string_piece"); + optional_cord_ = descriptor->FindFieldByName("optional_cord"); + repeated_string_ = descriptor->FindFieldByName("repeated_string"); + repeated_string_piece_ = + descriptor->FindFieldByName("repeated_string_piece"); + repeated_cord_ = descriptor->FindFieldByName("repeated_cord"); + default_string_ = descriptor->FindFieldByName("default_string"); + default_string_piece_ = descriptor->FindFieldByName("default_string_piece"); + default_cord_ = descriptor->FindFieldByName("default_cord"); + + optional_string_extension_ = + descriptor->file()->FindExtensionByName("optional_string_extension"); + repeated_string_extension_ = + descriptor->file()->FindExtensionByName("repeated_string_extension"); + + ASSERT_TRUE(optional_string_ != nullptr); + ASSERT_TRUE(optional_string_piece_ != nullptr); + ASSERT_TRUE(optional_cord_ != nullptr); + ASSERT_TRUE(repeated_string_ != nullptr); + ASSERT_TRUE(repeated_string_piece_ != nullptr); + ASSERT_TRUE(repeated_cord_ != nullptr); + ASSERT_TRUE(optional_string_extension_ != nullptr); + ASSERT_TRUE(repeated_string_extension_ != nullptr); + + reflection_ = message_.GetReflection(); + extensions_reflection_ = extensions_message_.GetReflection(); + } +}; + +TEST_F(GeneratedMessageReflectionCordAccessorsTest, GetCord) { + message_.set_optional_string("foo"); + + extensions_message_.SetExtension(unittest::optional_string_extension, "moo"); + + EXPECT_EQ("foo", reflection_->GetCord(message_, optional_string_)); + EXPECT_EQ("moo", extensions_reflection_->GetCord(extensions_message_, + optional_string_extension_)); + + EXPECT_EQ("hello", reflection_->GetCord(message_, default_string_)); + EXPECT_EQ("abc", reflection_->GetCord(message_, default_string_piece_)); + EXPECT_EQ("123", reflection_->GetCord(message_, default_cord_)); + + + unittest::TestCord message; + const Descriptor* descriptor = unittest::TestCord::descriptor(); + const Reflection* reflection = message.GetReflection(); + + message.set_optional_bytes_cord("bytes_cord"); + EXPECT_EQ("bytes_cord", + reflection->GetCord( + message, descriptor->FindFieldByName("optional_bytes_cord"))); +} + +TEST_F(GeneratedMessageReflectionCordAccessorsTest, GetOneofCord) { + unittest::TestOneof2 message; + const Descriptor* descriptor = unittest::TestOneof2::descriptor(); + const Reflection* reflection = message.GetReflection(); + + message.set_foo_bytes_cord("bytes_cord"); + EXPECT_EQ("bytes_cord", + reflection->GetCord(message, + descriptor->FindFieldByName("foo_bytes_cord"))); + + message.set_foo_string("foo"); + EXPECT_EQ("foo", reflection->GetCord( + message, descriptor->FindFieldByName("foo_string"))); + + message.set_foo_bytes("bytes"); + EXPECT_EQ("bytes", reflection->GetCord( + message, descriptor->FindFieldByName("foo_bytes"))); + +} + +TEST_F(GeneratedMessageReflectionCordAccessorsTest, SetStringFromCord) { + reflection_->SetString(&message_, optional_string_, absl::Cord("foo")); + reflection_->SetString(&message_, optional_string_piece_, absl::Cord("bar")); + reflection_->SetString(&message_, optional_cord_, absl::Cord("baz")); + extensions_reflection_->SetString( + &extensions_message_, optional_string_extension_, absl::Cord("moo")); + + EXPECT_TRUE(message_.has_optional_string()); + EXPECT_TRUE(message_.has_optional_string_piece()); + EXPECT_TRUE(message_.has_optional_cord()); + EXPECT_TRUE( + extensions_message_.HasExtension(unittest::optional_string_extension)); + + EXPECT_EQ("foo", message_.optional_string()); + EXPECT_EQ("bar", std::string( + reflection_->GetCord(message_, optional_string_piece_))); + EXPECT_EQ("baz", std::string(reflection_->GetCord(message_, optional_cord_))); + EXPECT_EQ("moo", extensions_message_.GetExtension( + unittest::optional_string_extension)); + + unittest::TestCord message; + const Descriptor* descriptor = unittest::TestCord::descriptor(); + const Reflection* reflection = message.GetReflection(); + + reflection->SetString(&message, + descriptor->FindFieldByName("optional_bytes_cord"), + absl::Cord("cord")); + EXPECT_TRUE(message.has_optional_bytes_cord()); + EXPECT_EQ("cord", message.optional_bytes_cord()); +} + +TEST_F(GeneratedMessageReflectionCordAccessorsTest, SetOneofStringFromCord) { + unittest::TestOneof2 message; + const Descriptor* descriptor = unittest::TestOneof2::descriptor(); + const Reflection* reflection = message.GetReflection(); + + reflection->SetString(&message, descriptor->FindFieldByName("foo_string"), + absl::Cord("foo")); + EXPECT_TRUE(message.has_foo_string()); + EXPECT_EQ("foo", message.foo_string()); + + reflection->SetString(&message, descriptor->FindFieldByName("foo_bytes"), + absl::Cord("bytes")); + EXPECT_TRUE(message.has_foo_bytes()); + EXPECT_EQ("bytes", message.foo_bytes()); + + reflection->SetString(&message, descriptor->FindFieldByName("foo_cord"), + absl::Cord("cord")); + EXPECT_EQ("cord", std::string(reflection->GetCord( + message, descriptor->FindFieldByName("foo_cord")))); + + reflection->SetString(&message, + descriptor->FindFieldByName("foo_string_piece"), + absl::Cord("string_piece")); + EXPECT_EQ("string_piece", + reflection->GetCord( + message, descriptor->FindFieldByName("foo_string_piece"))); + + reflection->SetString(&message, descriptor->FindFieldByName("foo_bytes_cord"), + absl::Cord("bytes_cord")); + EXPECT_TRUE(message.has_foo_bytes_cord()); + EXPECT_EQ("bytes_cord", message.foo_bytes_cord()); +} + +TEST_F(GeneratedMessageReflectionCordAccessorsTest, CordSingularBytes) { + unittest::TestCord message; + absl::Cord cord_value("test"); + message.set_optional_bytes_cord(cord_value); + EXPECT_EQ("test", message.optional_bytes_cord()); + + EXPECT_TRUE(message.has_optional_bytes_cord()); + message.clear_optional_bytes_cord(); + EXPECT_FALSE(message.has_optional_bytes_cord()); + + std::string string_value = "test"; + message.set_optional_bytes_cord(string_value); + EXPECT_EQ("test", message.optional_bytes_cord()); +} + +TEST_F(GeneratedMessageReflectionCordAccessorsTest, CordSingularBytesDefault) { + unittest::TestCord message; + EXPECT_EQ("hello", message.optional_bytes_cord_default()); + absl::Cord cord_value("world"); + message.set_optional_bytes_cord_default(cord_value); + EXPECT_EQ("world", message.optional_bytes_cord_default()); + message.clear_optional_bytes_cord_default(); + EXPECT_EQ("hello", message.optional_bytes_cord_default()); +} + +TEST_F(GeneratedMessageReflectionCordAccessorsTest, CordSingularOneofBytes) { + unittest::TestOneof2 message; + absl::Cord cord_value("test"); + message.set_foo_bytes_cord(cord_value); + EXPECT_EQ("test", message.foo_bytes_cord()); + + EXPECT_TRUE(message.has_foo_bytes_cord()); + message.clear_foo(); + EXPECT_FALSE(message.has_foo_bytes_cord()); + + std::string string_value = "test"; + message.set_foo_bytes_cord(string_value); + EXPECT_EQ("test", message.foo_bytes_cord()); + EXPECT_TRUE(message.has_foo_bytes_cord()); +} + +TEST_F(GeneratedMessageReflectionCordAccessorsTest, ClearOneofCord) { + unittest::TestOneof2 message; + absl::Cord cord_value("test"); + message.set_foo_bytes_cord(cord_value); + + const Descriptor* descriptor = unittest::TestOneof2::descriptor(); + const Reflection* reflection = message.GetReflection(); + + EXPECT_TRUE(message.has_foo_bytes_cord()); + reflection->ClearOneof(&message, descriptor->FindOneofByName("foo")); + EXPECT_FALSE(message.has_foo_bytes_cord()); +} + using internal::IsDescendant; diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h index ac7fbe5ac086..549f24a19510 100644 --- a/src/google/protobuf/message.h +++ b/src/google/protobuf/message.h @@ -122,6 +122,7 @@ #include "absl/base/call_once.h" #include "absl/base/casts.h" #include "absl/functional/function_ref.h" +#include "absl/strings/cord.h" #include "absl/strings/string_view.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/generated_message_reflection.h" @@ -131,7 +132,6 @@ #include "google/protobuf/message_lite.h" #include "google/protobuf/port.h" - // Must be included last. #include "google/protobuf/port_def.inc" @@ -630,6 +630,12 @@ class PROTOBUF_EXPORT Reflection final { const FieldDescriptor* field, std::string* scratch) const; + // Returns a Cord containing the value of the string field. If the + // underlying field is stored as a cord (e.g. it has the [ctype=CORD] + // option), this involves no copies (just reference counting). If the + // underlying representation is not a Cord, a copy will have to be made. + absl::Cord GetCord(const Message& message, + const FieldDescriptor* field) const; // Singular field mutators ----------------------------------------- diff --git a/src/google/protobuf/port_def.inc b/src/google/protobuf/port_def.inc index 57a752e1345e..cd45dd6ccfd0 100644 --- a/src/google/protobuf/port_def.inc +++ b/src/google/protobuf/port_def.inc @@ -213,10 +213,6 @@ static_assert(PROTOBUF_CPLUSPLUS_MIN(201402L), "Protobuf only supports C++14 and // Owner: shaod@, gberg@ #define PROTOBUF_FUTURE_DESCRIPTOR_EXTENSION_DECL 1 -// Enable cord handling. -// Owner: mvels@, mkruskal@ -#define PROTOBUF_FUTURE_OPENSOURCE_CORD 1 - // Used to remove `RepeatedPtrField::GetArena() const`. // Owner: ezb@ #define PROTOBUF_FUTURE_REMOVE_CONST_REPEATEDFIELD_GETARENA_API 1 diff --git a/src/google/protobuf/test_util.inc b/src/google/protobuf/test_util.inc index 37363a741b5b..4c53b202c3d4 100644 --- a/src/google/protobuf/test_util.inc +++ b/src/google/protobuf/test_util.inc @@ -2396,6 +2396,7 @@ inline void TestUtil::ExpectOneofClear(const UNITTEST::TestOneof2& message) { EXPECT_FALSE(message.has_foo_message()); EXPECT_FALSE(message.has_foogroup()); EXPECT_FALSE(message.has_foo_lazy_message()); + EXPECT_FALSE(message.has_foo_bytes_cord()); EXPECT_FALSE(message.has_bar_int()); EXPECT_FALSE(message.has_bar_string()); @@ -2419,6 +2420,7 @@ inline void TestUtil::ExpectAtMostOneFieldSetInOneof( if (message.has_foo_message()) count++; if (message.has_foogroup()) count++; if (message.has_foo_lazy_message()) count++; + if (message.has_foo_bytes_cord()) count++; EXPECT_LE(count, 1); count = 0; if (message.has_bar_int()) count++; diff --git a/src/google/protobuf/unittest.proto b/src/google/protobuf/unittest.proto index b98fc2c02667..d3025876b89c 100644 --- a/src/google/protobuf/unittest.proto +++ b/src/google/protobuf/unittest.proto @@ -827,7 +827,6 @@ message TestOneof { optional int32 a = 5; optional string b = 6; } - bytes foo_bytes_cord = 7 [ctype=CORD]; } } @@ -855,6 +854,7 @@ message TestOneof2 { optional string b = 10; } NestedMessage foo_lazy_message = 11 [lazy=true]; + bytes foo_bytes_cord = 30 [ctype=CORD]; } oneof bar {