Added Reflection::GetCord() method in C++

PiperOrigin-RevId: 519246549
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 71a540a..a83a62c 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 @@
       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 21d01cc..bdefe09 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 @@
   // 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 @@
 
 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 3970389..830846e 100644
--- a/src/google/protobuf/compiler/cpp/helpers.cc
+++ b/src/google/protobuf/compiler/cpp/helpers.cc
@@ -978,7 +978,8 @@
 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 @@
 
 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 @@
   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 fd2ea7b..56b599a 100644
--- a/src/google/protobuf/compiler/cpp/helpers.h
+++ b/src/google/protobuf/compiler/cpp/helpers.h
@@ -346,25 +346,21 @@
 
 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 dd5829c..bf434f5 100644
--- a/src/google/protobuf/compiler/cpp/unittest.inc
+++ b/src/google/protobuf/compiler/cpp/unittest.inc
@@ -1452,6 +1452,9 @@
       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 87ae9ed..68434ee 100644
--- a/src/google/protobuf/descriptor.h
+++ b/src/google/protobuf/descriptor.h
@@ -2645,6 +2645,21 @@
 
 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 FD = FieldDescriptor, typename FO = FieldOptions>
+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 e7cb11f..626672b 100644
--- a/src/google/protobuf/generated_message_reflection.cc
+++ b/src/google/protobuf/generated_message_reflection.cc
@@ -403,8 +403,20 @@
           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<absl::Cord*>(message, field)
+                                  ->EstimatedMemoryUsage();
+
+              } else {
+                // sizeof(absl::Cord) is included to self.
+                total_size += GetField<absl::Cord>(message, field)
+                                  .EstimatedMemoryUsage() -
+                              sizeof(absl::Cord);
+              }
+              break;
+            default:
             case FieldOptions::STRING:
               if (IsInlined(field)) {
                 const std::string* ptr =
@@ -501,7 +513,10 @@
           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 @@
 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<absl::Cord>(lhs, field),
+                *r->MutableRaw<absl::Cord>(rhs, field));
+      break;
     default:
     case FieldOptions::STRING: {
       if (r->IsInlined(field)) {
@@ -891,6 +911,7 @@
     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 @@
       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 @@
     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 @@
           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<absl::Cord>(message, field) =
+                    field->default_value_string();
+              } else {
+                MutableRaw<absl::Cord>(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 @@
     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<absl::Cord*>(message, field));
+        } else {
+          return std::string(GetField<absl::Cord>(message, field));
+        }
+      default:
       case FieldOptions::STRING:
         if (IsInlined(field)) {
           return GetField<InlinedStringField>(message, field).GetNoArena();
@@ -1743,8 +1780,16 @@
     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<absl::Cord*>(message, field),
+                                 scratch);
+        } else {
+          absl::CopyCordToString(GetField<absl::Cord>(message, field), scratch);
+        }
+        return *scratch;
+      default:
       case FieldOptions::STRING:
         if (IsInlined(field)) {
           return GetField<InlinedStringField>(message, field).GetNoArena();
@@ -1756,6 +1801,39 @@
   }
 }
 
+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<absl::Cord*>(message, field);
+        } else {
+          return GetField<absl::Cord>(message, field);
+        }
+      default:
+      case FieldOptions::STRING:
+        if (IsInlined(field)) {
+          return absl::Cord(
+              GetField<InlinedStringField>(message, field).GetNoArena());
+        } else {
+          const auto& str = GetField<ArenaStringPtr>(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 @@
     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<absl::Cord*>(message, field) =
+                Arena::Create<absl::Cord>(message->GetArenaForAllocation());
+          }
+          *(*MutableField<absl::Cord*>(message, field)) = value;
+          break;
+        }
+        *MutableField<absl::Cord>(message, field) = value;
+        break;
+      default:
       case FieldOptions::STRING: {
         if (IsInlined(field)) {
           const uint32_t index = schema_.InlinedStringIndex(field);
@@ -1805,7 +1895,7 @@
                                   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 @@
     // (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<const absl::Cord>(message, field).empty();
+          default:
+          case FieldOptions::STRING: {
             if (IsInlined(field)) {
               return !GetField<InlinedStringField>(message, field)
                           .GetNoArena()
@@ -2833,8 +2926,11 @@
     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<absl::Cord*>(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 3de242c..4c8e0ab 100644
--- a/src/google/protobuf/generated_message_reflection_unittest.cc
+++ b/src/google/protobuf/generated_message_reflection_unittest.cc
@@ -165,6 +165,22 @@
          "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<bool> {
  protected:
@@ -981,6 +997,8 @@
   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 @@
                         "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 @@
 
 #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 ac7fbe5..549f24a 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 @@
                                         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 57a752e..cd45dd6 100644
--- a/src/google/protobuf/port_def.inc
+++ b/src/google/protobuf/port_def.inc
@@ -213,10 +213,6 @@
 // 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 37363a7..4c53b20 100644
--- a/src/google/protobuf/test_util.inc
+++ b/src/google/protobuf/test_util.inc
@@ -2396,6 +2396,7 @@
   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 @@
   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 b98fc2c..d302587 100644
--- a/src/google/protobuf/unittest.proto
+++ b/src/google/protobuf/unittest.proto
@@ -827,7 +827,6 @@
       optional int32 a = 5;
       optional string b = 6;
     }
-    bytes foo_bytes_cord = 7 [ctype=CORD];
   }
 }
 
@@ -855,6 +854,7 @@
       optional string b = 10;
     }
     NestedMessage foo_lazy_message = 11 [lazy=true];
+    bytes foo_bytes_cord = 30 [ctype=CORD];
   }
 
   oneof bar {