Эх сурвалжийг харах

Merge pull request #7576 from jtattermusch/protobuf_buffer_serialization

New Span-based serialization logic (followup for #7351)
Jan Tattermusch 5 жил өмнө
parent
commit
206b973afd
80 өөрчлөгдсөн 9023 нэмэгдсэн , 621 устгасан
  1. 8 0
      Makefile.am
  2. 32 32
      csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
  3. 20 2
      csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/FieldCodecTest.cs
  4. 65 0
      csharp/src/AddressBook/Addressbook.cs
  5. 267 0
      csharp/src/Google.Protobuf.Benchmarks/BenchmarkMessage1Proto3.cs
  6. 22 0
      csharp/src/Google.Protobuf.Benchmarks/Benchmarks.cs
  7. 3 3
      csharp/src/Google.Protobuf.Benchmarks/ParseMessagesBenchmark.cs
  8. 39 4
      csharp/src/Google.Protobuf.Benchmarks/ParseRawPrimitivesBenchmark.cs
  9. 801 0
      csharp/src/Google.Protobuf.Benchmarks/WrapperBenchmarkMessages.cs
  10. 198 0
      csharp/src/Google.Protobuf.Benchmarks/WriteMessagesBenchmark.cs
  11. 467 0
      csharp/src/Google.Protobuf.Benchmarks/WriteRawPrimitivesBenchmark.cs
  12. 125 0
      csharp/src/Google.Protobuf.Conformance/Conformance.cs
  13. 132 0
      csharp/src/Google.Protobuf.Test.TestProtos/MapUnittestProto3.cs
  14. 446 0
      csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs
  15. 386 0
      csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs
  16. 762 103
      csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs
  17. 335 0
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestCustomOptionsProto3.cs
  18. 17 0
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestImport.cs
  19. 17 0
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestImportProto3.cs
  20. 17 0
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestImportPublic.cs
  21. 17 0
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestImportPublicProto3.cs
  22. 13 0
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssue6936B.cs
  23. 17 0
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssue6936C.cs
  24. 254 0
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs
  25. 616 2
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3.cs
  26. 114 0
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.cs
  27. 24 0
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestSelfreferentialOptions.cs
  28. 218 0
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestWellKnownTypes.cs
  29. 225 0
      csharp/src/Google.Protobuf.Test/Buffers/ArrayBufferWriter.cs
  30. 140 46
      csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
  31. 2 0
      csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs
  32. 22 2
      csharp/src/Google.Protobuf.Test/FieldCodecTest.cs
  33. 6 0
      csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs
  34. 11 3
      csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
  35. 82 19
      csharp/src/Google.Protobuf.Test/LegacyGeneratedCodeTest.cs
  36. 42 0
      csharp/src/Google.Protobuf.Test/MessageParsingHelpers.cs
  37. 10 0
      csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs
  38. 3 3
      csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs
  39. 126 289
      csharp/src/Google.Protobuf/CodedOutputStream.cs
  40. 31 4
      csharp/src/Google.Protobuf/Collections/MapField.cs
  41. 28 8
      csharp/src/Google.Protobuf/Collections/RepeatedField.cs
  42. 20 1
      csharp/src/Google.Protobuf/ExtensionSet.cs
  43. 7 12
      csharp/src/Google.Protobuf/ExtensionValue.cs
  44. 59 31
      csharp/src/Google.Protobuf/FieldCodec.cs
  45. 7 1
      csharp/src/Google.Protobuf/IBufferMessage.cs
  46. 35 1
      csharp/src/Google.Protobuf/MessageExtensions.cs
  47. 1 1
      csharp/src/Google.Protobuf/ParserInternalState.cs
  48. 3 3
      csharp/src/Google.Protobuf/ParsingPrimitives.cs
  49. 683 12
      csharp/src/Google.Protobuf/Reflection/Descriptor.cs
  50. 3 3
      csharp/src/Google.Protobuf/UnknownField.cs
  51. 18 1
      csharp/src/Google.Protobuf/UnknownFieldSet.cs
  52. 21 0
      csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
  53. 91 0
      csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
  54. 21 0
      csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs
  55. 13 0
      csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs
  56. 14 0
      csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
  57. 17 0
      csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs
  58. 65 0
      csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs
  59. 21 0
      csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs
  60. 148 0
      csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
  61. 153 0
      csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs
  62. 166 0
      csharp/src/Google.Protobuf/WriteBufferHelper.cs
  63. 371 0
      csharp/src/Google.Protobuf/WriteContext.cs
  64. 62 0
      csharp/src/Google.Protobuf/WriterInternalState.cs
  65. 638 0
      csharp/src/Google.Protobuf/WritingPrimitives.cs
  66. 112 0
      csharp/src/Google.Protobuf/WritingPrimitivesMessages.cs
  67. 6 0
      src/google/protobuf/compiler/csharp/csharp_field_base.cc
  68. 1 0
      src/google/protobuf/compiler/csharp/csharp_field_base.h
  69. 7 1
      src/google/protobuf/compiler/csharp/csharp_map_field.cc
  70. 1 0
      src/google/protobuf/compiler/csharp/csharp_map_field.h
  71. 50 25
      src/google/protobuf/compiler/csharp/csharp_message.cc
  72. 1 0
      src/google/protobuf/compiler/csharp/csharp_message.h
  73. 7 1
      src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc
  74. 1 0
      src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.h
  75. 7 1
      src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc
  76. 1 0
      src/google/protobuf/compiler/csharp/csharp_repeated_message_field.h
  77. 7 1
      src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc
  78. 1 0
      src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h
  79. 22 6
      src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc
  80. 2 0
      src/google/protobuf/compiler/csharp/csharp_wrapper_field.h

+ 8 - 0
Makefile.am

@@ -96,11 +96,14 @@ csharp_EXTRA_DIST=                                                           \
   csharp/src/Google.Protobuf.Benchmarks/Program.cs                           \
   csharp/src/Google.Protobuf.Benchmarks/wrapper_benchmark_messages.proto     \
   csharp/src/Google.Protobuf.Benchmarks/WrapperBenchmarkMessages.cs          \
+  csharp/src/Google.Protobuf.Benchmarks/WriteMessagesBenchmark.cs            \
+  csharp/src/Google.Protobuf.Benchmarks/WriteRawPrimitivesBenchmark.cs       \
   csharp/src/Google.Protobuf.Conformance/Conformance.cs                      \
   csharp/src/Google.Protobuf.Conformance/Google.Protobuf.Conformance.csproj  \
   csharp/src/Google.Protobuf.Conformance/Program.cs                          \
   csharp/src/Google.Protobuf.JsonDump/Google.Protobuf.JsonDump.csproj        \
   csharp/src/Google.Protobuf.JsonDump/Program.cs                             \
+  csharp/src/Google.Protobuf.Test/Buffers/ArrayBufferWriter.cs               \
   csharp/src/Google.Protobuf.Test/ByteStringTest.cs                          \
   csharp/src/Google.Protobuf.Test/CodedInputStreamExtensions.cs              \
   csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs                    \
@@ -259,6 +262,11 @@ csharp_EXTRA_DIST=                                                           \
   csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs                      \
   csharp/src/Google.Protobuf/WellKnownTypes/WrappersPartial.cs               \
   csharp/src/Google.Protobuf/WireFormat.cs                                   \
+  csharp/src/Google.Protobuf/WritingPrimitivesMessages.cs                    \
+  csharp/src/Google.Protobuf/WritingPrimitives.cs                            \
+  csharp/src/Google.Protobuf/WriterInternalState.cs                          \
+  csharp/src/Google.Protobuf/WriteContext.cs                                 \
+  csharp/src/Google.Protobuf/WriteBufferHelper.cs                            \
   csharp/src/Google.Protobuf/UnknownField.cs                                 \
   csharp/src/Google.Protobuf/UnknownFieldSet.cs
 

+ 32 - 32
csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedOutputStreamTest.cs

@@ -211,35 +211,35 @@ namespace Google.Protobuf
         [Test]
         public void EncodeZigZag32()
         {
-            Assert.AreEqual(0u, CodedOutputStream.EncodeZigZag32(0));
-            Assert.AreEqual(1u, CodedOutputStream.EncodeZigZag32(-1));
-            Assert.AreEqual(2u, CodedOutputStream.EncodeZigZag32(1));
-            Assert.AreEqual(3u, CodedOutputStream.EncodeZigZag32(-2));
-            Assert.AreEqual(0x7FFFFFFEu, CodedOutputStream.EncodeZigZag32(0x3FFFFFFF));
-            Assert.AreEqual(0x7FFFFFFFu, CodedOutputStream.EncodeZigZag32(unchecked((int) 0xC0000000)));
-            Assert.AreEqual(0xFFFFFFFEu, CodedOutputStream.EncodeZigZag32(0x7FFFFFFF));
-            Assert.AreEqual(0xFFFFFFFFu, CodedOutputStream.EncodeZigZag32(unchecked((int) 0x80000000)));
+            Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag32(0));
+            Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag32(-1));
+            Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag32(1));
+            Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag32(-2));
+            Assert.AreEqual(0x7FFFFFFEu, WritingPrimitives.EncodeZigZag32(0x3FFFFFFF));
+            Assert.AreEqual(0x7FFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0xC0000000)));
+            Assert.AreEqual(0xFFFFFFFEu, WritingPrimitives.EncodeZigZag32(0x7FFFFFFF));
+            Assert.AreEqual(0xFFFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0x80000000)));
         }
 
         [Test]
         public void EncodeZigZag64()
         {
-            Assert.AreEqual(0u, CodedOutputStream.EncodeZigZag64(0));
-            Assert.AreEqual(1u, CodedOutputStream.EncodeZigZag64(-1));
-            Assert.AreEqual(2u, CodedOutputStream.EncodeZigZag64(1));
-            Assert.AreEqual(3u, CodedOutputStream.EncodeZigZag64(-2));
+            Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag64(0));
+            Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag64(-1));
+            Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag64(1));
+            Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag64(-2));
             Assert.AreEqual(0x000000007FFFFFFEuL,
-                            CodedOutputStream.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL)));
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL)));
             Assert.AreEqual(0x000000007FFFFFFFuL,
-                            CodedOutputStream.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL)));
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL)));
             Assert.AreEqual(0x00000000FFFFFFFEuL,
-                            CodedOutputStream.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL)));
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL)));
             Assert.AreEqual(0x00000000FFFFFFFFuL,
-                            CodedOutputStream.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL)));
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL)));
             Assert.AreEqual(0xFFFFFFFFFFFFFFFEL,
-                            CodedOutputStream.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL)));
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL)));
             Assert.AreEqual(0xFFFFFFFFFFFFFFFFL,
-                            CodedOutputStream.EncodeZigZag64(unchecked((long) 0x8000000000000000UL)));
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x8000000000000000UL)));
         }
 
         [Test]
@@ -247,26 +247,26 @@ namespace Google.Protobuf
         {
             // Some easier-to-verify round-trip tests.  The inputs (other than 0, 1, -1)
             // were chosen semi-randomly via keyboard bashing.
-            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(0)));
-            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(1)));
-            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(-1)));
-            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(14927)));
-            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(-3612)));
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(0)));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(1)));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-1)));
+            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(14927)));
+            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-3612)));
         }
 
         [Test]
         public void RoundTripZigZag64()
         {
-            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(0)));
-            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(1)));
-            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-1)));
-            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(14927)));
-            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-3612)));
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(0)));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(1)));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-1)));
+            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(14927)));
+            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-3612)));
 
             Assert.AreEqual(856912304801416L,
-                            ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(856912304801416L)));
+                            ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(856912304801416L)));
             Assert.AreEqual(-75123905439571256L,
-                            ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-75123905439571256L)));
+                            ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-75123905439571256L)));
         }
 
         [Test]
@@ -395,7 +395,7 @@ namespace Google.Protobuf
             Assert.IsTrue(memoryStream.CanWrite);
             using (var cos = new CodedOutputStream(memoryStream))
             {
-                cos.WriteRawByte(0);
+                cos.WriteRawBytes(new byte[] {0});
                 Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
             }
             Assert.AreEqual(1, memoryStream.ToArray().Length); // Flushed data from CodedOutputStream to MemoryStream
@@ -409,7 +409,7 @@ namespace Google.Protobuf
             Assert.IsTrue(memoryStream.CanWrite);
             using (var cos = new CodedOutputStream(memoryStream, true))
             {
-                cos.WriteRawByte(0);
+                cos.WriteRawBytes(new byte[] {0});
                 Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
             }
             Assert.AreEqual(1, memoryStream.Position); // Flushed data from CodedOutputStream to MemoryStream

+ 20 - 2
csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/FieldCodecTest.cs

@@ -124,7 +124,16 @@ namespace Google.Protobuf
             {
                 var stream = new MemoryStream();
                 var codedOutput = new CodedOutputStream(stream);
-                codec.ValueWriter(codedOutput, sampleValue);
+                WriteContext.Initialize(codedOutput, out WriteContext ctx);
+                try
+                {
+                    // only write the value using the codec
+                    codec.ValueWriter(ref ctx, sampleValue);
+                }
+                finally
+                {
+                    ctx.CopyStateTo(codedOutput);
+                }
                 codedOutput.Flush();
                 stream.Position = 0;
                 var codedInput = new CodedInputStream(stream);
@@ -172,7 +181,16 @@ namespace Google.Protobuf
                 if (codec.DefaultValue != null) // This part isn't appropriate for message types.
                 {
                     codedOutput = new CodedOutputStream(stream);
-                    codec.ValueWriter(codedOutput, codec.DefaultValue);
+                    WriteContext.Initialize(codedOutput, out WriteContext ctx);
+                    try
+                    {
+                        // only write the value using the codec
+                        codec.ValueWriter(ref ctx, codec.DefaultValue);
+                    }
+                    finally
+                    {
+                        ctx.CopyStateTo(codedOutput);
+                    }
                     codedOutput.Flush();
                     Assert.AreNotEqual(0, stream.Position);
                     Assert.AreEqual(stream.Position, codec.ValueSizeCalculator(codec.DefaultValue));

+ 65 - 0
csharp/src/AddressBook/Addressbook.cs

@@ -190,6 +190,9 @@ namespace Google.Protobuf.Examples.AddressBook {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Name.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Name);
@@ -210,8 +213,35 @@ namespace Google.Protobuf.Examples.AddressBook {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+      if (Id != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Id);
+      }
+      if (Email.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Email);
+      }
+      phones_.WriteTo(ref output, _repeated_phones_codec);
+      if (lastUpdated_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(LastUpdated);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -441,6 +471,9 @@ namespace Google.Protobuf.Examples.AddressBook {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (Number.Length != 0) {
             output.WriteRawTag(10);
             output.WriteString(Number);
@@ -452,8 +485,26 @@ namespace Google.Protobuf.Examples.AddressBook {
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
         }
 
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (Number.Length != 0) {
+            output.WriteRawTag(10);
+            output.WriteString(Number);
+          }
+          if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.Mobile) {
+            output.WriteRawTag(16);
+            output.WriteEnum((int) Type);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {
           int size = 0;
@@ -621,12 +672,26 @@ namespace Google.Protobuf.Examples.AddressBook {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       people_.WriteTo(output, _repeated_people_codec);
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      people_.WriteTo(ref output, _repeated_people_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;

+ 267 - 0
csharp/src/Google.Protobuf.Benchmarks/BenchmarkMessage1Proto3.cs

@@ -706,6 +706,9 @@ namespace Benchmarks.Proto3 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Field1.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Field1);
@@ -870,7 +873,178 @@ namespace Benchmarks.Proto3 {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Field1.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Field1);
+      }
+      if (Field2 != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Field2);
+      }
+      if (Field3 != 0) {
+        output.WriteRawTag(24);
+        output.WriteInt32(Field3);
+      }
+      if (Field4.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Field4);
+      }
+      field5_.WriteTo(ref output, _repeated_field5_codec);
+      if (Field6 != 0) {
+        output.WriteRawTag(48);
+        output.WriteInt32(Field6);
+      }
+      if (Field7.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(Field7);
+      }
+      if (Field9.Length != 0) {
+        output.WriteRawTag(74);
+        output.WriteString(Field9);
+      }
+      if (Field12 != false) {
+        output.WriteRawTag(96);
+        output.WriteBool(Field12);
+      }
+      if (Field13 != false) {
+        output.WriteRawTag(104);
+        output.WriteBool(Field13);
+      }
+      if (Field14 != false) {
+        output.WriteRawTag(112);
+        output.WriteBool(Field14);
+      }
+      if (field15_ != null) {
+        output.WriteRawTag(122);
+        output.WriteMessage(Field15);
+      }
+      if (Field16 != 0) {
+        output.WriteRawTag(128, 1);
+        output.WriteInt32(Field16);
+      }
+      if (Field17 != false) {
+        output.WriteRawTag(136, 1);
+        output.WriteBool(Field17);
+      }
+      if (Field18.Length != 0) {
+        output.WriteRawTag(146, 1);
+        output.WriteString(Field18);
+      }
+      if (Field22 != 0L) {
+        output.WriteRawTag(176, 1);
+        output.WriteInt64(Field22);
+      }
+      if (Field23 != 0) {
+        output.WriteRawTag(184, 1);
+        output.WriteInt32(Field23);
+      }
+      if (Field24 != false) {
+        output.WriteRawTag(192, 1);
+        output.WriteBool(Field24);
+      }
+      if (Field25 != 0) {
+        output.WriteRawTag(200, 1);
+        output.WriteInt32(Field25);
+      }
+      if (Field29 != 0) {
+        output.WriteRawTag(232, 1);
+        output.WriteInt32(Field29);
+      }
+      if (Field30 != false) {
+        output.WriteRawTag(240, 1);
+        output.WriteBool(Field30);
+      }
+      if (Field59 != false) {
+        output.WriteRawTag(216, 3);
+        output.WriteBool(Field59);
+      }
+      if (Field60 != 0) {
+        output.WriteRawTag(224, 3);
+        output.WriteInt32(Field60);
+      }
+      if (Field67 != 0) {
+        output.WriteRawTag(152, 4);
+        output.WriteInt32(Field67);
+      }
+      if (Field68 != 0) {
+        output.WriteRawTag(160, 4);
+        output.WriteInt32(Field68);
+      }
+      if (Field78 != false) {
+        output.WriteRawTag(240, 4);
+        output.WriteBool(Field78);
+      }
+      if (Field80 != false) {
+        output.WriteRawTag(128, 5);
+        output.WriteBool(Field80);
+      }
+      if (Field81 != false) {
+        output.WriteRawTag(136, 5);
+        output.WriteBool(Field81);
+      }
+      if (Field100 != 0) {
+        output.WriteRawTag(160, 6);
+        output.WriteInt32(Field100);
+      }
+      if (Field101 != 0) {
+        output.WriteRawTag(168, 6);
+        output.WriteInt32(Field101);
+      }
+      if (Field102.Length != 0) {
+        output.WriteRawTag(178, 6);
+        output.WriteString(Field102);
+      }
+      if (Field103.Length != 0) {
+        output.WriteRawTag(186, 6);
+        output.WriteString(Field103);
+      }
+      if (Field104 != 0) {
+        output.WriteRawTag(192, 6);
+        output.WriteInt32(Field104);
+      }
+      if (Field128 != 0) {
+        output.WriteRawTag(128, 8);
+        output.WriteInt32(Field128);
+      }
+      if (Field129.Length != 0) {
+        output.WriteRawTag(138, 8);
+        output.WriteString(Field129);
+      }
+      if (Field130 != 0) {
+        output.WriteRawTag(144, 8);
+        output.WriteInt32(Field130);
+      }
+      if (Field131 != 0) {
+        output.WriteRawTag(152, 8);
+        output.WriteInt32(Field131);
+      }
+      if (Field150 != 0) {
+        output.WriteRawTag(176, 9);
+        output.WriteInt32(Field150);
+      }
+      if (Field271 != 0) {
+        output.WriteRawTag(248, 16);
+        output.WriteInt32(Field271);
+      }
+      if (Field272 != 0) {
+        output.WriteRawTag(128, 17);
+        output.WriteInt32(Field272);
+      }
+      if (Field280 != 0) {
+        output.WriteRawTag(192, 17);
+        output.WriteInt32(Field280);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1851,6 +2025,9 @@ namespace Benchmarks.Proto3 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Field1 != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(Field1);
@@ -1934,7 +2111,97 @@ namespace Benchmarks.Proto3 {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Field1 != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(Field1);
+      }
+      if (Field2 != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Field2);
+      }
+      if (Field3 != 0) {
+        output.WriteRawTag(24);
+        output.WriteInt32(Field3);
+      }
+      if (Field12 != false) {
+        output.WriteRawTag(96);
+        output.WriteBool(Field12);
+      }
+      if (Field13 != 0L) {
+        output.WriteRawTag(104);
+        output.WriteInt64(Field13);
+      }
+      if (Field14 != 0L) {
+        output.WriteRawTag(112);
+        output.WriteInt64(Field14);
+      }
+      if (Field15.Length != 0) {
+        output.WriteRawTag(122);
+        output.WriteString(Field15);
+      }
+      if (Field16 != 0) {
+        output.WriteRawTag(128, 1);
+        output.WriteInt32(Field16);
+      }
+      if (Field19 != 0) {
+        output.WriteRawTag(152, 1);
+        output.WriteInt32(Field19);
+      }
+      if (Field20 != false) {
+        output.WriteRawTag(160, 1);
+        output.WriteBool(Field20);
+      }
+      if (Field21 != 0UL) {
+        output.WriteRawTag(169, 1);
+        output.WriteFixed64(Field21);
+      }
+      if (Field22 != 0) {
+        output.WriteRawTag(176, 1);
+        output.WriteInt32(Field22);
+      }
+      if (Field23 != false) {
+        output.WriteRawTag(184, 1);
+        output.WriteBool(Field23);
+      }
+      if (Field28 != false) {
+        output.WriteRawTag(224, 1);
+        output.WriteBool(Field28);
+      }
+      if (Field203 != 0) {
+        output.WriteRawTag(221, 12);
+        output.WriteFixed32(Field203);
+      }
+      if (Field204 != 0) {
+        output.WriteRawTag(224, 12);
+        output.WriteInt32(Field204);
+      }
+      if (Field205.Length != 0) {
+        output.WriteRawTag(234, 12);
+        output.WriteString(Field205);
+      }
+      if (Field206 != false) {
+        output.WriteRawTag(240, 12);
+        output.WriteBool(Field206);
+      }
+      if (Field207 != 0UL) {
+        output.WriteRawTag(248, 12);
+        output.WriteUInt64(Field207);
+      }
+      if (Field300 != 0UL) {
+        output.WriteRawTag(224, 18);
+        output.WriteUInt64(Field300);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 22 - 0
csharp/src/Google.Protobuf.Benchmarks/Benchmarks.cs

@@ -176,6 +176,9 @@ namespace Benchmarks {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Name.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Name);
@@ -188,7 +191,26 @@ namespace Benchmarks {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+      if (MessageName.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(MessageName);
+      }
+      payload_.WriteTo(ref output, _repeated_payload_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 3 - 3
csharp/src/Google.Protobuf.Benchmarks/ParseMessagesBenchmark.cs

@@ -151,7 +151,7 @@ namespace Google.Protobuf.Benchmarks
             repeatedFieldTest.ParseDelimitedMessagesFromReadOnlySequence(messageCount);
         }
 
-        private static ManyWrapperFieldsMessage CreateManyWrapperFieldsMessage()
+        public static ManyWrapperFieldsMessage CreateManyWrapperFieldsMessage()
         {
             // Example data match data of an internal benchmarks
             return new ManyWrapperFieldsMessage()
@@ -168,7 +168,7 @@ namespace Google.Protobuf.Benchmarks
             };
         }
 
-        private static ManyPrimitiveFieldsMessage CreateManyPrimitiveFieldsMessage()
+        public static ManyPrimitiveFieldsMessage CreateManyPrimitiveFieldsMessage()
         {
             // Example data match data of an internal benchmarks
             return new ManyPrimitiveFieldsMessage()
@@ -185,7 +185,7 @@ namespace Google.Protobuf.Benchmarks
             };
         }
 
-        private static GoogleMessage1 CreateRepeatedFieldMessage()
+        public static GoogleMessage1 CreateRepeatedFieldMessage()
         {
             // Message with a repeated fixed length item collection
             var message = new GoogleMessage1();

+ 39 - 4
csharp/src/Google.Protobuf.Benchmarks/ParseRawPrimitivesBenchmark.cs

@@ -337,7 +337,7 @@ namespace Google.Protobuf.Benchmarks
             CodedOutputStream cos = new CodedOutputStream(ms);
             for (int i = 0; i < valueCount + paddingValueCount; i++)
             {
-                cos.WriteUInt64(RandomUnsignedVarint(random, encodedSize));
+                cos.WriteUInt64(RandomUnsignedVarint(random, encodedSize, false));
             }
             cos.Flush();
             var buffer = ms.ToArray();
@@ -386,11 +386,11 @@ namespace Google.Protobuf.Benchmarks
         /// <summary>
         /// Generate a random value that will take exactly "encodedSize" bytes when varint-encoded.
         /// </summary>
-        private static ulong RandomUnsignedVarint(Random random, int encodedSize)
+        public static ulong RandomUnsignedVarint(Random random, int encodedSize, bool fitsIn32Bits)
         {
             Span<byte> randomBytesBuffer = stackalloc byte[8];
 
-            if (encodedSize < 1 || encodedSize > 10)
+            if (encodedSize < 1 || encodedSize > 10 || (fitsIn32Bits && encodedSize > 5))
             {
                 throw new ArgumentException("Illegal encodedSize value requested", nameof(encodedSize));
             }
@@ -406,6 +406,12 @@ namespace Google.Protobuf.Benchmarks
                 ulong bitmask = encodedSize < 10 ? ((1UL << (encodedSize * bitsPerByte)) - 1) : ulong.MaxValue;
                 result = randomValue & bitmask;
 
+                if (fitsIn32Bits)
+                {
+                    // make sure the resulting value is representable by a uint.
+                    result &= uint.MaxValue;
+                }
+
                 if (encodedSize == 10)
                 {
                     // for 10-byte values the highest bit always needs to be set (7*9=63)
@@ -443,7 +449,7 @@ namespace Google.Protobuf.Benchmarks
             return buffer;
         }
 
-        private static string CreateStringWithEncodedSize(int encodedSize)
+        public static string CreateStringWithEncodedSize(int encodedSize)
         {
             var str = new string('a', encodedSize);
             while (CodedOutputStream.ComputeStringSize(str) > encodedSize)
@@ -457,5 +463,34 @@ namespace Google.Protobuf.Benchmarks
             }
             return str;
         }
+
+        public static string CreateNonAsciiStringWithEncodedSize(int encodedSize)
+        {
+            if (encodedSize < 3)
+            {
+                throw new ArgumentException("Illegal encoded size for a string with non-ascii chars.");
+            }
+            var twoByteChar = '\u00DC';  // U-umlaut, UTF8 encoding has 2 bytes
+            var str = new string(twoByteChar, encodedSize / 2);
+            while (CodedOutputStream.ComputeStringSize(str) > encodedSize)
+            {
+                str = str.Substring(1);
+            }
+
+            // add padding of ascii characters to reach the desired encoded size.
+            while (CodedOutputStream.ComputeStringSize(str) < encodedSize)
+            {
+                str += 'a';
+            }
+
+            // Note that for a few specific encodedSize values, it might be impossible to generate
+            // the string with the desired encodedSize using the algorithm above. For testing purposes, checking that
+            // the encoded size we got is actually correct is good enough.
+            if (CodedOutputStream.ComputeStringSize(str) != encodedSize)
+            {
+                throw new InvalidOperationException($"Generated string with wrong encodedSize");
+            }
+            return str;
+        }
     }
 }

+ 801 - 0
csharp/src/Google.Protobuf.Benchmarks/WrapperBenchmarkMessages.cs

@@ -2073,6 +2073,9 @@ namespace Google.Protobuf.Benchmarks {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (doubleField1_ != null) {
         _single_doubleField1_codec.WriteTagAndValue(output, DoubleField1);
       }
@@ -2410,7 +2413,351 @@ namespace Google.Protobuf.Benchmarks {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (doubleField1_ != null) {
+        _single_doubleField1_codec.WriteTagAndValue(ref output, DoubleField1);
+      }
+      if (int64Field2_ != null) {
+        _single_int64Field2_codec.WriteTagAndValue(ref output, Int64Field2);
+      }
+      if (int64Field3_ != null) {
+        _single_int64Field3_codec.WriteTagAndValue(ref output, Int64Field3);
+      }
+      if (int64Field4_ != null) {
+        _single_int64Field4_codec.WriteTagAndValue(ref output, Int64Field4);
+      }
+      if (doubleField7_ != null) {
+        _single_doubleField7_codec.WriteTagAndValue(ref output, DoubleField7);
+      }
+      if (doubleField8_ != null) {
+        _single_doubleField8_codec.WriteTagAndValue(ref output, DoubleField8);
+      }
+      if (doubleField9_ != null) {
+        _single_doubleField9_codec.WriteTagAndValue(ref output, DoubleField9);
+      }
+      if (doubleField10_ != null) {
+        _single_doubleField10_codec.WriteTagAndValue(ref output, DoubleField10);
+      }
+      if (doubleField11_ != null) {
+        _single_doubleField11_codec.WriteTagAndValue(ref output, DoubleField11);
+      }
+      if (doubleField14_ != null) {
+        _single_doubleField14_codec.WriteTagAndValue(ref output, DoubleField14);
+      }
+      if (doubleField15_ != null) {
+        _single_doubleField15_codec.WriteTagAndValue(ref output, DoubleField15);
+      }
+      if (int64Field19_ != null) {
+        _single_int64Field19_codec.WriteTagAndValue(ref output, Int64Field19);
+      }
+      if (doubleField20_ != null) {
+        _single_doubleField20_codec.WriteTagAndValue(ref output, DoubleField20);
+      }
+      if (doubleField21_ != null) {
+        _single_doubleField21_codec.WriteTagAndValue(ref output, DoubleField21);
+      }
+      if (doubleField22_ != null) {
+        _single_doubleField22_codec.WriteTagAndValue(ref output, DoubleField22);
+      }
+      if (doubleField25_ != null) {
+        _single_doubleField25_codec.WriteTagAndValue(ref output, DoubleField25);
+      }
+      if (int64Field26_ != null) {
+        _single_int64Field26_codec.WriteTagAndValue(ref output, Int64Field26);
+      }
+      if (doubleField28_ != null) {
+        _single_doubleField28_codec.WriteTagAndValue(ref output, DoubleField28);
+      }
+      if (doubleField29_ != null) {
+        _single_doubleField29_codec.WriteTagAndValue(ref output, DoubleField29);
+      }
+      if (doubleField30_ != null) {
+        _single_doubleField30_codec.WriteTagAndValue(ref output, DoubleField30);
+      }
+      if (doubleField31_ != null) {
+        _single_doubleField31_codec.WriteTagAndValue(ref output, DoubleField31);
+      }
+      if (int64Field32_ != null) {
+        _single_int64Field32_codec.WriteTagAndValue(ref output, Int64Field32);
+      }
+      if (int64Field37_ != null) {
+        _single_int64Field37_codec.WriteTagAndValue(ref output, Int64Field37);
+      }
+      if (doubleField38_ != null) {
+        _single_doubleField38_codec.WriteTagAndValue(ref output, DoubleField38);
+      }
+      if (interactions_ != null) {
+        _single_interactions_codec.WriteTagAndValue(ref output, Interactions);
+      }
+      if (doubleField40_ != null) {
+        _single_doubleField40_codec.WriteTagAndValue(ref output, DoubleField40);
+      }
+      if (int64Field41_ != null) {
+        _single_int64Field41_codec.WriteTagAndValue(ref output, Int64Field41);
+      }
+      if (doubleField42_ != null) {
+        _single_doubleField42_codec.WriteTagAndValue(ref output, DoubleField42);
+      }
+      if (int64Field43_ != null) {
+        _single_int64Field43_codec.WriteTagAndValue(ref output, Int64Field43);
+      }
+      if (int64Field44_ != null) {
+        _single_int64Field44_codec.WriteTagAndValue(ref output, Int64Field44);
+      }
+      if (doubleField45_ != null) {
+        _single_doubleField45_codec.WriteTagAndValue(ref output, DoubleField45);
+      }
+      if (doubleField46_ != null) {
+        _single_doubleField46_codec.WriteTagAndValue(ref output, DoubleField46);
+      }
+      if (doubleField47_ != null) {
+        _single_doubleField47_codec.WriteTagAndValue(ref output, DoubleField47);
+      }
+      if (doubleField48_ != null) {
+        _single_doubleField48_codec.WriteTagAndValue(ref output, DoubleField48);
+      }
+      if (doubleField49_ != null) {
+        _single_doubleField49_codec.WriteTagAndValue(ref output, DoubleField49);
+      }
+      if (doubleField50_ != null) {
+        _single_doubleField50_codec.WriteTagAndValue(ref output, DoubleField50);
+      }
+      if (doubleField51_ != null) {
+        _single_doubleField51_codec.WriteTagAndValue(ref output, DoubleField51);
+      }
+      if (doubleField52_ != null) {
+        _single_doubleField52_codec.WriteTagAndValue(ref output, DoubleField52);
+      }
+      if (doubleField53_ != null) {
+        _single_doubleField53_codec.WriteTagAndValue(ref output, DoubleField53);
+      }
+      if (doubleField54_ != null) {
+        _single_doubleField54_codec.WriteTagAndValue(ref output, DoubleField54);
+      }
+      if (doubleField55_ != null) {
+        _single_doubleField55_codec.WriteTagAndValue(ref output, DoubleField55);
+      }
+      if (doubleField56_ != null) {
+        _single_doubleField56_codec.WriteTagAndValue(ref output, DoubleField56);
+      }
+      if (doubleField57_ != null) {
+        _single_doubleField57_codec.WriteTagAndValue(ref output, DoubleField57);
+      }
+      if (doubleField58_ != null) {
+        _single_doubleField58_codec.WriteTagAndValue(ref output, DoubleField58);
+      }
+      if (int64Field59_ != null) {
+        _single_int64Field59_codec.WriteTagAndValue(ref output, Int64Field59);
+      }
+      if (int64Field60_ != null) {
+        _single_int64Field60_codec.WriteTagAndValue(ref output, Int64Field60);
+      }
+      if (doubleField62_ != null) {
+        _single_doubleField62_codec.WriteTagAndValue(ref output, DoubleField62);
+      }
+      if (doubleField65_ != null) {
+        _single_doubleField65_codec.WriteTagAndValue(ref output, DoubleField65);
+      }
+      if (doubleField66_ != null) {
+        _single_doubleField66_codec.WriteTagAndValue(ref output, DoubleField66);
+      }
+      if (doubleField67_ != null) {
+        _single_doubleField67_codec.WriteTagAndValue(ref output, DoubleField67);
+      }
+      if (doubleField68_ != null) {
+        _single_doubleField68_codec.WriteTagAndValue(ref output, DoubleField68);
+      }
+      if (doubleField69_ != null) {
+        _single_doubleField69_codec.WriteTagAndValue(ref output, DoubleField69);
+      }
+      if (doubleField70_ != null) {
+        _single_doubleField70_codec.WriteTagAndValue(ref output, DoubleField70);
+      }
+      if (doubleField71_ != null) {
+        _single_doubleField71_codec.WriteTagAndValue(ref output, DoubleField71);
+      }
+      if (doubleField72_ != null) {
+        _single_doubleField72_codec.WriteTagAndValue(ref output, DoubleField72);
+      }
+      if (stringField73_ != null) {
+        _single_stringField73_codec.WriteTagAndValue(ref output, StringField73);
+      }
+      if (stringField74_ != null) {
+        _single_stringField74_codec.WriteTagAndValue(ref output, StringField74);
+      }
+      if (doubleField75_ != null) {
+        _single_doubleField75_codec.WriteTagAndValue(ref output, DoubleField75);
+      }
+      if (doubleField77_ != null) {
+        _single_doubleField77_codec.WriteTagAndValue(ref output, DoubleField77);
+      }
+      if (doubleField78_ != null) {
+        _single_doubleField78_codec.WriteTagAndValue(ref output, DoubleField78);
+      }
+      if (doubleField79_ != null) {
+        _single_doubleField79_codec.WriteTagAndValue(ref output, DoubleField79);
+      }
+      if (EnumField80 != 0) {
+        output.WriteRawTag(128, 5);
+        output.WriteInt32(EnumField80);
+      }
+      if (EnumField81 != 0) {
+        output.WriteRawTag(136, 5);
+        output.WriteInt32(EnumField81);
+      }
+      if (int64Field82_ != null) {
+        _single_int64Field82_codec.WriteTagAndValue(ref output, Int64Field82);
+      }
+      if (EnumField83 != 0) {
+        output.WriteRawTag(152, 5);
+        output.WriteInt32(EnumField83);
+      }
+      if (doubleField84_ != null) {
+        _single_doubleField84_codec.WriteTagAndValue(ref output, DoubleField84);
+      }
+      if (int64Field85_ != null) {
+        _single_int64Field85_codec.WriteTagAndValue(ref output, Int64Field85);
+      }
+      if (int64Field86_ != null) {
+        _single_int64Field86_codec.WriteTagAndValue(ref output, Int64Field86);
+      }
+      if (int64Field87_ != null) {
+        _single_int64Field87_codec.WriteTagAndValue(ref output, Int64Field87);
+      }
+      if (doubleField88_ != null) {
+        _single_doubleField88_codec.WriteTagAndValue(ref output, DoubleField88);
+      }
+      if (doubleField89_ != null) {
+        _single_doubleField89_codec.WriteTagAndValue(ref output, DoubleField89);
+      }
+      if (doubleField90_ != null) {
+        _single_doubleField90_codec.WriteTagAndValue(ref output, DoubleField90);
+      }
+      if (doubleField91_ != null) {
+        _single_doubleField91_codec.WriteTagAndValue(ref output, DoubleField91);
+      }
+      if (doubleField92_ != null) {
+        _single_doubleField92_codec.WriteTagAndValue(ref output, DoubleField92);
+      }
+      if (doubleField93_ != null) {
+        _single_doubleField93_codec.WriteTagAndValue(ref output, DoubleField93);
+      }
+      if (doubleField94_ != null) {
+        _single_doubleField94_codec.WriteTagAndValue(ref output, DoubleField94);
+      }
+      if (doubleField95_ != null) {
+        _single_doubleField95_codec.WriteTagAndValue(ref output, DoubleField95);
+      }
+      if (doubleField96_ != null) {
+        _single_doubleField96_codec.WriteTagAndValue(ref output, DoubleField96);
+      }
+      if (doubleField97_ != null) {
+        _single_doubleField97_codec.WriteTagAndValue(ref output, DoubleField97);
+      }
+      if (doubleField98_ != null) {
+        _single_doubleField98_codec.WriteTagAndValue(ref output, DoubleField98);
+      }
+      if (doubleField99_ != null) {
+        _single_doubleField99_codec.WriteTagAndValue(ref output, DoubleField99);
+      }
+      repeatedIntField100_.WriteTo(ref output, _repeated_repeatedIntField100_codec);
+      if (doubleField101_ != null) {
+        _single_doubleField101_codec.WriteTagAndValue(ref output, DoubleField101);
+      }
+      if (doubleField102_ != null) {
+        _single_doubleField102_codec.WriteTagAndValue(ref output, DoubleField102);
+      }
+      if (doubleField103_ != null) {
+        _single_doubleField103_codec.WriteTagAndValue(ref output, DoubleField103);
+      }
+      if (doubleField104_ != null) {
+        _single_doubleField104_codec.WriteTagAndValue(ref output, DoubleField104);
+      }
+      if (doubleField105_ != null) {
+        _single_doubleField105_codec.WriteTagAndValue(ref output, DoubleField105);
+      }
+      if (doubleField106_ != null) {
+        _single_doubleField106_codec.WriteTagAndValue(ref output, DoubleField106);
+      }
+      if (int64Field107_ != null) {
+        _single_int64Field107_codec.WriteTagAndValue(ref output, Int64Field107);
+      }
+      if (doubleField108_ != null) {
+        _single_doubleField108_codec.WriteTagAndValue(ref output, DoubleField108);
+      }
+      if (doubleField109_ != null) {
+        _single_doubleField109_codec.WriteTagAndValue(ref output, DoubleField109);
+      }
+      if (int64Field110_ != null) {
+        _single_int64Field110_codec.WriteTagAndValue(ref output, Int64Field110);
+      }
+      if (doubleField111_ != null) {
+        _single_doubleField111_codec.WriteTagAndValue(ref output, DoubleField111);
+      }
+      if (int64Field112_ != null) {
+        _single_int64Field112_codec.WriteTagAndValue(ref output, Int64Field112);
+      }
+      if (doubleField113_ != null) {
+        _single_doubleField113_codec.WriteTagAndValue(ref output, DoubleField113);
+      }
+      if (int64Field114_ != null) {
+        _single_int64Field114_codec.WriteTagAndValue(ref output, Int64Field114);
+      }
+      if (int64Field115_ != null) {
+        _single_int64Field115_codec.WriteTagAndValue(ref output, Int64Field115);
+      }
+      if (doubleField116_ != null) {
+        _single_doubleField116_codec.WriteTagAndValue(ref output, DoubleField116);
+      }
+      if (int64Field117_ != null) {
+        _single_int64Field117_codec.WriteTagAndValue(ref output, Int64Field117);
+      }
+      if (doubleField118_ != null) {
+        _single_doubleField118_codec.WriteTagAndValue(ref output, DoubleField118);
+      }
+      if (doubleField119_ != null) {
+        _single_doubleField119_codec.WriteTagAndValue(ref output, DoubleField119);
+      }
+      if (doubleField120_ != null) {
+        _single_doubleField120_codec.WriteTagAndValue(ref output, DoubleField120);
+      }
+      if (doubleField121_ != null) {
+        _single_doubleField121_codec.WriteTagAndValue(ref output, DoubleField121);
+      }
+      if (doubleField122_ != null) {
+        _single_doubleField122_codec.WriteTagAndValue(ref output, DoubleField122);
+      }
+      if (doubleField123_ != null) {
+        _single_doubleField123_codec.WriteTagAndValue(ref output, DoubleField123);
+      }
+      if (doubleField124_ != null) {
+        _single_doubleField124_codec.WriteTagAndValue(ref output, DoubleField124);
+      }
+      if (int64Field125_ != null) {
+        _single_int64Field125_codec.WriteTagAndValue(ref output, Int64Field125);
+      }
+      if (int64Field126_ != null) {
+        _single_int64Field126_codec.WriteTagAndValue(ref output, Int64Field126);
+      }
+      if (int64Field127_ != null) {
+        _single_int64Field127_codec.WriteTagAndValue(ref output, Int64Field127);
+      }
+      if (doubleField128_ != null) {
+        _single_doubleField128_codec.WriteTagAndValue(ref output, DoubleField128);
+      }
+      if (doubleField129_ != null) {
+        _single_doubleField129_codec.WriteTagAndValue(ref output, DoubleField129);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -6495,6 +6842,9 @@ namespace Google.Protobuf.Benchmarks {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (DoubleField1 != 0D) {
         output.WriteRawTag(9);
         output.WriteDouble(DoubleField1);
@@ -6939,8 +7289,459 @@ namespace Google.Protobuf.Benchmarks {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DoubleField1 != 0D) {
+        output.WriteRawTag(9);
+        output.WriteDouble(DoubleField1);
+      }
+      if (Int64Field2 != 0L) {
+        output.WriteRawTag(16);
+        output.WriteInt64(Int64Field2);
+      }
+      if (Int64Field3 != 0L) {
+        output.WriteRawTag(24);
+        output.WriteInt64(Int64Field3);
+      }
+      if (Int64Field4 != 0L) {
+        output.WriteRawTag(32);
+        output.WriteInt64(Int64Field4);
+      }
+      if (DoubleField7 != 0D) {
+        output.WriteRawTag(57);
+        output.WriteDouble(DoubleField7);
+      }
+      if (DoubleField8 != 0D) {
+        output.WriteRawTag(65);
+        output.WriteDouble(DoubleField8);
+      }
+      if (DoubleField9 != 0D) {
+        output.WriteRawTag(73);
+        output.WriteDouble(DoubleField9);
+      }
+      if (DoubleField10 != 0D) {
+        output.WriteRawTag(81);
+        output.WriteDouble(DoubleField10);
+      }
+      if (DoubleField11 != 0D) {
+        output.WriteRawTag(89);
+        output.WriteDouble(DoubleField11);
+      }
+      if (DoubleField14 != 0D) {
+        output.WriteRawTag(113);
+        output.WriteDouble(DoubleField14);
+      }
+      if (DoubleField15 != 0D) {
+        output.WriteRawTag(121);
+        output.WriteDouble(DoubleField15);
+      }
+      if (Int64Field19 != 0L) {
+        output.WriteRawTag(152, 1);
+        output.WriteInt64(Int64Field19);
+      }
+      if (DoubleField20 != 0D) {
+        output.WriteRawTag(161, 1);
+        output.WriteDouble(DoubleField20);
+      }
+      if (DoubleField21 != 0D) {
+        output.WriteRawTag(169, 1);
+        output.WriteDouble(DoubleField21);
+      }
+      if (DoubleField22 != 0D) {
+        output.WriteRawTag(177, 1);
+        output.WriteDouble(DoubleField22);
+      }
+      if (DoubleField25 != 0D) {
+        output.WriteRawTag(201, 1);
+        output.WriteDouble(DoubleField25);
+      }
+      if (Int64Field26 != 0L) {
+        output.WriteRawTag(208, 1);
+        output.WriteInt64(Int64Field26);
+      }
+      if (DoubleField28 != 0D) {
+        output.WriteRawTag(225, 1);
+        output.WriteDouble(DoubleField28);
+      }
+      if (DoubleField29 != 0D) {
+        output.WriteRawTag(233, 1);
+        output.WriteDouble(DoubleField29);
+      }
+      if (DoubleField30 != 0D) {
+        output.WriteRawTag(241, 1);
+        output.WriteDouble(DoubleField30);
+      }
+      if (DoubleField31 != 0D) {
+        output.WriteRawTag(249, 1);
+        output.WriteDouble(DoubleField31);
+      }
+      if (Int64Field32 != 0L) {
+        output.WriteRawTag(128, 2);
+        output.WriteInt64(Int64Field32);
+      }
+      if (Int64Field37 != 0L) {
+        output.WriteRawTag(168, 2);
+        output.WriteInt64(Int64Field37);
+      }
+      if (DoubleField38 != 0D) {
+        output.WriteRawTag(177, 2);
+        output.WriteDouble(DoubleField38);
+      }
+      if (Interactions != 0L) {
+        output.WriteRawTag(184, 2);
+        output.WriteInt64(Interactions);
+      }
+      if (DoubleField40 != 0D) {
+        output.WriteRawTag(193, 2);
+        output.WriteDouble(DoubleField40);
+      }
+      if (Int64Field41 != 0L) {
+        output.WriteRawTag(200, 2);
+        output.WriteInt64(Int64Field41);
+      }
+      if (DoubleField42 != 0D) {
+        output.WriteRawTag(209, 2);
+        output.WriteDouble(DoubleField42);
+      }
+      if (Int64Field43 != 0L) {
+        output.WriteRawTag(216, 2);
+        output.WriteInt64(Int64Field43);
+      }
+      if (Int64Field44 != 0L) {
+        output.WriteRawTag(224, 2);
+        output.WriteInt64(Int64Field44);
+      }
+      if (DoubleField45 != 0D) {
+        output.WriteRawTag(233, 2);
+        output.WriteDouble(DoubleField45);
+      }
+      if (DoubleField46 != 0D) {
+        output.WriteRawTag(241, 2);
+        output.WriteDouble(DoubleField46);
+      }
+      if (DoubleField47 != 0D) {
+        output.WriteRawTag(249, 2);
+        output.WriteDouble(DoubleField47);
+      }
+      if (DoubleField48 != 0D) {
+        output.WriteRawTag(129, 3);
+        output.WriteDouble(DoubleField48);
+      }
+      if (DoubleField49 != 0D) {
+        output.WriteRawTag(137, 3);
+        output.WriteDouble(DoubleField49);
+      }
+      if (DoubleField50 != 0D) {
+        output.WriteRawTag(145, 3);
+        output.WriteDouble(DoubleField50);
+      }
+      if (DoubleField51 != 0D) {
+        output.WriteRawTag(153, 3);
+        output.WriteDouble(DoubleField51);
+      }
+      if (DoubleField52 != 0D) {
+        output.WriteRawTag(161, 3);
+        output.WriteDouble(DoubleField52);
+      }
+      if (DoubleField53 != 0D) {
+        output.WriteRawTag(169, 3);
+        output.WriteDouble(DoubleField53);
+      }
+      if (DoubleField54 != 0D) {
+        output.WriteRawTag(177, 3);
+        output.WriteDouble(DoubleField54);
+      }
+      if (DoubleField55 != 0D) {
+        output.WriteRawTag(185, 3);
+        output.WriteDouble(DoubleField55);
+      }
+      if (DoubleField56 != 0D) {
+        output.WriteRawTag(193, 3);
+        output.WriteDouble(DoubleField56);
+      }
+      if (DoubleField57 != 0D) {
+        output.WriteRawTag(201, 3);
+        output.WriteDouble(DoubleField57);
+      }
+      if (DoubleField58 != 0D) {
+        output.WriteRawTag(209, 3);
+        output.WriteDouble(DoubleField58);
+      }
+      if (Int64Field59 != 0L) {
+        output.WriteRawTag(216, 3);
+        output.WriteInt64(Int64Field59);
+      }
+      if (Int64Field60 != 0L) {
+        output.WriteRawTag(224, 3);
+        output.WriteInt64(Int64Field60);
+      }
+      if (DoubleField62 != 0D) {
+        output.WriteRawTag(241, 3);
+        output.WriteDouble(DoubleField62);
+      }
+      if (DoubleField65 != 0D) {
+        output.WriteRawTag(137, 4);
+        output.WriteDouble(DoubleField65);
+      }
+      if (DoubleField66 != 0D) {
+        output.WriteRawTag(145, 4);
+        output.WriteDouble(DoubleField66);
+      }
+      if (DoubleField67 != 0D) {
+        output.WriteRawTag(153, 4);
+        output.WriteDouble(DoubleField67);
+      }
+      if (DoubleField68 != 0D) {
+        output.WriteRawTag(161, 4);
+        output.WriteDouble(DoubleField68);
+      }
+      if (DoubleField69 != 0D) {
+        output.WriteRawTag(169, 4);
+        output.WriteDouble(DoubleField69);
+      }
+      if (DoubleField70 != 0D) {
+        output.WriteRawTag(177, 4);
+        output.WriteDouble(DoubleField70);
+      }
+      if (DoubleField71 != 0D) {
+        output.WriteRawTag(185, 4);
+        output.WriteDouble(DoubleField71);
+      }
+      if (DoubleField72 != 0D) {
+        output.WriteRawTag(193, 4);
+        output.WriteDouble(DoubleField72);
+      }
+      if (StringField73.Length != 0) {
+        output.WriteRawTag(202, 4);
+        output.WriteString(StringField73);
+      }
+      if (StringField74.Length != 0) {
+        output.WriteRawTag(210, 4);
+        output.WriteString(StringField74);
+      }
+      if (DoubleField75 != 0D) {
+        output.WriteRawTag(217, 4);
+        output.WriteDouble(DoubleField75);
+      }
+      if (DoubleField77 != 0D) {
+        output.WriteRawTag(233, 4);
+        output.WriteDouble(DoubleField77);
+      }
+      if (DoubleField78 != 0D) {
+        output.WriteRawTag(241, 4);
+        output.WriteDouble(DoubleField78);
+      }
+      if (DoubleField79 != 0D) {
+        output.WriteRawTag(249, 4);
+        output.WriteDouble(DoubleField79);
+      }
+      if (EnumField80 != 0) {
+        output.WriteRawTag(128, 5);
+        output.WriteInt32(EnumField80);
+      }
+      if (EnumField81 != 0) {
+        output.WriteRawTag(136, 5);
+        output.WriteInt32(EnumField81);
+      }
+      if (Int64Field82 != 0L) {
+        output.WriteRawTag(144, 5);
+        output.WriteInt64(Int64Field82);
+      }
+      if (EnumField83 != 0) {
+        output.WriteRawTag(152, 5);
+        output.WriteInt32(EnumField83);
+      }
+      if (DoubleField84 != 0D) {
+        output.WriteRawTag(161, 5);
+        output.WriteDouble(DoubleField84);
+      }
+      if (Int64Field85 != 0L) {
+        output.WriteRawTag(168, 5);
+        output.WriteInt64(Int64Field85);
+      }
+      if (Int64Field86 != 0L) {
+        output.WriteRawTag(176, 5);
+        output.WriteInt64(Int64Field86);
+      }
+      if (Int64Field87 != 0L) {
+        output.WriteRawTag(184, 5);
+        output.WriteInt64(Int64Field87);
+      }
+      if (DoubleField88 != 0D) {
+        output.WriteRawTag(193, 5);
+        output.WriteDouble(DoubleField88);
+      }
+      if (DoubleField89 != 0D) {
+        output.WriteRawTag(201, 5);
+        output.WriteDouble(DoubleField89);
+      }
+      if (DoubleField90 != 0D) {
+        output.WriteRawTag(209, 5);
+        output.WriteDouble(DoubleField90);
+      }
+      if (DoubleField91 != 0D) {
+        output.WriteRawTag(217, 5);
+        output.WriteDouble(DoubleField91);
+      }
+      if (DoubleField92 != 0D) {
+        output.WriteRawTag(225, 5);
+        output.WriteDouble(DoubleField92);
+      }
+      if (DoubleField93 != 0D) {
+        output.WriteRawTag(233, 5);
+        output.WriteDouble(DoubleField93);
+      }
+      if (DoubleField94 != 0D) {
+        output.WriteRawTag(241, 5);
+        output.WriteDouble(DoubleField94);
+      }
+      if (DoubleField95 != 0D) {
+        output.WriteRawTag(249, 5);
+        output.WriteDouble(DoubleField95);
+      }
+      if (DoubleField96 != 0D) {
+        output.WriteRawTag(129, 6);
+        output.WriteDouble(DoubleField96);
+      }
+      if (DoubleField97 != 0D) {
+        output.WriteRawTag(137, 6);
+        output.WriteDouble(DoubleField97);
+      }
+      if (DoubleField98 != 0D) {
+        output.WriteRawTag(145, 6);
+        output.WriteDouble(DoubleField98);
+      }
+      if (DoubleField99 != 0D) {
+        output.WriteRawTag(153, 6);
+        output.WriteDouble(DoubleField99);
+      }
+      repeatedIntField100_.WriteTo(ref output, _repeated_repeatedIntField100_codec);
+      if (DoubleField101 != 0D) {
+        output.WriteRawTag(169, 6);
+        output.WriteDouble(DoubleField101);
+      }
+      if (DoubleField102 != 0D) {
+        output.WriteRawTag(177, 6);
+        output.WriteDouble(DoubleField102);
+      }
+      if (DoubleField103 != 0D) {
+        output.WriteRawTag(185, 6);
+        output.WriteDouble(DoubleField103);
+      }
+      if (DoubleField104 != 0D) {
+        output.WriteRawTag(193, 6);
+        output.WriteDouble(DoubleField104);
+      }
+      if (DoubleField105 != 0D) {
+        output.WriteRawTag(201, 6);
+        output.WriteDouble(DoubleField105);
+      }
+      if (DoubleField106 != 0D) {
+        output.WriteRawTag(209, 6);
+        output.WriteDouble(DoubleField106);
+      }
+      if (Int64Field107 != 0L) {
+        output.WriteRawTag(216, 6);
+        output.WriteInt64(Int64Field107);
+      }
+      if (DoubleField108 != 0D) {
+        output.WriteRawTag(225, 6);
+        output.WriteDouble(DoubleField108);
+      }
+      if (DoubleField109 != 0D) {
+        output.WriteRawTag(233, 6);
+        output.WriteDouble(DoubleField109);
+      }
+      if (Int64Field110 != 0L) {
+        output.WriteRawTag(240, 6);
+        output.WriteInt64(Int64Field110);
+      }
+      if (DoubleField111 != 0D) {
+        output.WriteRawTag(249, 6);
+        output.WriteDouble(DoubleField111);
+      }
+      if (Int64Field112 != 0L) {
+        output.WriteRawTag(128, 7);
+        output.WriteInt64(Int64Field112);
+      }
+      if (DoubleField113 != 0D) {
+        output.WriteRawTag(137, 7);
+        output.WriteDouble(DoubleField113);
+      }
+      if (Int64Field114 != 0L) {
+        output.WriteRawTag(144, 7);
+        output.WriteInt64(Int64Field114);
+      }
+      if (Int64Field115 != 0L) {
+        output.WriteRawTag(152, 7);
+        output.WriteInt64(Int64Field115);
+      }
+      if (DoubleField116 != 0D) {
+        output.WriteRawTag(161, 7);
+        output.WriteDouble(DoubleField116);
+      }
+      if (Int64Field117 != 0L) {
+        output.WriteRawTag(168, 7);
+        output.WriteInt64(Int64Field117);
+      }
+      if (DoubleField118 != 0D) {
+        output.WriteRawTag(177, 7);
+        output.WriteDouble(DoubleField118);
+      }
+      if (DoubleField119 != 0D) {
+        output.WriteRawTag(185, 7);
+        output.WriteDouble(DoubleField119);
+      }
+      if (DoubleField120 != 0D) {
+        output.WriteRawTag(193, 7);
+        output.WriteDouble(DoubleField120);
+      }
+      if (DoubleField121 != 0D) {
+        output.WriteRawTag(201, 7);
+        output.WriteDouble(DoubleField121);
+      }
+      if (DoubleField122 != 0D) {
+        output.WriteRawTag(209, 7);
+        output.WriteDouble(DoubleField122);
+      }
+      if (DoubleField123 != 0D) {
+        output.WriteRawTag(217, 7);
+        output.WriteDouble(DoubleField123);
+      }
+      if (DoubleField124 != 0D) {
+        output.WriteRawTag(225, 7);
+        output.WriteDouble(DoubleField124);
+      }
+      if (Int64Field125 != 0L) {
+        output.WriteRawTag(232, 7);
+        output.WriteInt64(Int64Field125);
+      }
+      if (Int64Field126 != 0L) {
+        output.WriteRawTag(240, 7);
+        output.WriteInt64(Int64Field126);
+      }
+      if (Int64Field127 != 0L) {
+        output.WriteRawTag(248, 7);
+        output.WriteInt64(Int64Field127);
+      }
+      if (DoubleField128 != 0D) {
+        output.WriteRawTag(129, 8);
+        output.WriteDouble(DoubleField128);
+      }
+      if (DoubleField129 != 0D) {
+        output.WriteRawTag(137, 8);
+        output.WriteDouble(DoubleField129);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;

+ 198 - 0
csharp/src/Google.Protobuf.Benchmarks/WriteMessagesBenchmark.cs

@@ -0,0 +1,198 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2019 Google Inc.  All rights reserved.
+// https://github.com/protocolbuffers/protobuf
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using BenchmarkDotNet.Attributes;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Buffers;
+using Google.Protobuf.WellKnownTypes;
+
+namespace Google.Protobuf.Benchmarks
+{
+    /// <summary>
+    /// Benchmark that tests writing performance for various messages.
+    /// </summary>
+    [MemoryDiagnoser]
+    public class WriteMessagesBenchmark
+    {
+        const int MaxMessages = 100;
+
+        SubTest manyWrapperFieldsTest = new SubTest(ParseMessagesBenchmark.CreateManyWrapperFieldsMessage(), MaxMessages);
+        SubTest manyPrimitiveFieldsTest = new SubTest(ParseMessagesBenchmark.CreateManyPrimitiveFieldsMessage(), MaxMessages);
+        SubTest emptyMessageTest = new SubTest(new Empty(), MaxMessages);
+
+        public IEnumerable<int> MessageCountValues => new[] { 10, 100 };
+
+        [GlobalSetup]
+        public void GlobalSetup()
+        {
+        }
+
+        [Benchmark]
+        public byte[] ManyWrapperFieldsMessage_ToByteArray()
+        {
+            return manyWrapperFieldsTest.ToByteArray();
+        }
+
+        [Benchmark]
+        public byte[] ManyWrapperFieldsMessage_WriteToCodedOutputStream()
+        {
+            return manyWrapperFieldsTest.WriteToCodedOutputStream_PreAllocatedBuffer();
+        }
+
+        [Benchmark]
+        public byte[] ManyWrapperFieldsMessage_WriteToSpan()
+        {
+            return manyWrapperFieldsTest.WriteToSpan_PreAllocatedBuffer();
+        }
+
+
+        [Benchmark]
+        public byte[] ManyPrimitiveFieldsMessage_ToByteArray()
+        {
+            return manyPrimitiveFieldsTest.ToByteArray();
+        }
+
+        [Benchmark]
+        public byte[] ManyPrimitiveFieldsMessage_WriteToCodedOutputStream()
+        {
+            return manyPrimitiveFieldsTest.WriteToCodedOutputStream_PreAllocatedBuffer();
+        }
+
+        [Benchmark]
+        public byte[] ManyPrimitiveFieldsMessage_WriteToSpan()
+        {
+            return manyPrimitiveFieldsTest.WriteToSpan_PreAllocatedBuffer();
+        }
+
+        [Benchmark]
+        public byte[] EmptyMessage_ToByteArray()
+        {
+            return emptyMessageTest.ToByteArray();
+        }
+
+        [Benchmark]
+        public byte[] EmptyMessage_WriteToCodedOutputStream()
+        {
+            return emptyMessageTest.WriteToCodedOutputStream_PreAllocatedBuffer();
+        }
+
+        [Benchmark]
+        public byte[] EmptyMessage_WriteToSpan()
+        {
+            return emptyMessageTest.WriteToSpan_PreAllocatedBuffer();
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(MessageCountValues))]
+        public void ManyWrapperFieldsMessage_WriteDelimitedMessagesToCodedOutputStream(int messageCount)
+        {
+            manyWrapperFieldsTest.WriteDelimitedMessagesToCodedOutputStream_PreAllocatedBuffer(messageCount);
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(MessageCountValues))]
+        public void ManyWrapperFieldsMessage_WriteDelimitedMessagesToSpan(int messageCount)
+        {
+            manyWrapperFieldsTest.WriteDelimitedMessagesToSpan_PreAllocatedBuffer(messageCount);
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(MessageCountValues))]
+        public void ManyPrimitiveFieldsMessage_WriteDelimitedMessagesToCodedOutputStream(int messageCount)
+        {
+            manyPrimitiveFieldsTest.WriteDelimitedMessagesToCodedOutputStream_PreAllocatedBuffer(messageCount);
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(MessageCountValues))]
+        public void ManyPrimitiveFieldsMessage_WriteDelimitedMessagesToSpan(int messageCount)
+        {
+            manyPrimitiveFieldsTest.WriteDelimitedMessagesToSpan_PreAllocatedBuffer(messageCount);
+        }
+
+        private class SubTest
+        {
+            private readonly IMessage message;
+            private readonly byte[] outputBuffer;
+            private readonly byte[] multipleMessagesOutputBuffer;
+
+            public SubTest(IMessage message, int maxMessageCount)
+            {
+                this.message = message;
+
+                int messageSize = message.CalculateSize();
+                this.outputBuffer = new byte[messageSize];
+                this.multipleMessagesOutputBuffer = new byte[maxMessageCount * (messageSize + CodedOutputStream.ComputeLengthSize(messageSize))];
+            }
+
+            public byte[] ToByteArray() => message.ToByteArray();
+
+            public byte[] WriteToCodedOutputStream_PreAllocatedBuffer()
+            {
+                var cos = new CodedOutputStream(outputBuffer);  // use pre-existing output buffer
+                message.WriteTo(cos);
+                return outputBuffer;
+            }
+
+            public byte[] WriteToSpan_PreAllocatedBuffer()
+            {
+                var span = new Span<byte>(outputBuffer);  // use pre-existing output buffer
+                message.WriteTo(span);
+                return outputBuffer;
+            }
+
+            public byte[] WriteDelimitedMessagesToCodedOutputStream_PreAllocatedBuffer(int messageCount)
+            {
+                var cos = new CodedOutputStream(multipleMessagesOutputBuffer);  // use pre-existing output buffer
+                for (int i = 0; i < messageCount; i++)
+                {
+                    cos.WriteMessage(message);
+                }
+                return multipleMessagesOutputBuffer;
+            }
+
+            public byte[] WriteDelimitedMessagesToSpan_PreAllocatedBuffer(int messageCount)
+            {
+                var span = new Span<byte>(multipleMessagesOutputBuffer);  // use pre-existing output buffer
+                WriteContext.Initialize(ref span, out WriteContext ctx);
+                for (int i = 0; i < messageCount; i++)
+                {
+                    ctx.WriteMessage(message);
+                }
+                return multipleMessagesOutputBuffer;
+            }
+        }
+    }
+}

+ 467 - 0
csharp/src/Google.Protobuf.Benchmarks/WriteRawPrimitivesBenchmark.cs

@@ -0,0 +1,467 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2019 Google Inc.  All rights reserved.
+// https://github.com/protocolbuffers/protobuf
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using BenchmarkDotNet.Attributes;
+using System;
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.IO;
+using System.Buffers;
+using System.Text;
+
+namespace Google.Protobuf.Benchmarks
+{
+    /// <summary>
+    /// Benchmarks throughput when writing raw primitives.
+    /// </summary>
+    [MemoryDiagnoser]
+    public class WriteRawPrimitivesBenchmark
+    {
+        // key is the encodedSize of varint values
+        Dictionary<int, uint[]> varint32Values;
+        Dictionary<int, ulong[]> varint64Values;
+
+        double[] doubleValues;
+        float[] floatValues;
+
+        // key is the encodedSize of string values
+        Dictionary<int, string[]> stringValues;
+
+        // key is the encodedSize of string values
+        Dictionary<int, string[]> nonAsciiStringValues;
+
+        // key is the encodedSize of string values
+        Dictionary<int, ByteString[]> byteStringValues;
+
+        // the buffer to which all the data will be written
+        byte[] outputBuffer;
+
+        Random random = new Random(417384220);  // random but deterministic seed
+
+        public IEnumerable<int> StringEncodedSizes => new[] { 1, 4, 10, 105, 10080 };
+
+        public IEnumerable<int> NonAsciiStringEncodedSizes => new[] { 4, 10, 105, 10080 };
+
+        [GlobalSetup]
+        public void GlobalSetup()
+        {
+            outputBuffer = new byte[BytesToWrite];
+
+            varint32Values = new Dictionary<int, uint[]>();
+            varint64Values = new Dictionary<int, ulong[]>();
+            for (int encodedSize = 1; encodedSize <= 10; encodedSize++)
+            {
+                if (encodedSize <= 5)
+                {
+                    varint32Values.Add(encodedSize, CreateRandomVarints32(random, BytesToWrite / encodedSize, encodedSize));
+                }
+                varint64Values.Add(encodedSize, CreateRandomVarints64(random, BytesToWrite / encodedSize, encodedSize));
+            }
+
+            doubleValues = CreateRandomDoubles(random, BytesToWrite / sizeof(double));
+            floatValues = CreateRandomFloats(random, BytesToWrite / sizeof(float));
+
+            stringValues = new Dictionary<int, string[]>();
+
+            byteStringValues = new Dictionary<int, ByteString[]>();
+            foreach(var encodedSize in StringEncodedSizes)
+            {
+                stringValues.Add(encodedSize, CreateStrings(BytesToWrite / encodedSize, encodedSize));
+                byteStringValues.Add(encodedSize, CreateByteStrings(BytesToWrite / encodedSize, encodedSize));
+            }
+
+            nonAsciiStringValues = new Dictionary<int, string[]>();
+            foreach(var encodedSize in NonAsciiStringEncodedSizes)
+            {
+                nonAsciiStringValues.Add(encodedSize, CreateNonAsciiStrings(BytesToWrite / encodedSize, encodedSize));
+            }
+        }
+
+        // Total number of bytes that each benchmark will write.
+        // Measuring the time taken to write buffer of given size makes it easier to compare parsing speed for different
+        // types and makes it easy to calculate the througput (in MB/s)
+        // 10800 bytes is chosen because it is divisible by all possible encoded sizes for all primitive types {1..10}
+        [Params(10080)]
+        public int BytesToWrite { get; set; }
+
+        [Benchmark]
+        [Arguments(1)]
+        [Arguments(2)]
+        [Arguments(3)]
+        [Arguments(4)]
+        [Arguments(5)]
+        public void WriteRawVarint32_CodedOutputStream(int encodedSize)
+        {
+            var values = varint32Values[encodedSize];
+            var cos = new CodedOutputStream(outputBuffer);
+            foreach (var value in values)
+            {
+                cos.WriteRawVarint32(value);
+            }
+            cos.Flush();
+            cos.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        [Arguments(1)]
+        [Arguments(2)]
+        [Arguments(3)]
+        [Arguments(4)]
+        [Arguments(5)]
+        public void WriteRawVarint32_WriteContext(int encodedSize)
+        {
+            var values = varint32Values[encodedSize];
+            var span = new Span<byte>(outputBuffer);
+            WriteContext.Initialize(ref span, out WriteContext ctx);
+            foreach (var value in values)
+            {
+                ctx.WriteUInt32(value);
+            }
+            ctx.Flush();
+            ctx.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        [Arguments(1)]
+        [Arguments(2)]
+        [Arguments(3)]
+        [Arguments(4)]
+        [Arguments(5)]
+        [Arguments(6)]
+        [Arguments(7)]
+        [Arguments(8)]
+        [Arguments(9)]
+        [Arguments(10)]
+        public void WriteRawVarint64_CodedOutputStream(int encodedSize)
+        {
+            var values = varint64Values[encodedSize];
+            var cos = new CodedOutputStream(outputBuffer);
+            foreach (var value in values)
+            {
+                cos.WriteRawVarint64(value);
+            }
+            cos.Flush();
+            cos.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        [Arguments(1)]
+        [Arguments(2)]
+        [Arguments(3)]
+        [Arguments(4)]
+        [Arguments(5)]
+        [Arguments(6)]
+        [Arguments(7)]
+        [Arguments(8)]
+        [Arguments(9)]
+        [Arguments(10)]
+        public void WriteRawVarint64_WriteContext(int encodedSize)
+        {
+            var values = varint64Values[encodedSize];
+            var span = new Span<byte>(outputBuffer);
+            WriteContext.Initialize(ref span, out WriteContext ctx);
+            foreach (var value in values)
+            {
+                ctx.WriteUInt64(value);
+            }
+            ctx.Flush();
+            ctx.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        public void WriteFixed32_CodedOutputStream()
+        {
+            const int encodedSize = sizeof(uint);
+            var cos = new CodedOutputStream(outputBuffer);
+            for(int i = 0; i < BytesToWrite / encodedSize; i++)
+            {
+                cos.WriteFixed32(12345);
+            }
+            cos.Flush();
+            cos.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        public void WriteFixed32_WriteContext()
+        {
+            const int encodedSize = sizeof(uint);
+            var span = new Span<byte>(outputBuffer);
+            WriteContext.Initialize(ref span, out WriteContext ctx);
+            for (uint i = 0; i < BytesToWrite / encodedSize; i++)
+            {
+                ctx.WriteFixed32(12345);
+            }
+            ctx.Flush();
+            ctx.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        public void WriteFixed64_CodedOutputStream()
+        {
+            const int encodedSize = sizeof(ulong);
+            var cos = new CodedOutputStream(outputBuffer);
+            for(int i = 0; i < BytesToWrite / encodedSize; i++)
+            {
+                cos.WriteFixed64(123456789);
+            }
+            cos.Flush();
+            cos.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        public void WriteFixed64_WriteContext()
+        {
+            const int encodedSize = sizeof(ulong);
+            var span = new Span<byte>(outputBuffer);
+            WriteContext.Initialize(ref span, out WriteContext ctx);
+            for (uint i = 0; i < BytesToWrite / encodedSize; i++)
+            {
+                ctx.WriteFixed64(123456789);
+            }
+            ctx.Flush();
+            ctx.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        public void WriteRawFloat_CodedOutputStream()
+        {
+            var cos = new CodedOutputStream(outputBuffer);
+            foreach (var value in floatValues)
+            {
+                cos.WriteFloat(value);
+            }
+            cos.Flush();
+            cos.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        public void WriteRawFloat_WriteContext()
+        {
+            var span = new Span<byte>(outputBuffer);
+            WriteContext.Initialize(ref span, out WriteContext ctx);
+            foreach (var value in floatValues)
+            {
+                ctx.WriteFloat(value);
+            }
+            ctx.Flush();
+            ctx.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        public void WriteRawDouble_CodedOutputStream()
+        {
+            var cos = new CodedOutputStream(outputBuffer);
+            foreach (var value in doubleValues)
+            {
+                cos.WriteDouble(value);
+            }
+            cos.Flush();
+            cos.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        public void WriteRawDouble_WriteContext()
+        {
+            var span = new Span<byte>(outputBuffer);
+            WriteContext.Initialize(ref span, out WriteContext ctx);
+            foreach (var value in doubleValues)
+            {
+                ctx.WriteDouble(value);
+            }
+            ctx.Flush();
+            ctx.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(StringEncodedSizes))]
+        public void WriteString_CodedOutputStream(int encodedSize)
+        {
+            var values = stringValues[encodedSize];
+            var cos = new CodedOutputStream(outputBuffer);
+            foreach (var value in values)
+            {
+                cos.WriteString(value);
+            }
+            cos.Flush();
+            cos.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(StringEncodedSizes))]
+        public void WriteString_WriteContext(int encodedSize)
+        {
+            var values = stringValues[encodedSize];
+            var span = new Span<byte>(outputBuffer);
+            WriteContext.Initialize(ref span, out WriteContext ctx);
+            foreach (var value in values)
+            {
+                ctx.WriteString(value);
+            }
+            ctx.Flush();
+            ctx.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(NonAsciiStringEncodedSizes))]
+        public void WriteNonAsciiString_CodedOutputStream(int encodedSize)
+        {
+            var values = nonAsciiStringValues[encodedSize];
+            var cos = new CodedOutputStream(outputBuffer);
+            foreach (var value in values)
+            {
+                cos.WriteString(value);
+            }
+            cos.Flush();
+            cos.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(NonAsciiStringEncodedSizes))]
+        public void WriteNonAsciiString_WriteContext(int encodedSize)
+        {
+            var values = nonAsciiStringValues[encodedSize];
+            var span = new Span<byte>(outputBuffer);
+            WriteContext.Initialize(ref span, out WriteContext ctx);
+            foreach (var value in values)
+            {
+                ctx.WriteString(value);
+            }
+            ctx.Flush();
+            ctx.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(StringEncodedSizes))]
+        public void WriteBytes_CodedOutputStream(int encodedSize)
+        {
+            var values = byteStringValues[encodedSize];
+            var cos = new CodedOutputStream(outputBuffer);
+            foreach (var value in values)
+            {
+                cos.WriteBytes(value);
+            }
+            cos.Flush();
+            cos.CheckNoSpaceLeft();
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(StringEncodedSizes))]
+        public void WriteBytes_WriteContext(int encodedSize)
+        {
+            var values = byteStringValues[encodedSize];
+            var span = new Span<byte>(outputBuffer);
+            WriteContext.Initialize(ref span, out WriteContext ctx);
+            foreach (var value in values)
+            {
+                ctx.WriteBytes(value);
+            }
+            ctx.Flush();
+            ctx.CheckNoSpaceLeft();
+        }
+
+        private static uint[] CreateRandomVarints32(Random random, int valueCount, int encodedSize)
+        {
+            var result = new uint[valueCount];
+            for (int i = 0; i < valueCount; i++)
+            {
+                result[i] = (uint) ParseRawPrimitivesBenchmark.RandomUnsignedVarint(random, encodedSize, true);
+            }
+            return result;
+        }
+
+        private static ulong[] CreateRandomVarints64(Random random, int valueCount, int encodedSize)
+        {            
+            var result = new ulong[valueCount];
+            for (int i = 0; i < valueCount; i++)
+            {
+                result[i] = ParseRawPrimitivesBenchmark.RandomUnsignedVarint(random, encodedSize, false);
+            }
+            return result;
+        }
+
+        private static float[] CreateRandomFloats(Random random, int valueCount)
+        {
+            var result = new float[valueCount];
+            for (int i = 0; i < valueCount; i++)
+            {
+                result[i] = (float)random.NextDouble();
+            }
+            return result;
+        }
+
+        private static double[] CreateRandomDoubles(Random random, int valueCount)
+        {
+            var result = new double[valueCount];
+            for (int i = 0; i < valueCount; i++)
+            {
+                result[i] = random.NextDouble();
+            }
+            return result;
+        }
+
+        private static string[] CreateStrings(int valueCount, int encodedSize)
+        {
+            var str = ParseRawPrimitivesBenchmark.CreateStringWithEncodedSize(encodedSize);
+
+            var result = new string[valueCount];
+            for (int i = 0; i < valueCount; i++)
+            {
+                result[i] = str;
+            }
+            return result;
+        }
+
+        private static string[] CreateNonAsciiStrings(int valueCount, int encodedSize)
+        {
+            var str = ParseRawPrimitivesBenchmark.CreateNonAsciiStringWithEncodedSize(encodedSize);
+
+            var result = new string[valueCount];
+            for (int i = 0; i < valueCount; i++)
+            {
+                result[i] = str;
+            }
+            return result;
+        }
+
+        private static ByteString[] CreateByteStrings(int valueCount, int encodedSize)
+        {
+            var str = ParseRawPrimitivesBenchmark.CreateStringWithEncodedSize(encodedSize);
+
+            var result = new ByteString[valueCount];
+            for (int i = 0; i < valueCount; i++)
+            {
+                result[i] = ByteString.CopyFrom(Encoding.UTF8.GetBytes(str));
+            }
+            return result;
+        }
+    }
+}

+ 125 - 0
csharp/src/Google.Protobuf.Conformance/Conformance.cs

@@ -189,11 +189,25 @@ namespace Conformance {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       failure_.WriteTo(output, _repeated_failure_codec);
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      failure_.WriteTo(ref output, _repeated_failure_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -513,6 +527,9 @@ namespace Conformance {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (payloadCase_ == PayloadOneofCase.ProtobufPayload) {
         output.WriteRawTag(10);
         output.WriteBytes(ProtobufPayload);
@@ -552,7 +569,53 @@ namespace Conformance {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (payloadCase_ == PayloadOneofCase.ProtobufPayload) {
+        output.WriteRawTag(10);
+        output.WriteBytes(ProtobufPayload);
+      }
+      if (payloadCase_ == PayloadOneofCase.JsonPayload) {
+        output.WriteRawTag(18);
+        output.WriteString(JsonPayload);
+      }
+      if (RequestedOutputFormat != global::Conformance.WireFormat.Unspecified) {
+        output.WriteRawTag(24);
+        output.WriteEnum((int) RequestedOutputFormat);
+      }
+      if (MessageType.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(MessageType);
+      }
+      if (TestCategory != global::Conformance.TestCategory.UnspecifiedTest) {
+        output.WriteRawTag(40);
+        output.WriteEnum((int) TestCategory);
+      }
+      if (jspbEncodingOptions_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(JspbEncodingOptions);
+      }
+      if (payloadCase_ == PayloadOneofCase.JspbPayload) {
+        output.WriteRawTag(58);
+        output.WriteString(JspbPayload);
+      }
+      if (payloadCase_ == PayloadOneofCase.TextPayload) {
+        output.WriteRawTag(66);
+        output.WriteString(TextPayload);
+      }
+      if (PrintUnknownFields != false) {
+        output.WriteRawTag(72);
+        output.WriteBool(PrintUnknownFields);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1009,6 +1072,9 @@ namespace Conformance {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (resultCase_ == ResultOneofCase.ParseError) {
         output.WriteRawTag(10);
         output.WriteString(ParseError);
@@ -1044,7 +1110,49 @@ namespace Conformance {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (resultCase_ == ResultOneofCase.ParseError) {
+        output.WriteRawTag(10);
+        output.WriteString(ParseError);
+      }
+      if (resultCase_ == ResultOneofCase.RuntimeError) {
+        output.WriteRawTag(18);
+        output.WriteString(RuntimeError);
+      }
+      if (resultCase_ == ResultOneofCase.ProtobufPayload) {
+        output.WriteRawTag(26);
+        output.WriteBytes(ProtobufPayload);
+      }
+      if (resultCase_ == ResultOneofCase.JsonPayload) {
+        output.WriteRawTag(34);
+        output.WriteString(JsonPayload);
+      }
+      if (resultCase_ == ResultOneofCase.Skipped) {
+        output.WriteRawTag(42);
+        output.WriteString(Skipped);
+      }
+      if (resultCase_ == ResultOneofCase.SerializeError) {
+        output.WriteRawTag(50);
+        output.WriteString(SerializeError);
+      }
+      if (resultCase_ == ResultOneofCase.JspbPayload) {
+        output.WriteRawTag(58);
+        output.WriteString(JspbPayload);
+      }
+      if (resultCase_ == ResultOneofCase.TextPayload) {
+        output.WriteRawTag(66);
+        output.WriteString(TextPayload);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1299,6 +1407,9 @@ namespace Conformance {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (UseJspbArrayAnyFormat != false) {
         output.WriteRawTag(8);
         output.WriteBool(UseJspbArrayAnyFormat);
@@ -1306,7 +1417,21 @@ namespace Conformance {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (UseJspbArrayAnyFormat != false) {
+        output.WriteRawTag(8);
+        output.WriteBool(UseJspbArrayAnyFormat);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 132 - 0
csharp/src/Google.Protobuf.Test.TestProtos/MapUnittestProto3.cs

@@ -466,6 +466,9 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       mapInt32Int32_.WriteTo(output, _map_mapInt32Int32_codec);
       mapInt64Int64_.WriteTo(output, _map_mapInt64Int64_codec);
       mapUint32Uint32_.WriteTo(output, _map_mapUint32Uint32_codec);
@@ -486,7 +489,34 @@ namespace Google.Protobuf.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      mapInt32Int32_.WriteTo(ref output, _map_mapInt32Int32_codec);
+      mapInt64Int64_.WriteTo(ref output, _map_mapInt64Int64_codec);
+      mapUint32Uint32_.WriteTo(ref output, _map_mapUint32Uint32_codec);
+      mapUint64Uint64_.WriteTo(ref output, _map_mapUint64Uint64_codec);
+      mapSint32Sint32_.WriteTo(ref output, _map_mapSint32Sint32_codec);
+      mapSint64Sint64_.WriteTo(ref output, _map_mapSint64Sint64_codec);
+      mapFixed32Fixed32_.WriteTo(ref output, _map_mapFixed32Fixed32_codec);
+      mapFixed64Fixed64_.WriteTo(ref output, _map_mapFixed64Fixed64_codec);
+      mapSfixed32Sfixed32_.WriteTo(ref output, _map_mapSfixed32Sfixed32_codec);
+      mapSfixed64Sfixed64_.WriteTo(ref output, _map_mapSfixed64Sfixed64_codec);
+      mapInt32Float_.WriteTo(ref output, _map_mapInt32Float_codec);
+      mapInt32Double_.WriteTo(ref output, _map_mapInt32Double_codec);
+      mapBoolBool_.WriteTo(ref output, _map_mapBoolBool_codec);
+      mapStringString_.WriteTo(ref output, _map_mapStringString_codec);
+      mapInt32Bytes_.WriteTo(ref output, _map_mapInt32Bytes_codec);
+      mapInt32Enum_.WriteTo(ref output, _map_mapInt32Enum_codec);
+      mapInt32ForeignMessage_.WriteTo(ref output, _map_mapInt32ForeignMessage_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -790,6 +820,9 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (testMap_ != null) {
         output.WriteRawTag(10);
         output.WriteMessage(TestMap);
@@ -797,8 +830,22 @@ namespace Google.Protobuf.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (testMap_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(TestMap);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -953,11 +1000,25 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       mapInt32Message_.WriteTo(output, _map_mapInt32Message_codec);
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      mapInt32Message_.WriteTo(ref output, _map_mapInt32Message_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1116,12 +1177,27 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       map1_.WriteTo(output, _map_map1_codec);
       map2_.WriteTo(output, _map_map2_codec);
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      map1_.WriteTo(ref output, _map_map1_codec);
+      map2_.WriteTo(ref output, _map_map2_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1456,6 +1532,9 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       mapInt32Int32_.WriteTo(output, _map_mapInt32Int32_codec);
       mapInt64Int64_.WriteTo(output, _map_mapInt64Int64_codec);
       mapUint32Uint32_.WriteTo(output, _map_mapUint32Uint32_codec);
@@ -1474,8 +1553,33 @@ namespace Google.Protobuf.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      mapInt32Int32_.WriteTo(ref output, _map_mapInt32Int32_codec);
+      mapInt64Int64_.WriteTo(ref output, _map_mapInt64Int64_codec);
+      mapUint32Uint32_.WriteTo(ref output, _map_mapUint32Uint32_codec);
+      mapUint64Uint64_.WriteTo(ref output, _map_mapUint64Uint64_codec);
+      mapSint32Sint32_.WriteTo(ref output, _map_mapSint32Sint32_codec);
+      mapSint64Sint64_.WriteTo(ref output, _map_mapSint64Sint64_codec);
+      mapFixed32Fixed32_.WriteTo(ref output, _map_mapFixed32Fixed32_codec);
+      mapFixed64Fixed64_.WriteTo(ref output, _map_mapFixed64Fixed64_codec);
+      mapSfixed32Sfixed32_.WriteTo(ref output, _map_mapSfixed32Sfixed32_codec);
+      mapSfixed64Sfixed64_.WriteTo(ref output, _map_mapSfixed64Sfixed64_codec);
+      mapInt32Float_.WriteTo(ref output, _map_mapInt32Float_codec);
+      mapInt32Double_.WriteTo(ref output, _map_mapInt32Double_codec);
+      mapBoolBool_.WriteTo(ref output, _map_mapBoolBool_codec);
+      mapInt32Enum_.WriteTo(ref output, _map_mapInt32Enum_codec);
+      mapInt32ForeignMessage_.WriteTo(ref output, _map_mapInt32ForeignMessage_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -1761,12 +1865,26 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       type_.WriteTo(output, _map_type_codec);
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      type_.WriteTo(ref output, _map_type_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -1922,12 +2040,26 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       entry_.WriteTo(output, _map_entry_codec);
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      entry_.WriteTo(ref output, _map_entry_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;

+ 446 - 0
csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs

@@ -2561,6 +2561,9 @@ namespace ProtobufTestMessages.Proto2 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (HasOptionalInt32) {
         output.WriteRawTag(8);
         output.WriteInt32(OptionalInt32);
@@ -2836,7 +2839,289 @@ namespace ProtobufTestMessages.Proto2 {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (HasOptionalInt32) {
+        output.WriteRawTag(8);
+        output.WriteInt32(OptionalInt32);
+      }
+      if (HasOptionalInt64) {
+        output.WriteRawTag(16);
+        output.WriteInt64(OptionalInt64);
+      }
+      if (HasOptionalUint32) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(OptionalUint32);
+      }
+      if (HasOptionalUint64) {
+        output.WriteRawTag(32);
+        output.WriteUInt64(OptionalUint64);
+      }
+      if (HasOptionalSint32) {
+        output.WriteRawTag(40);
+        output.WriteSInt32(OptionalSint32);
+      }
+      if (HasOptionalSint64) {
+        output.WriteRawTag(48);
+        output.WriteSInt64(OptionalSint64);
+      }
+      if (HasOptionalFixed32) {
+        output.WriteRawTag(61);
+        output.WriteFixed32(OptionalFixed32);
+      }
+      if (HasOptionalFixed64) {
+        output.WriteRawTag(65);
+        output.WriteFixed64(OptionalFixed64);
+      }
+      if (HasOptionalSfixed32) {
+        output.WriteRawTag(77);
+        output.WriteSFixed32(OptionalSfixed32);
+      }
+      if (HasOptionalSfixed64) {
+        output.WriteRawTag(81);
+        output.WriteSFixed64(OptionalSfixed64);
+      }
+      if (HasOptionalFloat) {
+        output.WriteRawTag(93);
+        output.WriteFloat(OptionalFloat);
+      }
+      if (HasOptionalDouble) {
+        output.WriteRawTag(97);
+        output.WriteDouble(OptionalDouble);
+      }
+      if (HasOptionalBool) {
+        output.WriteRawTag(104);
+        output.WriteBool(OptionalBool);
+      }
+      if (HasOptionalString) {
+        output.WriteRawTag(114);
+        output.WriteString(OptionalString);
+      }
+      if (HasOptionalBytes) {
+        output.WriteRawTag(122);
+        output.WriteBytes(OptionalBytes);
+      }
+      if (optionalNestedMessage_ != null) {
+        output.WriteRawTag(146, 1);
+        output.WriteMessage(OptionalNestedMessage);
+      }
+      if (optionalForeignMessage_ != null) {
+        output.WriteRawTag(154, 1);
+        output.WriteMessage(OptionalForeignMessage);
+      }
+      if (HasOptionalNestedEnum) {
+        output.WriteRawTag(168, 1);
+        output.WriteEnum((int) OptionalNestedEnum);
+      }
+      if (HasOptionalForeignEnum) {
+        output.WriteRawTag(176, 1);
+        output.WriteEnum((int) OptionalForeignEnum);
+      }
+      if (HasOptionalStringPiece) {
+        output.WriteRawTag(194, 1);
+        output.WriteString(OptionalStringPiece);
+      }
+      if (HasOptionalCord) {
+        output.WriteRawTag(202, 1);
+        output.WriteString(OptionalCord);
+      }
+      if (recursiveMessage_ != null) {
+        output.WriteRawTag(218, 1);
+        output.WriteMessage(RecursiveMessage);
+      }
+      repeatedInt32_.WriteTo(ref output, _repeated_repeatedInt32_codec);
+      repeatedInt64_.WriteTo(ref output, _repeated_repeatedInt64_codec);
+      repeatedUint32_.WriteTo(ref output, _repeated_repeatedUint32_codec);
+      repeatedUint64_.WriteTo(ref output, _repeated_repeatedUint64_codec);
+      repeatedSint32_.WriteTo(ref output, _repeated_repeatedSint32_codec);
+      repeatedSint64_.WriteTo(ref output, _repeated_repeatedSint64_codec);
+      repeatedFixed32_.WriteTo(ref output, _repeated_repeatedFixed32_codec);
+      repeatedFixed64_.WriteTo(ref output, _repeated_repeatedFixed64_codec);
+      repeatedSfixed32_.WriteTo(ref output, _repeated_repeatedSfixed32_codec);
+      repeatedSfixed64_.WriteTo(ref output, _repeated_repeatedSfixed64_codec);
+      repeatedFloat_.WriteTo(ref output, _repeated_repeatedFloat_codec);
+      repeatedDouble_.WriteTo(ref output, _repeated_repeatedDouble_codec);
+      repeatedBool_.WriteTo(ref output, _repeated_repeatedBool_codec);
+      repeatedString_.WriteTo(ref output, _repeated_repeatedString_codec);
+      repeatedBytes_.WriteTo(ref output, _repeated_repeatedBytes_codec);
+      repeatedNestedMessage_.WriteTo(ref output, _repeated_repeatedNestedMessage_codec);
+      repeatedForeignMessage_.WriteTo(ref output, _repeated_repeatedForeignMessage_codec);
+      repeatedNestedEnum_.WriteTo(ref output, _repeated_repeatedNestedEnum_codec);
+      repeatedForeignEnum_.WriteTo(ref output, _repeated_repeatedForeignEnum_codec);
+      repeatedStringPiece_.WriteTo(ref output, _repeated_repeatedStringPiece_codec);
+      repeatedCord_.WriteTo(ref output, _repeated_repeatedCord_codec);
+      mapInt32Int32_.WriteTo(ref output, _map_mapInt32Int32_codec);
+      mapInt64Int64_.WriteTo(ref output, _map_mapInt64Int64_codec);
+      mapUint32Uint32_.WriteTo(ref output, _map_mapUint32Uint32_codec);
+      mapUint64Uint64_.WriteTo(ref output, _map_mapUint64Uint64_codec);
+      mapSint32Sint32_.WriteTo(ref output, _map_mapSint32Sint32_codec);
+      mapSint64Sint64_.WriteTo(ref output, _map_mapSint64Sint64_codec);
+      mapFixed32Fixed32_.WriteTo(ref output, _map_mapFixed32Fixed32_codec);
+      mapFixed64Fixed64_.WriteTo(ref output, _map_mapFixed64Fixed64_codec);
+      mapSfixed32Sfixed32_.WriteTo(ref output, _map_mapSfixed32Sfixed32_codec);
+      mapSfixed64Sfixed64_.WriteTo(ref output, _map_mapSfixed64Sfixed64_codec);
+      mapInt32Float_.WriteTo(ref output, _map_mapInt32Float_codec);
+      mapInt32Double_.WriteTo(ref output, _map_mapInt32Double_codec);
+      mapBoolBool_.WriteTo(ref output, _map_mapBoolBool_codec);
+      mapStringString_.WriteTo(ref output, _map_mapStringString_codec);
+      mapStringBytes_.WriteTo(ref output, _map_mapStringBytes_codec);
+      mapStringNestedMessage_.WriteTo(ref output, _map_mapStringNestedMessage_codec);
+      mapStringForeignMessage_.WriteTo(ref output, _map_mapStringForeignMessage_codec);
+      mapStringNestedEnum_.WriteTo(ref output, _map_mapStringNestedEnum_codec);
+      mapStringForeignEnum_.WriteTo(ref output, _map_mapStringForeignEnum_codec);
+      packedInt32_.WriteTo(ref output, _repeated_packedInt32_codec);
+      packedInt64_.WriteTo(ref output, _repeated_packedInt64_codec);
+      packedUint32_.WriteTo(ref output, _repeated_packedUint32_codec);
+      packedUint64_.WriteTo(ref output, _repeated_packedUint64_codec);
+      packedSint32_.WriteTo(ref output, _repeated_packedSint32_codec);
+      packedSint64_.WriteTo(ref output, _repeated_packedSint64_codec);
+      packedFixed32_.WriteTo(ref output, _repeated_packedFixed32_codec);
+      packedFixed64_.WriteTo(ref output, _repeated_packedFixed64_codec);
+      packedSfixed32_.WriteTo(ref output, _repeated_packedSfixed32_codec);
+      packedSfixed64_.WriteTo(ref output, _repeated_packedSfixed64_codec);
+      packedFloat_.WriteTo(ref output, _repeated_packedFloat_codec);
+      packedDouble_.WriteTo(ref output, _repeated_packedDouble_codec);
+      packedBool_.WriteTo(ref output, _repeated_packedBool_codec);
+      packedNestedEnum_.WriteTo(ref output, _repeated_packedNestedEnum_codec);
+      unpackedInt32_.WriteTo(ref output, _repeated_unpackedInt32_codec);
+      unpackedInt64_.WriteTo(ref output, _repeated_unpackedInt64_codec);
+      unpackedUint32_.WriteTo(ref output, _repeated_unpackedUint32_codec);
+      unpackedUint64_.WriteTo(ref output, _repeated_unpackedUint64_codec);
+      unpackedSint32_.WriteTo(ref output, _repeated_unpackedSint32_codec);
+      unpackedSint64_.WriteTo(ref output, _repeated_unpackedSint64_codec);
+      unpackedFixed32_.WriteTo(ref output, _repeated_unpackedFixed32_codec);
+      unpackedFixed64_.WriteTo(ref output, _repeated_unpackedFixed64_codec);
+      unpackedSfixed32_.WriteTo(ref output, _repeated_unpackedSfixed32_codec);
+      unpackedSfixed64_.WriteTo(ref output, _repeated_unpackedSfixed64_codec);
+      unpackedFloat_.WriteTo(ref output, _repeated_unpackedFloat_codec);
+      unpackedDouble_.WriteTo(ref output, _repeated_unpackedDouble_codec);
+      unpackedBool_.WriteTo(ref output, _repeated_unpackedBool_codec);
+      unpackedNestedEnum_.WriteTo(ref output, _repeated_unpackedNestedEnum_codec);
+      if (HasOneofUint32) {
+        output.WriteRawTag(248, 6);
+        output.WriteUInt32(OneofUint32);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) {
+        output.WriteRawTag(130, 7);
+        output.WriteMessage(OneofNestedMessage);
+      }
+      if (HasOneofString) {
+        output.WriteRawTag(138, 7);
+        output.WriteString(OneofString);
+      }
+      if (HasOneofBytes) {
+        output.WriteRawTag(146, 7);
+        output.WriteBytes(OneofBytes);
+      }
+      if (HasOneofBool) {
+        output.WriteRawTag(152, 7);
+        output.WriteBool(OneofBool);
+      }
+      if (HasOneofUint64) {
+        output.WriteRawTag(160, 7);
+        output.WriteUInt64(OneofUint64);
+      }
+      if (HasOneofFloat) {
+        output.WriteRawTag(173, 7);
+        output.WriteFloat(OneofFloat);
+      }
+      if (HasOneofDouble) {
+        output.WriteRawTag(177, 7);
+        output.WriteDouble(OneofDouble);
+      }
+      if (HasOneofEnum) {
+        output.WriteRawTag(184, 7);
+        output.WriteEnum((int) OneofEnum);
+      }
+      if (HasData) {
+        output.WriteRawTag(203, 12);
+        output.WriteGroup(Data);
+        output.WriteRawTag(204, 12);
+      }
+      if (HasFieldname1) {
+        output.WriteRawTag(136, 25);
+        output.WriteInt32(Fieldname1);
+      }
+      if (HasFieldName2) {
+        output.WriteRawTag(144, 25);
+        output.WriteInt32(FieldName2);
+      }
+      if (HasFieldName3) {
+        output.WriteRawTag(152, 25);
+        output.WriteInt32(FieldName3);
+      }
+      if (HasFieldName4) {
+        output.WriteRawTag(160, 25);
+        output.WriteInt32(FieldName4);
+      }
+      if (HasField0Name5) {
+        output.WriteRawTag(168, 25);
+        output.WriteInt32(Field0Name5);
+      }
+      if (HasField0Name6) {
+        output.WriteRawTag(176, 25);
+        output.WriteInt32(Field0Name6);
+      }
+      if (HasFieldName7) {
+        output.WriteRawTag(184, 25);
+        output.WriteInt32(FieldName7);
+      }
+      if (HasFieldName8) {
+        output.WriteRawTag(192, 25);
+        output.WriteInt32(FieldName8);
+      }
+      if (HasFieldName9) {
+        output.WriteRawTag(200, 25);
+        output.WriteInt32(FieldName9);
+      }
+      if (HasFieldName10) {
+        output.WriteRawTag(208, 25);
+        output.WriteInt32(FieldName10);
+      }
+      if (HasFIELDNAME11) {
+        output.WriteRawTag(216, 25);
+        output.WriteInt32(FIELDNAME11);
+      }
+      if (HasFIELDName12) {
+        output.WriteRawTag(224, 25);
+        output.WriteInt32(FIELDName12);
+      }
+      if (HasFieldName13) {
+        output.WriteRawTag(232, 25);
+        output.WriteInt32(FieldName13);
+      }
+      if (HasFieldName14) {
+        output.WriteRawTag(240, 25);
+        output.WriteInt32(FieldName14);
+      }
+      if (HasFieldName15) {
+        output.WriteRawTag(248, 25);
+        output.WriteInt32(FieldName15);
+      }
+      if (HasFieldName16) {
+        output.WriteRawTag(128, 26);
+        output.WriteInt32(FieldName16);
+      }
+      if (HasFieldName17) {
+        output.WriteRawTag(136, 26);
+        output.WriteInt32(FieldName17);
+      }
+      if (HasFieldName18) {
+        output.WriteRawTag(144, 26);
+        output.WriteInt32(FieldName18);
+      }
+      if (_extensions != null) {
+        _extensions.WriteTo(ref output);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -4561,6 +4846,9 @@ namespace ProtobufTestMessages.Proto2 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (HasA) {
             output.WriteRawTag(8);
             output.WriteInt32(A);
@@ -4572,8 +4860,26 @@ namespace ProtobufTestMessages.Proto2 {
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
         }
 
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (HasA) {
+            output.WriteRawTag(8);
+            output.WriteInt32(A);
+          }
+          if (corecursive_ != null) {
+            output.WriteRawTag(18);
+            output.WriteMessage(Corecursive);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {
           int size = 0;
@@ -4788,6 +5094,9 @@ namespace ProtobufTestMessages.Proto2 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (HasGroupInt32) {
             output.WriteRawTag(208, 12);
             output.WriteInt32(GroupInt32);
@@ -4799,8 +5108,26 @@ namespace ProtobufTestMessages.Proto2 {
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
         }
 
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (HasGroupInt32) {
+            output.WriteRawTag(208, 12);
+            output.WriteInt32(GroupInt32);
+          }
+          if (HasGroupUint32) {
+            output.WriteRawTag(216, 12);
+            output.WriteUInt32(GroupUint32);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {
           int size = 0;
@@ -4963,13 +5290,29 @@ namespace ProtobufTestMessages.Proto2 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (_extensions != null) {
             _extensions.WriteTo(output);
           }
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (_extensions != null) {
+            _extensions.WriteTo(ref output);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
         }
+        #endif
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {
@@ -5145,6 +5488,9 @@ namespace ProtobufTestMessages.Proto2 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (HasStr) {
             output.WriteRawTag(202, 1);
             output.WriteString(Str);
@@ -5152,7 +5498,21 @@ namespace ProtobufTestMessages.Proto2 {
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (HasStr) {
+            output.WriteRawTag(202, 1);
+            output.WriteString(Str);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
         }
+        #endif
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {
@@ -5324,6 +5684,9 @@ namespace ProtobufTestMessages.Proto2 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (HasI) {
             output.WriteRawTag(72);
             output.WriteInt32(I);
@@ -5331,7 +5694,21 @@ namespace ProtobufTestMessages.Proto2 {
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (HasI) {
+            output.WriteRawTag(72);
+            output.WriteInt32(I);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
         }
+        #endif
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {
@@ -5508,6 +5885,9 @@ namespace ProtobufTestMessages.Proto2 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (HasC) {
         output.WriteRawTag(8);
         output.WriteInt32(C);
@@ -5515,7 +5895,21 @@ namespace ProtobufTestMessages.Proto2 {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (HasC) {
+        output.WriteRawTag(8);
+        output.WriteInt32(C);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -5782,6 +6176,9 @@ namespace ProtobufTestMessages.Proto2 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (HasOptionalInt32) {
         output.WriteRawTag(200, 62);
         output.WriteInt32(OptionalInt32);
@@ -5807,8 +6204,40 @@ namespace ProtobufTestMessages.Proto2 {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (HasOptionalInt32) {
+        output.WriteRawTag(200, 62);
+        output.WriteInt32(OptionalInt32);
+      }
+      if (HasOptionalString) {
+        output.WriteRawTag(210, 62);
+        output.WriteString(OptionalString);
+      }
+      if (nestedMessage_ != null) {
+        output.WriteRawTag(218, 62);
+        output.WriteMessage(NestedMessage);
+      }
+      if (HasOptionalGroup) {
+        output.WriteRawTag(227, 62);
+        output.WriteGroup(OptionalGroup);
+        output.WriteRawTag(228, 62);
+      }
+      if (HasOptionalBool) {
+        output.WriteRawTag(240, 62);
+        output.WriteBool(OptionalBool);
+      }
+      repeatedInt32_.WriteTo(ref output, _repeated_repeatedInt32_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -6058,6 +6487,9 @@ namespace ProtobufTestMessages.Proto2 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (HasA) {
             output.WriteRawTag(8);
             output.WriteInt32(A);
@@ -6065,8 +6497,22 @@ namespace ProtobufTestMessages.Proto2 {
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
         }
 
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (HasA) {
+            output.WriteRawTag(8);
+            output.WriteInt32(A);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {
           int size = 0;

+ 386 - 0
csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs

@@ -2419,6 +2419,9 @@ namespace ProtobufTestMessages.Proto3 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (OptionalInt32 != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(OptionalInt32);
@@ -2757,8 +2760,353 @@ namespace ProtobufTestMessages.Proto3 {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (OptionalInt32 != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(OptionalInt32);
+      }
+      if (OptionalInt64 != 0L) {
+        output.WriteRawTag(16);
+        output.WriteInt64(OptionalInt64);
+      }
+      if (OptionalUint32 != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(OptionalUint32);
+      }
+      if (OptionalUint64 != 0UL) {
+        output.WriteRawTag(32);
+        output.WriteUInt64(OptionalUint64);
+      }
+      if (OptionalSint32 != 0) {
+        output.WriteRawTag(40);
+        output.WriteSInt32(OptionalSint32);
+      }
+      if (OptionalSint64 != 0L) {
+        output.WriteRawTag(48);
+        output.WriteSInt64(OptionalSint64);
+      }
+      if (OptionalFixed32 != 0) {
+        output.WriteRawTag(61);
+        output.WriteFixed32(OptionalFixed32);
+      }
+      if (OptionalFixed64 != 0UL) {
+        output.WriteRawTag(65);
+        output.WriteFixed64(OptionalFixed64);
+      }
+      if (OptionalSfixed32 != 0) {
+        output.WriteRawTag(77);
+        output.WriteSFixed32(OptionalSfixed32);
+      }
+      if (OptionalSfixed64 != 0L) {
+        output.WriteRawTag(81);
+        output.WriteSFixed64(OptionalSfixed64);
+      }
+      if (OptionalFloat != 0F) {
+        output.WriteRawTag(93);
+        output.WriteFloat(OptionalFloat);
+      }
+      if (OptionalDouble != 0D) {
+        output.WriteRawTag(97);
+        output.WriteDouble(OptionalDouble);
+      }
+      if (OptionalBool != false) {
+        output.WriteRawTag(104);
+        output.WriteBool(OptionalBool);
+      }
+      if (OptionalString.Length != 0) {
+        output.WriteRawTag(114);
+        output.WriteString(OptionalString);
+      }
+      if (OptionalBytes.Length != 0) {
+        output.WriteRawTag(122);
+        output.WriteBytes(OptionalBytes);
+      }
+      if (optionalNestedMessage_ != null) {
+        output.WriteRawTag(146, 1);
+        output.WriteMessage(OptionalNestedMessage);
+      }
+      if (optionalForeignMessage_ != null) {
+        output.WriteRawTag(154, 1);
+        output.WriteMessage(OptionalForeignMessage);
+      }
+      if (OptionalNestedEnum != global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum.Foo) {
+        output.WriteRawTag(168, 1);
+        output.WriteEnum((int) OptionalNestedEnum);
+      }
+      if (OptionalForeignEnum != global::ProtobufTestMessages.Proto3.ForeignEnum.ForeignFoo) {
+        output.WriteRawTag(176, 1);
+        output.WriteEnum((int) OptionalForeignEnum);
+      }
+      if (OptionalAliasedEnum != global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.AliasedEnum.AliasFoo) {
+        output.WriteRawTag(184, 1);
+        output.WriteEnum((int) OptionalAliasedEnum);
+      }
+      if (OptionalStringPiece.Length != 0) {
+        output.WriteRawTag(194, 1);
+        output.WriteString(OptionalStringPiece);
+      }
+      if (OptionalCord.Length != 0) {
+        output.WriteRawTag(202, 1);
+        output.WriteString(OptionalCord);
+      }
+      if (recursiveMessage_ != null) {
+        output.WriteRawTag(218, 1);
+        output.WriteMessage(RecursiveMessage);
+      }
+      repeatedInt32_.WriteTo(ref output, _repeated_repeatedInt32_codec);
+      repeatedInt64_.WriteTo(ref output, _repeated_repeatedInt64_codec);
+      repeatedUint32_.WriteTo(ref output, _repeated_repeatedUint32_codec);
+      repeatedUint64_.WriteTo(ref output, _repeated_repeatedUint64_codec);
+      repeatedSint32_.WriteTo(ref output, _repeated_repeatedSint32_codec);
+      repeatedSint64_.WriteTo(ref output, _repeated_repeatedSint64_codec);
+      repeatedFixed32_.WriteTo(ref output, _repeated_repeatedFixed32_codec);
+      repeatedFixed64_.WriteTo(ref output, _repeated_repeatedFixed64_codec);
+      repeatedSfixed32_.WriteTo(ref output, _repeated_repeatedSfixed32_codec);
+      repeatedSfixed64_.WriteTo(ref output, _repeated_repeatedSfixed64_codec);
+      repeatedFloat_.WriteTo(ref output, _repeated_repeatedFloat_codec);
+      repeatedDouble_.WriteTo(ref output, _repeated_repeatedDouble_codec);
+      repeatedBool_.WriteTo(ref output, _repeated_repeatedBool_codec);
+      repeatedString_.WriteTo(ref output, _repeated_repeatedString_codec);
+      repeatedBytes_.WriteTo(ref output, _repeated_repeatedBytes_codec);
+      repeatedNestedMessage_.WriteTo(ref output, _repeated_repeatedNestedMessage_codec);
+      repeatedForeignMessage_.WriteTo(ref output, _repeated_repeatedForeignMessage_codec);
+      repeatedNestedEnum_.WriteTo(ref output, _repeated_repeatedNestedEnum_codec);
+      repeatedForeignEnum_.WriteTo(ref output, _repeated_repeatedForeignEnum_codec);
+      repeatedStringPiece_.WriteTo(ref output, _repeated_repeatedStringPiece_codec);
+      repeatedCord_.WriteTo(ref output, _repeated_repeatedCord_codec);
+      mapInt32Int32_.WriteTo(ref output, _map_mapInt32Int32_codec);
+      mapInt64Int64_.WriteTo(ref output, _map_mapInt64Int64_codec);
+      mapUint32Uint32_.WriteTo(ref output, _map_mapUint32Uint32_codec);
+      mapUint64Uint64_.WriteTo(ref output, _map_mapUint64Uint64_codec);
+      mapSint32Sint32_.WriteTo(ref output, _map_mapSint32Sint32_codec);
+      mapSint64Sint64_.WriteTo(ref output, _map_mapSint64Sint64_codec);
+      mapFixed32Fixed32_.WriteTo(ref output, _map_mapFixed32Fixed32_codec);
+      mapFixed64Fixed64_.WriteTo(ref output, _map_mapFixed64Fixed64_codec);
+      mapSfixed32Sfixed32_.WriteTo(ref output, _map_mapSfixed32Sfixed32_codec);
+      mapSfixed64Sfixed64_.WriteTo(ref output, _map_mapSfixed64Sfixed64_codec);
+      mapInt32Float_.WriteTo(ref output, _map_mapInt32Float_codec);
+      mapInt32Double_.WriteTo(ref output, _map_mapInt32Double_codec);
+      mapBoolBool_.WriteTo(ref output, _map_mapBoolBool_codec);
+      mapStringString_.WriteTo(ref output, _map_mapStringString_codec);
+      mapStringBytes_.WriteTo(ref output, _map_mapStringBytes_codec);
+      mapStringNestedMessage_.WriteTo(ref output, _map_mapStringNestedMessage_codec);
+      mapStringForeignMessage_.WriteTo(ref output, _map_mapStringForeignMessage_codec);
+      mapStringNestedEnum_.WriteTo(ref output, _map_mapStringNestedEnum_codec);
+      mapStringForeignEnum_.WriteTo(ref output, _map_mapStringForeignEnum_codec);
+      packedInt32_.WriteTo(ref output, _repeated_packedInt32_codec);
+      packedInt64_.WriteTo(ref output, _repeated_packedInt64_codec);
+      packedUint32_.WriteTo(ref output, _repeated_packedUint32_codec);
+      packedUint64_.WriteTo(ref output, _repeated_packedUint64_codec);
+      packedSint32_.WriteTo(ref output, _repeated_packedSint32_codec);
+      packedSint64_.WriteTo(ref output, _repeated_packedSint64_codec);
+      packedFixed32_.WriteTo(ref output, _repeated_packedFixed32_codec);
+      packedFixed64_.WriteTo(ref output, _repeated_packedFixed64_codec);
+      packedSfixed32_.WriteTo(ref output, _repeated_packedSfixed32_codec);
+      packedSfixed64_.WriteTo(ref output, _repeated_packedSfixed64_codec);
+      packedFloat_.WriteTo(ref output, _repeated_packedFloat_codec);
+      packedDouble_.WriteTo(ref output, _repeated_packedDouble_codec);
+      packedBool_.WriteTo(ref output, _repeated_packedBool_codec);
+      packedNestedEnum_.WriteTo(ref output, _repeated_packedNestedEnum_codec);
+      unpackedInt32_.WriteTo(ref output, _repeated_unpackedInt32_codec);
+      unpackedInt64_.WriteTo(ref output, _repeated_unpackedInt64_codec);
+      unpackedUint32_.WriteTo(ref output, _repeated_unpackedUint32_codec);
+      unpackedUint64_.WriteTo(ref output, _repeated_unpackedUint64_codec);
+      unpackedSint32_.WriteTo(ref output, _repeated_unpackedSint32_codec);
+      unpackedSint64_.WriteTo(ref output, _repeated_unpackedSint64_codec);
+      unpackedFixed32_.WriteTo(ref output, _repeated_unpackedFixed32_codec);
+      unpackedFixed64_.WriteTo(ref output, _repeated_unpackedFixed64_codec);
+      unpackedSfixed32_.WriteTo(ref output, _repeated_unpackedSfixed32_codec);
+      unpackedSfixed64_.WriteTo(ref output, _repeated_unpackedSfixed64_codec);
+      unpackedFloat_.WriteTo(ref output, _repeated_unpackedFloat_codec);
+      unpackedDouble_.WriteTo(ref output, _repeated_unpackedDouble_codec);
+      unpackedBool_.WriteTo(ref output, _repeated_unpackedBool_codec);
+      unpackedNestedEnum_.WriteTo(ref output, _repeated_unpackedNestedEnum_codec);
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
+        output.WriteRawTag(248, 6);
+        output.WriteUInt32(OneofUint32);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) {
+        output.WriteRawTag(130, 7);
+        output.WriteMessage(OneofNestedMessage);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
+        output.WriteRawTag(138, 7);
+        output.WriteString(OneofString);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) {
+        output.WriteRawTag(146, 7);
+        output.WriteBytes(OneofBytes);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBool) {
+        output.WriteRawTag(152, 7);
+        output.WriteBool(OneofBool);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint64) {
+        output.WriteRawTag(160, 7);
+        output.WriteUInt64(OneofUint64);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofFloat) {
+        output.WriteRawTag(173, 7);
+        output.WriteFloat(OneofFloat);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofDouble) {
+        output.WriteRawTag(177, 7);
+        output.WriteDouble(OneofDouble);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofEnum) {
+        output.WriteRawTag(184, 7);
+        output.WriteEnum((int) OneofEnum);
+      }
+      if (optionalBoolWrapper_ != null) {
+        _single_optionalBoolWrapper_codec.WriteTagAndValue(ref output, OptionalBoolWrapper);
+      }
+      if (optionalInt32Wrapper_ != null) {
+        _single_optionalInt32Wrapper_codec.WriteTagAndValue(ref output, OptionalInt32Wrapper);
+      }
+      if (optionalInt64Wrapper_ != null) {
+        _single_optionalInt64Wrapper_codec.WriteTagAndValue(ref output, OptionalInt64Wrapper);
+      }
+      if (optionalUint32Wrapper_ != null) {
+        _single_optionalUint32Wrapper_codec.WriteTagAndValue(ref output, OptionalUint32Wrapper);
+      }
+      if (optionalUint64Wrapper_ != null) {
+        _single_optionalUint64Wrapper_codec.WriteTagAndValue(ref output, OptionalUint64Wrapper);
+      }
+      if (optionalFloatWrapper_ != null) {
+        _single_optionalFloatWrapper_codec.WriteTagAndValue(ref output, OptionalFloatWrapper);
+      }
+      if (optionalDoubleWrapper_ != null) {
+        _single_optionalDoubleWrapper_codec.WriteTagAndValue(ref output, OptionalDoubleWrapper);
+      }
+      if (optionalStringWrapper_ != null) {
+        _single_optionalStringWrapper_codec.WriteTagAndValue(ref output, OptionalStringWrapper);
+      }
+      if (optionalBytesWrapper_ != null) {
+        _single_optionalBytesWrapper_codec.WriteTagAndValue(ref output, OptionalBytesWrapper);
+      }
+      repeatedBoolWrapper_.WriteTo(ref output, _repeated_repeatedBoolWrapper_codec);
+      repeatedInt32Wrapper_.WriteTo(ref output, _repeated_repeatedInt32Wrapper_codec);
+      repeatedInt64Wrapper_.WriteTo(ref output, _repeated_repeatedInt64Wrapper_codec);
+      repeatedUint32Wrapper_.WriteTo(ref output, _repeated_repeatedUint32Wrapper_codec);
+      repeatedUint64Wrapper_.WriteTo(ref output, _repeated_repeatedUint64Wrapper_codec);
+      repeatedFloatWrapper_.WriteTo(ref output, _repeated_repeatedFloatWrapper_codec);
+      repeatedDoubleWrapper_.WriteTo(ref output, _repeated_repeatedDoubleWrapper_codec);
+      repeatedStringWrapper_.WriteTo(ref output, _repeated_repeatedStringWrapper_codec);
+      repeatedBytesWrapper_.WriteTo(ref output, _repeated_repeatedBytesWrapper_codec);
+      if (optionalDuration_ != null) {
+        output.WriteRawTag(234, 18);
+        output.WriteMessage(OptionalDuration);
+      }
+      if (optionalTimestamp_ != null) {
+        output.WriteRawTag(242, 18);
+        output.WriteMessage(OptionalTimestamp);
+      }
+      if (optionalFieldMask_ != null) {
+        output.WriteRawTag(250, 18);
+        output.WriteMessage(OptionalFieldMask);
+      }
+      if (optionalStruct_ != null) {
+        output.WriteRawTag(130, 19);
+        output.WriteMessage(OptionalStruct);
+      }
+      if (optionalAny_ != null) {
+        output.WriteRawTag(138, 19);
+        output.WriteMessage(OptionalAny);
+      }
+      if (optionalValue_ != null) {
+        output.WriteRawTag(146, 19);
+        output.WriteMessage(OptionalValue);
+      }
+      repeatedDuration_.WriteTo(ref output, _repeated_repeatedDuration_codec);
+      repeatedTimestamp_.WriteTo(ref output, _repeated_repeatedTimestamp_codec);
+      repeatedFieldmask_.WriteTo(ref output, _repeated_repeatedFieldmask_codec);
+      repeatedAny_.WriteTo(ref output, _repeated_repeatedAny_codec);
+      repeatedValue_.WriteTo(ref output, _repeated_repeatedValue_codec);
+      repeatedListValue_.WriteTo(ref output, _repeated_repeatedListValue_codec);
+      repeatedStruct_.WriteTo(ref output, _repeated_repeatedStruct_codec);
+      if (Fieldname1 != 0) {
+        output.WriteRawTag(136, 25);
+        output.WriteInt32(Fieldname1);
+      }
+      if (FieldName2 != 0) {
+        output.WriteRawTag(144, 25);
+        output.WriteInt32(FieldName2);
+      }
+      if (FieldName3 != 0) {
+        output.WriteRawTag(152, 25);
+        output.WriteInt32(FieldName3);
+      }
+      if (FieldName4 != 0) {
+        output.WriteRawTag(160, 25);
+        output.WriteInt32(FieldName4);
+      }
+      if (Field0Name5 != 0) {
+        output.WriteRawTag(168, 25);
+        output.WriteInt32(Field0Name5);
+      }
+      if (Field0Name6 != 0) {
+        output.WriteRawTag(176, 25);
+        output.WriteInt32(Field0Name6);
+      }
+      if (FieldName7 != 0) {
+        output.WriteRawTag(184, 25);
+        output.WriteInt32(FieldName7);
+      }
+      if (FieldName8 != 0) {
+        output.WriteRawTag(192, 25);
+        output.WriteInt32(FieldName8);
+      }
+      if (FieldName9 != 0) {
+        output.WriteRawTag(200, 25);
+        output.WriteInt32(FieldName9);
+      }
+      if (FieldName10 != 0) {
+        output.WriteRawTag(208, 25);
+        output.WriteInt32(FieldName10);
+      }
+      if (FIELDNAME11 != 0) {
+        output.WriteRawTag(216, 25);
+        output.WriteInt32(FIELDNAME11);
+      }
+      if (FIELDName12 != 0) {
+        output.WriteRawTag(224, 25);
+        output.WriteInt32(FIELDName12);
+      }
+      if (FieldName13 != 0) {
+        output.WriteRawTag(232, 25);
+        output.WriteInt32(FieldName13);
+      }
+      if (FieldName14 != 0) {
+        output.WriteRawTag(240, 25);
+        output.WriteInt32(FieldName14);
+      }
+      if (FieldName15 != 0) {
+        output.WriteRawTag(248, 25);
+        output.WriteInt32(FieldName15);
+      }
+      if (FieldName16 != 0) {
+        output.WriteRawTag(128, 26);
+        output.WriteInt32(FieldName16);
+      }
+      if (FieldName17 != 0) {
+        output.WriteRawTag(136, 26);
+        output.WriteInt32(FieldName17);
+      }
+      if (FieldName18 != 0) {
+        output.WriteRawTag(144, 26);
+        output.WriteInt32(FieldName18);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -4933,6 +5281,9 @@ namespace ProtobufTestMessages.Proto3 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (A != 0) {
             output.WriteRawTag(8);
             output.WriteInt32(A);
@@ -4944,8 +5295,26 @@ namespace ProtobufTestMessages.Proto3 {
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
         }
 
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (A != 0) {
+            output.WriteRawTag(8);
+            output.WriteInt32(A);
+          }
+          if (corecursive_ != null) {
+            output.WriteRawTag(18);
+            output.WriteMessage(Corecursive);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {
           int size = 0;
@@ -5120,6 +5489,9 @@ namespace ProtobufTestMessages.Proto3 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (C != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(C);
@@ -5127,8 +5499,22 @@ namespace ProtobufTestMessages.Proto3 {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (C != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(C);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 762 - 103
csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs


+ 335 - 0
csharp/src/Google.Protobuf.Test.TestProtos/UnittestCustomOptionsProto3.cs

@@ -379,6 +379,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Field1.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Field1);
@@ -390,7 +393,25 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Field1.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Field1);
+      }
+      if (anOneofCase_ == AnOneofOneofCase.OneofField) {
+        output.WriteRawTag(16);
+        output.WriteInt32(OneofField);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -558,11 +579,24 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -681,10 +715,23 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -804,11 +851,24 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -927,10 +987,23 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1050,10 +1123,23 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1186,11 +1272,24 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -1309,10 +1408,23 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1432,11 +1544,24 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -1555,11 +1680,24 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -1678,11 +1816,24 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -1801,10 +1952,23 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1979,6 +2143,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Foo != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(Foo);
@@ -1995,7 +2162,30 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Foo != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(Foo);
+      }
+      if (Foo2 != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Foo2);
+      }
+      if (Foo3 != 0) {
+        output.WriteRawTag(24);
+        output.WriteInt32(Foo3);
+      }
+      foo4_.WriteTo(ref output, _repeated_foo4_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -2224,6 +2414,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (bar_ != null) {
         output.WriteRawTag(10);
         output.WriteMessage(Bar);
@@ -2240,7 +2433,30 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (bar_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Bar);
+      }
+      if (Baz != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Baz);
+      }
+      if (fred_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(Fred);
+      }
+      barney_.WriteTo(ref output, _repeated_barney_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -2446,6 +2662,9 @@ namespace UnitTest.Issues.TestProtos {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (Waldo != 0) {
             output.WriteRawTag(8);
             output.WriteInt32(Waldo);
@@ -2453,8 +2672,22 @@ namespace UnitTest.Issues.TestProtos {
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
         }
 
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (Waldo != 0) {
+            output.WriteRawTag(8);
+            output.WriteInt32(Waldo);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {
           int size = 0;
@@ -2615,6 +2848,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Qux != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(Qux);
@@ -2622,8 +2858,22 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Qux != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(Qux);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -2759,11 +3009,24 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -2930,6 +3193,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (I != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(I);
@@ -2945,7 +3211,29 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (I != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(I);
+      }
+      if (S.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(S);
+      }
+      if (sub_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(Sub);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -3130,6 +3418,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Fieldname != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(Fieldname);
@@ -3137,8 +3428,22 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Fieldname != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(Fieldname);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -3274,11 +3579,24 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -3418,6 +3736,9 @@ namespace UnitTest.Issues.TestProtos {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (NestedField != 0) {
             output.WriteRawTag(8);
             output.WriteInt32(NestedField);
@@ -3425,7 +3746,21 @@ namespace UnitTest.Issues.TestProtos {
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (NestedField != 0) {
+            output.WriteRawTag(8);
+            output.WriteInt32(NestedField);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
         }
+        #endif
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {

+ 17 - 0
csharp/src/Google.Protobuf.Test.TestProtos/UnittestImport.cs

@@ -157,6 +157,9 @@ namespace Google.Protobuf.TestProtos.Proto2 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (HasD) {
         output.WriteRawTag(8);
         output.WriteInt32(D);
@@ -164,7 +167,21 @@ namespace Google.Protobuf.TestProtos.Proto2 {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (HasD) {
+        output.WriteRawTag(8);
+        output.WriteInt32(D);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 17 - 0
csharp/src/Google.Protobuf.Test.TestProtos/UnittestImportProto3.cs

@@ -133,6 +133,9 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (D != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(D);
@@ -140,7 +143,21 @@ namespace Google.Protobuf.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (D != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(D);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 17 - 0
csharp/src/Google.Protobuf.Test.TestProtos/UnittestImportPublic.cs

@@ -135,6 +135,9 @@ namespace Google.Protobuf.TestProtos.Proto2 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (HasE) {
         output.WriteRawTag(8);
         output.WriteInt32(E);
@@ -142,7 +145,21 @@ namespace Google.Protobuf.TestProtos.Proto2 {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (HasE) {
+        output.WriteRawTag(8);
+        output.WriteInt32(E);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 17 - 0
csharp/src/Google.Protobuf.Test.TestProtos/UnittestImportPublicProto3.cs

@@ -121,6 +121,9 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (E != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(E);
@@ -128,7 +131,21 @@ namespace Google.Protobuf.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (E != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(E);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 13 - 0
csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssue6936B.cs

@@ -106,10 +106,23 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 17 - 0
csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssue6936C.cs

@@ -122,6 +122,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (foo_ != null) {
         output.WriteRawTag(10);
         output.WriteMessage(Foo);
@@ -129,7 +132,21 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (foo_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Foo);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 254 - 0
csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs

@@ -157,10 +157,23 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -282,11 +295,24 @@ namespace UnitTest.Issues.TestProtos {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
         }
 
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {
           int size = 0;
@@ -407,11 +433,24 @@ namespace UnitTest.Issues.TestProtos {
 
             [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
             public void WriteTo(pb::CodedOutputStream output) {
+            #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+              output.WriteRawMessage(this);
+            #else
               if (_unknownFields != null) {
                 _unknownFields.WriteTo(output);
               }
+            #endif
             }
 
+            #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+            [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+            void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+              if (_unknownFields != null) {
+                _unknownFields.WriteTo(ref output);
+              }
+            }
+            #endif
+
             [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
             public int CalculateSize() {
               int size = 0;
@@ -580,6 +619,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.Zero) {
         output.WriteRawTag(8);
         output.WriteEnum((int) Value);
@@ -589,7 +631,23 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.Zero) {
+        output.WriteRawTag(8);
+        output.WriteEnum((int) Value);
+      }
+      values_.WriteTo(ref output, _repeated_values_codec);
+      packedValues_.WriteTo(ref output, _repeated_packedValues_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -747,11 +805,24 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -957,6 +1028,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (PrimitiveValue != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(PrimitiveValue);
@@ -975,7 +1049,32 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (PrimitiveValue != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(PrimitiveValue);
+      }
+      primitiveArray_.WriteTo(ref output, _repeated_primitiveArray_codec);
+      if (messageValue_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(MessageValue);
+      }
+      messageArray_.WriteTo(ref output, _repeated_messageArray_codec);
+      if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DeprecatedZero) {
+        output.WriteRawTag(40);
+        output.WriteEnum((int) EnumValue);
+      }
+      enumArray_.WriteTo(ref output, _repeated_enumArray_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1197,6 +1296,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Item != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(Item);
@@ -1204,7 +1306,21 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Item != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(Item);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1366,6 +1482,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Types_ != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(Types_);
@@ -1377,7 +1496,25 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Types_ != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(Types_);
+      }
+      if (Descriptor_ != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Descriptor_);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1530,10 +1667,23 @@ namespace UnitTest.Issues.TestProtos {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
         }
+        #endif
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {
@@ -1810,6 +1960,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (PlainString.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(PlainString);
@@ -1837,8 +1990,42 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (PlainString.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(PlainString);
+      }
+      if (o1Case_ == O1OneofCase.O1String) {
+        output.WriteRawTag(18);
+        output.WriteString(O1String);
+      }
+      if (o2Case_ == O2OneofCase.O2String) {
+        output.WriteRawTag(26);
+        output.WriteString(O2String);
+      }
+      if (PlainInt32 != 0) {
+        output.WriteRawTag(32);
+        output.WriteInt32(PlainInt32);
+      }
+      if (o1Case_ == O1OneofCase.O1Int32) {
+        output.WriteRawTag(40);
+        output.WriteInt32(O1Int32);
+      }
+      if (o2Case_ == O2OneofCase.O2Int32) {
+        output.WriteRawTag(48);
+        output.WriteInt32(O2Int32);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -2092,6 +2279,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Name.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Name);
@@ -2107,8 +2297,30 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Description);
+      }
+      if (Guid.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Guid);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -2330,6 +2542,9 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (valueCase_ == ValueOneofCase.Text) {
         output.WriteRawTag(10);
         output.WriteString(Text);
@@ -2341,7 +2556,25 @@ namespace UnitTest.Issues.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (valueCase_ == ValueOneofCase.Text) {
+        output.WriteRawTag(10);
+        output.WriteString(Text);
+      }
+      if (valueCase_ == ValueOneofCase.Nested) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Nested);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -2535,6 +2768,9 @@ namespace UnitTest.Issues.TestProtos {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (X != 0) {
             output.WriteRawTag(8);
             output.WriteInt32(X);
@@ -2546,7 +2782,25 @@ namespace UnitTest.Issues.TestProtos {
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (X != 0) {
+            output.WriteRawTag(8);
+            output.WriteInt32(X);
+          }
+          if (Y != 0) {
+            output.WriteRawTag(16);
+            output.WriteInt32(Y);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
         }
+        #endif
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 616 - 2
csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3.cs


+ 114 - 0
csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.cs

@@ -622,6 +622,9 @@ namespace ProtobufUnittest {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (HasOptionalInt32) {
         output.WriteRawTag(8);
         output.WriteInt32(OptionalInt32);
@@ -709,7 +712,101 @@ namespace ProtobufUnittest {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (HasOptionalInt32) {
+        output.WriteRawTag(8);
+        output.WriteInt32(OptionalInt32);
+      }
+      if (HasOptionalInt64) {
+        output.WriteRawTag(16);
+        output.WriteInt64(OptionalInt64);
+      }
+      if (HasOptionalUint32) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(OptionalUint32);
+      }
+      if (HasOptionalUint64) {
+        output.WriteRawTag(32);
+        output.WriteUInt64(OptionalUint64);
+      }
+      if (HasOptionalSint32) {
+        output.WriteRawTag(40);
+        output.WriteSInt32(OptionalSint32);
+      }
+      if (HasOptionalSint64) {
+        output.WriteRawTag(48);
+        output.WriteSInt64(OptionalSint64);
+      }
+      if (HasOptionalFixed32) {
+        output.WriteRawTag(61);
+        output.WriteFixed32(OptionalFixed32);
+      }
+      if (HasOptionalFixed64) {
+        output.WriteRawTag(65);
+        output.WriteFixed64(OptionalFixed64);
+      }
+      if (HasOptionalSfixed32) {
+        output.WriteRawTag(77);
+        output.WriteSFixed32(OptionalSfixed32);
+      }
+      if (HasOptionalSfixed64) {
+        output.WriteRawTag(81);
+        output.WriteSFixed64(OptionalSfixed64);
+      }
+      if (HasOptionalFloat) {
+        output.WriteRawTag(93);
+        output.WriteFloat(OptionalFloat);
+      }
+      if (HasOptionalDouble) {
+        output.WriteRawTag(97);
+        output.WriteDouble(OptionalDouble);
+      }
+      if (HasOptionalBool) {
+        output.WriteRawTag(104);
+        output.WriteBool(OptionalBool);
+      }
+      if (HasOptionalString) {
+        output.WriteRawTag(114);
+        output.WriteString(OptionalString);
+      }
+      if (HasOptionalBytes) {
+        output.WriteRawTag(122);
+        output.WriteBytes(OptionalBytes);
+      }
+      if (HasOptionalCord) {
+        output.WriteRawTag(130, 1);
+        output.WriteString(OptionalCord);
+      }
+      if (optionalNestedMessage_ != null) {
+        output.WriteRawTag(146, 1);
+        output.WriteMessage(OptionalNestedMessage);
+      }
+      if (lazyNestedMessage_ != null) {
+        output.WriteRawTag(154, 1);
+        output.WriteMessage(LazyNestedMessage);
+      }
+      if (HasOptionalNestedEnum) {
+        output.WriteRawTag(168, 1);
+        output.WriteEnum((int) OptionalNestedEnum);
+      }
+      if (SingularInt32 != 0) {
+        output.WriteRawTag(176, 1);
+        output.WriteInt32(SingularInt32);
+      }
+      if (SingularInt64 != 0L) {
+        output.WriteRawTag(184, 1);
+        output.WriteInt64(SingularInt64);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1186,6 +1283,9 @@ namespace ProtobufUnittest {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
           if (HasBb) {
             output.WriteRawTag(8);
             output.WriteInt32(Bb);
@@ -1193,8 +1293,22 @@ namespace ProtobufUnittest {
           if (_unknownFields != null) {
             _unknownFields.WriteTo(output);
           }
+        #endif
         }
 
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (HasBb) {
+            output.WriteRawTag(8);
+            output.WriteInt32(Bb);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public int CalculateSize() {
           int size = 0;

+ 24 - 0
csharp/src/Google.Protobuf.Test.TestProtos/UnittestSelfreferentialOptions.cs

@@ -203,6 +203,9 @@ namespace UnitTest.Issues.TestProtos.SelfreferentialOptions {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (HasIntOpt) {
         output.WriteRawTag(8);
         output.WriteInt32(IntOpt);
@@ -217,7 +220,28 @@ namespace UnitTest.Issues.TestProtos.SelfreferentialOptions {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (HasIntOpt) {
+        output.WriteRawTag(8);
+        output.WriteInt32(IntOpt);
+      }
+      if (HasFoo) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Foo);
+      }
+      if (_extensions != null) {
+        _extensions.WriteTo(ref output);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 218 - 0
csharp/src/Google.Protobuf.Test.TestProtos/UnittestWellKnownTypes.cs

@@ -535,6 +535,9 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (anyField_ != null) {
         output.WriteRawTag(10);
         output.WriteMessage(AnyField);
@@ -605,7 +608,84 @@ namespace Google.Protobuf.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (anyField_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(AnyField);
+      }
+      if (apiField_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(ApiField);
+      }
+      if (durationField_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DurationField);
+      }
+      if (emptyField_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(EmptyField);
+      }
+      if (fieldMaskField_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(FieldMaskField);
+      }
+      if (sourceContextField_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(SourceContextField);
+      }
+      if (structField_ != null) {
+        output.WriteRawTag(58);
+        output.WriteMessage(StructField);
+      }
+      if (timestampField_ != null) {
+        output.WriteRawTag(66);
+        output.WriteMessage(TimestampField);
+      }
+      if (typeField_ != null) {
+        output.WriteRawTag(74);
+        output.WriteMessage(TypeField);
+      }
+      if (doubleField_ != null) {
+        _single_doubleField_codec.WriteTagAndValue(ref output, DoubleField);
+      }
+      if (floatField_ != null) {
+        _single_floatField_codec.WriteTagAndValue(ref output, FloatField);
+      }
+      if (int64Field_ != null) {
+        _single_int64Field_codec.WriteTagAndValue(ref output, Int64Field);
+      }
+      if (uint64Field_ != null) {
+        _single_uint64Field_codec.WriteTagAndValue(ref output, Uint64Field);
+      }
+      if (int32Field_ != null) {
+        _single_int32Field_codec.WriteTagAndValue(ref output, Int32Field);
+      }
+      if (uint32Field_ != null) {
+        _single_uint32Field_codec.WriteTagAndValue(ref output, Uint32Field);
+      }
+      if (boolField_ != null) {
+        _single_boolField_codec.WriteTagAndValue(ref output, BoolField);
+      }
+      if (stringField_ != null) {
+        _single_stringField_codec.WriteTagAndValue(ref output, StringField);
+      }
+      if (bytesField_ != null) {
+        _single_bytesField_codec.WriteTagAndValue(ref output, BytesField);
+      }
+      if (valueField_ != null) {
+        output.WriteRawTag(154, 1);
+        output.WriteMessage(ValueField);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1393,6 +1473,9 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       anyField_.WriteTo(output, _repeated_anyField_codec);
       apiField_.WriteTo(output, _repeated_apiField_codec);
       durationField_.WriteTo(output, _repeated_durationField_codec);
@@ -1414,7 +1497,35 @@ namespace Google.Protobuf.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      anyField_.WriteTo(ref output, _repeated_anyField_codec);
+      apiField_.WriteTo(ref output, _repeated_apiField_codec);
+      durationField_.WriteTo(ref output, _repeated_durationField_codec);
+      emptyField_.WriteTo(ref output, _repeated_emptyField_codec);
+      fieldMaskField_.WriteTo(ref output, _repeated_fieldMaskField_codec);
+      sourceContextField_.WriteTo(ref output, _repeated_sourceContextField_codec);
+      structField_.WriteTo(ref output, _repeated_structField_codec);
+      timestampField_.WriteTo(ref output, _repeated_timestampField_codec);
+      typeField_.WriteTo(ref output, _repeated_typeField_codec);
+      doubleField_.WriteTo(ref output, _repeated_doubleField_codec);
+      floatField_.WriteTo(ref output, _repeated_floatField_codec);
+      int64Field_.WriteTo(ref output, _repeated_int64Field_codec);
+      uint64Field_.WriteTo(ref output, _repeated_uint64Field_codec);
+      int32Field_.WriteTo(ref output, _repeated_int32Field_codec);
+      uint32Field_.WriteTo(ref output, _repeated_uint32Field_codec);
+      boolField_.WriteTo(ref output, _repeated_boolField_codec);
+      stringField_.WriteTo(ref output, _repeated_stringField_codec);
+      bytesField_.WriteTo(ref output, _repeated_bytesField_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -2051,6 +2162,9 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (oneofFieldCase_ == OneofFieldOneofCase.AnyField) {
         output.WriteRawTag(10);
         output.WriteMessage(AnyField);
@@ -2117,7 +2231,80 @@ namespace Google.Protobuf.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.AnyField) {
+        output.WriteRawTag(10);
+        output.WriteMessage(AnyField);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.ApiField) {
+        output.WriteRawTag(18);
+        output.WriteMessage(ApiField);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.DurationField) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DurationField);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.EmptyField) {
+        output.WriteRawTag(34);
+        output.WriteMessage(EmptyField);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.FieldMaskField) {
+        output.WriteRawTag(42);
+        output.WriteMessage(FieldMaskField);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.SourceContextField) {
+        output.WriteRawTag(50);
+        output.WriteMessage(SourceContextField);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.StructField) {
+        output.WriteRawTag(58);
+        output.WriteMessage(StructField);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.TimestampField) {
+        output.WriteRawTag(66);
+        output.WriteMessage(TimestampField);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.TypeField) {
+        output.WriteRawTag(74);
+        output.WriteMessage(TypeField);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.DoubleField) {
+        _oneof_doubleField_codec.WriteTagAndValue(ref output, (double?) oneofField_);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.FloatField) {
+        _oneof_floatField_codec.WriteTagAndValue(ref output, (float?) oneofField_);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.Int64Field) {
+        _oneof_int64Field_codec.WriteTagAndValue(ref output, (long?) oneofField_);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.Uint64Field) {
+        _oneof_uint64Field_codec.WriteTagAndValue(ref output, (ulong?) oneofField_);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.Int32Field) {
+        _oneof_int32Field_codec.WriteTagAndValue(ref output, (int?) oneofField_);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.Uint32Field) {
+        _oneof_uint32Field_codec.WriteTagAndValue(ref output, (uint?) oneofField_);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.BoolField) {
+        _oneof_boolField_codec.WriteTagAndValue(ref output, (bool?) oneofField_);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.StringField) {
+        _oneof_stringField_codec.WriteTagAndValue(ref output, (string) oneofField_);
+      }
+      if (oneofFieldCase_ == OneofFieldOneofCase.BytesField) {
+        _oneof_bytesField_codec.WriteTagAndValue(ref output, (pb::ByteString) oneofField_);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -2848,6 +3035,9 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       anyField_.WriteTo(output, _map_anyField_codec);
       apiField_.WriteTo(output, _map_apiField_codec);
       durationField_.WriteTo(output, _map_durationField_codec);
@@ -2869,8 +3059,36 @@ namespace Google.Protobuf.TestProtos {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      anyField_.WriteTo(ref output, _map_anyField_codec);
+      apiField_.WriteTo(ref output, _map_apiField_codec);
+      durationField_.WriteTo(ref output, _map_durationField_codec);
+      emptyField_.WriteTo(ref output, _map_emptyField_codec);
+      fieldMaskField_.WriteTo(ref output, _map_fieldMaskField_codec);
+      sourceContextField_.WriteTo(ref output, _map_sourceContextField_codec);
+      structField_.WriteTo(ref output, _map_structField_codec);
+      timestampField_.WriteTo(ref output, _map_timestampField_codec);
+      typeField_.WriteTo(ref output, _map_typeField_codec);
+      doubleField_.WriteTo(ref output, _map_doubleField_codec);
+      floatField_.WriteTo(ref output, _map_floatField_codec);
+      int64Field_.WriteTo(ref output, _map_int64Field_codec);
+      uint64Field_.WriteTo(ref output, _map_uint64Field_codec);
+      int32Field_.WriteTo(ref output, _map_int32Field_codec);
+      uint32Field_.WriteTo(ref output, _map_uint32Field_codec);
+      boolField_.WriteTo(ref output, _map_boolField_codec);
+      stringField_.WriteTo(ref output, _map_stringField_codec);
+      bytesField_.WriteTo(ref output, _map_bytesField_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;

+ 225 - 0
csharp/src/Google.Protobuf.Test/Buffers/ArrayBufferWriter.cs

@@ -0,0 +1,225 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Buffers;
+using System.Diagnostics;
+
+namespace Google.Protobuf.Buffers
+{
+    /// <summary>
+    /// Represents a heap-based, array-backed output sink into which <typeparam name="T"/> data can be written.
+    /// 
+    /// ArrayBufferWriter is originally from corefx, and has been contributed to Protobuf
+    /// https://github.com/dotnet/runtime/blob/071da4c41aa808c949a773b92dca6f88de9d11f3/src/libraries/Common/src/System/Buffers/ArrayBufferWriter.cs
+    /// </summary>
+    internal sealed class ArrayBufferWriter<T> : IBufferWriter<T>
+    {
+        private T[] _buffer;
+        private int _index;
+
+        private const int DefaultInitialBufferSize = 256;
+
+        /// <summary>
+        /// Creates an instance of an <see cref="ArrayBufferWriter{T}"/>, in which data can be written to,
+        /// with the default initial capacity.
+        /// </summary>
+        public ArrayBufferWriter()
+        {
+            _buffer = new T[0];
+            _index = 0;
+        }
+
+        /// <summary>
+        /// Userful for testing writing to buffer writer with a lot of small segments.
+        /// If set, it limits the max number of bytes by which the buffer grows by at once.
+        /// </summary>
+        public int? MaxGrowBy { get; set; }
+
+        /// <summary>
+        /// Creates an instance of an <see cref="ArrayBufferWriter{T}"/>, in which data can be written to,
+        /// with an initial capacity specified.
+        /// </summary>
+        /// <param name="initialCapacity">The minimum capacity with which to initialize the underlying buffer.</param>
+        /// <exception cref="ArgumentException">
+        /// Thrown when <paramref name="initialCapacity"/> is not positive (i.e. less than or equal to 0).
+        /// </exception>
+        public ArrayBufferWriter(int initialCapacity)
+        {
+            if (initialCapacity <= 0)
+                throw new ArgumentException(nameof(initialCapacity));
+
+            _buffer = new T[initialCapacity];
+            _index = 0;
+        }
+
+        /// <summary>
+        /// Returns the data written to the underlying buffer so far, as a <see cref="ReadOnlyMemory{T}"/>.
+        /// </summary>
+        public ReadOnlyMemory<T> WrittenMemory => _buffer.AsMemory(0, _index);
+
+        /// <summary>
+        /// Returns the data written to the underlying buffer so far, as a <see cref="ReadOnlySpan{T}"/>.
+        /// </summary>
+        public ReadOnlySpan<T> WrittenSpan => _buffer.AsSpan(0, _index);
+
+        /// <summary>
+        /// Returns the amount of data written to the underlying buffer so far.
+        /// </summary>
+        public int WrittenCount => _index;
+
+        /// <summary>
+        /// Returns the total amount of space within the underlying buffer.
+        /// </summary>
+        public int Capacity => _buffer.Length;
+
+        /// <summary>
+        /// Returns the amount of space available that can still be written into without forcing the underlying buffer to grow.
+        /// </summary>
+        public int FreeCapacity => _buffer.Length - _index;
+
+        /// <summary>
+        /// Clears the data written to the underlying buffer.
+        /// </summary>
+        /// <remarks>
+        /// You must clear the <see cref="ArrayBufferWriter{T}"/> before trying to re-use it.
+        /// </remarks>
+        public void Clear()
+        {
+            Debug.Assert(_buffer.Length >= _index);
+            _buffer.AsSpan(0, _index).Clear();
+            _index = 0;
+        }
+
+        /// <summary>
+        /// Notifies <see cref="IBufferWriter{T}"/> that <paramref name="count"/> amount of data was written to the output <see cref="Span{T}"/>/<see cref="Memory{T}"/>
+        /// </summary>
+        /// <exception cref="ArgumentException">
+        /// Thrown when <paramref name="count"/> is negative.
+        /// </exception>
+        /// <exception cref="InvalidOperationException">
+        /// Thrown when attempting to advance past the end of the underlying buffer.
+        /// </exception>
+        /// <remarks>
+        /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer.
+        /// </remarks>
+        public void Advance(int count)
+        {
+            if (count < 0)
+                throw new ArgumentException(nameof(count));
+
+            if (_index > _buffer.Length - count)
+                throw new InvalidOperationException("Advanced past capacity.");
+
+            _index += count;
+        }
+
+        /// <summary>
+        /// Returns a <see cref="Memory{T}"/> to write to that is at least the requested length (specified by <paramref name="sizeHint"/>).
+        /// If no <paramref name="sizeHint"/> is provided (or it's equal to <code>0</code>), some non-empty buffer is returned.
+        /// </summary>
+        /// <exception cref="ArgumentException">
+        /// Thrown when <paramref name="sizeHint"/> is negative.
+        /// </exception>
+        /// <remarks>
+        /// This will never return an empty <see cref="Memory{T}"/>.
+        /// </remarks>
+        /// <remarks>
+        /// There is no guarantee that successive calls will return the same buffer or the same-sized buffer.
+        /// </remarks>
+        /// <remarks>
+        /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer.
+        /// </remarks>
+        public Memory<T> GetMemory(int sizeHint = 0)
+        {
+            CheckAndResizeBuffer(sizeHint);
+            Debug.Assert(_buffer.Length > _index);
+            return _buffer.AsMemory(_index);
+        }
+
+        /// <summary>
+        /// Returns a <see cref="Span{T}"/> to write to that is at least the requested length (specified by <paramref name="sizeHint"/>).
+        /// If no <paramref name="sizeHint"/> is provided (or it's equal to <code>0</code>), some non-empty buffer is returned.
+        /// </summary>
+        /// <exception cref="ArgumentException">
+        /// Thrown when <paramref name="sizeHint"/> is negative.
+        /// </exception>
+        /// <remarks>
+        /// This will never return an empty <see cref="Span{T}"/>.
+        /// </remarks>
+        /// <remarks>
+        /// There is no guarantee that successive calls will return the same buffer or the same-sized buffer.
+        /// </remarks>
+        /// <remarks>
+        /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer.
+        /// </remarks>
+        public Span<T> GetSpan(int sizeHint = 0)
+        {
+            CheckAndResizeBuffer(sizeHint);
+            Debug.Assert(_buffer.Length > _index);
+            return _buffer.AsSpan(_index);
+        }
+
+        private void CheckAndResizeBuffer(int sizeHint)
+        {
+            if (sizeHint < 0)
+                throw new ArgumentException(nameof(sizeHint));
+
+            if (sizeHint == 0)
+            {
+                sizeHint = 1;
+            }
+
+            if (sizeHint > FreeCapacity)
+            {
+                int growBy = Math.Max(sizeHint, _buffer.Length);
+
+                if (_buffer.Length == 0)
+                {
+                    growBy = Math.Max(growBy, DefaultInitialBufferSize);
+                }
+
+                // enable tests that write to small buffer segments
+                if (MaxGrowBy.HasValue && growBy > MaxGrowBy.Value)
+                {
+                    growBy = MaxGrowBy.Value;
+                }
+
+                int newSize = checked(_buffer.Length + growBy);
+
+                Array.Resize(ref _buffer, newSize);
+            }
+
+            Debug.Assert(FreeCapacity > 0 && FreeCapacity >= sizeHint);
+        }
+    }
+}

+ 140 - 46
csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs

@@ -33,6 +33,7 @@
 using System;
 using System.IO;
 using Google.Protobuf.TestProtos;
+using Google.Protobuf.Buffers;
 using NUnit.Framework;
 
 namespace Google.Protobuf
@@ -48,22 +49,39 @@ namespace Google.Protobuf
             // Only do 32-bit write if the value fits in 32 bits.
             if ((value >> 32) == 0)
             {
+                // CodedOutputStream
                 MemoryStream rawOutput = new MemoryStream();
                 CodedOutputStream output = new CodedOutputStream(rawOutput);
                 output.WriteRawVarint32((uint) value);
                 output.Flush();
                 Assert.AreEqual(data, rawOutput.ToArray());
+
+                // IBufferWriter
+                var bufferWriter = new ArrayBufferWriter<byte>();
+                WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                ctx.WriteUInt32((uint) value);
+                ctx.Flush();
+                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+
                 // Also try computing size.
                 Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint32Size((uint) value));
             }
 
             {
+                // CodedOutputStream
                 MemoryStream rawOutput = new MemoryStream();
                 CodedOutputStream output = new CodedOutputStream(rawOutput);
                 output.WriteRawVarint64(value);
                 output.Flush();
                 Assert.AreEqual(data, rawOutput.ToArray());
 
+                // IBufferWriter
+                var bufferWriter = new ArrayBufferWriter<byte>();
+                WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                ctx.WriteUInt64(value);
+                ctx.Flush();
+                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+
                 // Also try computing size.
                 Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint64Size(value));
             }
@@ -80,6 +98,13 @@ namespace Google.Protobuf
                     output.WriteRawVarint32((uint) value);
                     output.Flush();
                     Assert.AreEqual(data, rawOutput.ToArray());
+
+                    var bufferWriter = new ArrayBufferWriter<byte>();
+                    bufferWriter.MaxGrowBy = bufferSize;
+                    WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                    ctx.WriteUInt32((uint) value);
+                    ctx.Flush();
+                    Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
                 }
 
                 {
@@ -88,7 +113,15 @@ namespace Google.Protobuf
                     output.WriteRawVarint64(value);
                     output.Flush();
                     Assert.AreEqual(data, rawOutput.ToArray());
+
+                    var bufferWriter = new ArrayBufferWriter<byte>();
+                    bufferWriter.MaxGrowBy = bufferSize;
+                    WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                    ctx.WriteUInt64(value);
+                    ctx.Flush();
+                    Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
                 }
+
             }
         }
 
@@ -133,20 +166,35 @@ namespace Google.Protobuf
         /// </summary>
         private static void AssertWriteLittleEndian32(byte[] data, uint value)
         {
-            MemoryStream rawOutput = new MemoryStream();
-            CodedOutputStream output = new CodedOutputStream(rawOutput);
-            output.WriteRawLittleEndian32(value);
-            output.Flush();
-            Assert.AreEqual(data, rawOutput.ToArray());
+            {
+                var rawOutput = new MemoryStream();
+                var output = new CodedOutputStream(rawOutput);
+                output.WriteRawLittleEndian32(value);
+                output.Flush();
+                Assert.AreEqual(data, rawOutput.ToArray());
+
+                var bufferWriter = new ArrayBufferWriter<byte>();
+                WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                ctx.WriteFixed32(value);
+                ctx.Flush();
+                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+            }
 
             // Try different buffer sizes.
             for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
             {
-                rawOutput = new MemoryStream();
-                output = new CodedOutputStream(rawOutput, bufferSize);
+                var rawOutput = new MemoryStream();
+                var output = new CodedOutputStream(rawOutput, bufferSize);
                 output.WriteRawLittleEndian32(value);
                 output.Flush();
                 Assert.AreEqual(data, rawOutput.ToArray());
+
+                var bufferWriter = new ArrayBufferWriter<byte>();
+                bufferWriter.MaxGrowBy = bufferSize;
+                WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                ctx.WriteFixed32(value);
+                ctx.Flush();
+                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
             }
         }
 
@@ -156,20 +204,35 @@ namespace Google.Protobuf
         /// </summary>
         private static void AssertWriteLittleEndian64(byte[] data, ulong value)
         {
-            MemoryStream rawOutput = new MemoryStream();
-            CodedOutputStream output = new CodedOutputStream(rawOutput);
-            output.WriteRawLittleEndian64(value);
-            output.Flush();
-            Assert.AreEqual(data, rawOutput.ToArray());
+            {
+                var rawOutput = new MemoryStream();
+                var output = new CodedOutputStream(rawOutput);
+                output.WriteRawLittleEndian64(value);
+                output.Flush();
+                Assert.AreEqual(data, rawOutput.ToArray());
+
+                var bufferWriter = new ArrayBufferWriter<byte>();
+                WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                ctx.WriteFixed64(value);
+                ctx.Flush();
+                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+            }
 
             // Try different block sizes.
             for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
             {
-                rawOutput = new MemoryStream();
-                output = new CodedOutputStream(rawOutput, blockSize);
+                var rawOutput = new MemoryStream();
+                var output = new CodedOutputStream(rawOutput, blockSize);
                 output.WriteRawLittleEndian64(value);
                 output.Flush();
                 Assert.AreEqual(data, rawOutput.ToArray());
+
+                var bufferWriter = new ArrayBufferWriter<byte>();
+                bufferWriter.MaxGrowBy = blockSize;
+                WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                ctx.WriteFixed64(value);
+                ctx.Flush();
+                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
             }
         }
 
@@ -205,41 +268,72 @@ namespace Google.Protobuf
                 message.WriteTo(output);
                 output.Flush();
                 Assert.AreEqual(rawBytes, rawOutput.ToArray());
+
+                var bufferWriter = new ArrayBufferWriter<byte>();
+                bufferWriter.MaxGrowBy = blockSize;
+                message.WriteTo(bufferWriter);
+                Assert.AreEqual(rawBytes, bufferWriter.WrittenSpan.ToArray()); 
             }
         }
+
+        [Test]
+        public void WriteContext_WritesWithFlushes()
+        {
+            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+            MemoryStream expectedOutput = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(expectedOutput);
+            output.WriteMessage(message);
+            output.Flush();
+            byte[] expectedBytes1 = expectedOutput.ToArray();
+
+            output.WriteMessage(message);
+            output.Flush();
+            byte[] expectedBytes2 = expectedOutput.ToArray();
+
+            var bufferWriter = new ArrayBufferWriter<byte>();
+            WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+            ctx.WriteMessage(message);
+            ctx.Flush();
+            Assert.AreEqual(expectedBytes1, bufferWriter.WrittenSpan.ToArray());
+
+            ctx.WriteMessage(message);
+            ctx.Flush();
+            Assert.AreEqual(expectedBytes2, bufferWriter.WrittenSpan.ToArray());
+        }
         
         [Test]
         public void EncodeZigZag32()
         {
-            Assert.AreEqual(0u, CodedOutputStream.EncodeZigZag32(0));
-            Assert.AreEqual(1u, CodedOutputStream.EncodeZigZag32(-1));
-            Assert.AreEqual(2u, CodedOutputStream.EncodeZigZag32(1));
-            Assert.AreEqual(3u, CodedOutputStream.EncodeZigZag32(-2));
-            Assert.AreEqual(0x7FFFFFFEu, CodedOutputStream.EncodeZigZag32(0x3FFFFFFF));
-            Assert.AreEqual(0x7FFFFFFFu, CodedOutputStream.EncodeZigZag32(unchecked((int) 0xC0000000)));
-            Assert.AreEqual(0xFFFFFFFEu, CodedOutputStream.EncodeZigZag32(0x7FFFFFFF));
-            Assert.AreEqual(0xFFFFFFFFu, CodedOutputStream.EncodeZigZag32(unchecked((int) 0x80000000)));
+            Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag32(0));
+            Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag32(-1));
+            Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag32(1));
+            Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag32(-2));
+            Assert.AreEqual(0x7FFFFFFEu, WritingPrimitives.EncodeZigZag32(0x3FFFFFFF));
+            Assert.AreEqual(0x7FFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0xC0000000)));
+            Assert.AreEqual(0xFFFFFFFEu, WritingPrimitives.EncodeZigZag32(0x7FFFFFFF));
+            Assert.AreEqual(0xFFFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0x80000000)));
         }
 
         [Test]
         public void EncodeZigZag64()
         {
-            Assert.AreEqual(0u, CodedOutputStream.EncodeZigZag64(0));
-            Assert.AreEqual(1u, CodedOutputStream.EncodeZigZag64(-1));
-            Assert.AreEqual(2u, CodedOutputStream.EncodeZigZag64(1));
-            Assert.AreEqual(3u, CodedOutputStream.EncodeZigZag64(-2));
+            Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag64(0));
+            Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag64(-1));
+            Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag64(1));
+            Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag64(-2));
             Assert.AreEqual(0x000000007FFFFFFEuL,
-                            CodedOutputStream.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL)));
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL)));
             Assert.AreEqual(0x000000007FFFFFFFuL,
-                            CodedOutputStream.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL)));
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL)));
             Assert.AreEqual(0x00000000FFFFFFFEuL,
-                            CodedOutputStream.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL)));
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL)));
             Assert.AreEqual(0x00000000FFFFFFFFuL,
-                            CodedOutputStream.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL)));
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL)));
             Assert.AreEqual(0xFFFFFFFFFFFFFFFEL,
-                            CodedOutputStream.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL)));
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL)));
             Assert.AreEqual(0xFFFFFFFFFFFFFFFFL,
-                            CodedOutputStream.EncodeZigZag64(unchecked((long) 0x8000000000000000UL)));
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x8000000000000000UL)));
         }
 
         [Test]
@@ -247,26 +341,26 @@ namespace Google.Protobuf
         {
             // Some easier-to-verify round-trip tests.  The inputs (other than 0, 1, -1)
             // were chosen semi-randomly via keyboard bashing.
-            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(0)));
-            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(1)));
-            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(-1)));
-            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(14927)));
-            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(-3612)));
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(0)));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(1)));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-1)));
+            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(14927)));
+            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-3612)));
         }
 
         [Test]
         public void RoundTripZigZag64()
         {
-            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(0)));
-            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(1)));
-            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-1)));
-            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(14927)));
-            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-3612)));
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(0)));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(1)));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-1)));
+            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(14927)));
+            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-3612)));
 
             Assert.AreEqual(856912304801416L,
-                            ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(856912304801416L)));
+                            ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(856912304801416L)));
             Assert.AreEqual(-75123905439571256L,
-                            ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-75123905439571256L)));
+                            ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-75123905439571256L)));
         }
 
         [Test]
@@ -395,7 +489,7 @@ namespace Google.Protobuf
             Assert.IsTrue(memoryStream.CanWrite);
             using (var cos = new CodedOutputStream(memoryStream))
             {
-                cos.WriteRawByte(0);
+                cos.WriteRawBytes(new byte[] {0});
                 Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
             }
             Assert.AreEqual(1, memoryStream.ToArray().Length); // Flushed data from CodedOutputStream to MemoryStream
@@ -409,7 +503,7 @@ namespace Google.Protobuf
             Assert.IsTrue(memoryStream.CanWrite);
             using (var cos = new CodedOutputStream(memoryStream, true))
             {
-                cos.WriteRawByte(0);
+                cos.WriteRawBytes(new byte[] {0});
                 Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
             }
             Assert.AreEqual(1, memoryStream.Position); // Flushed data from CodedOutputStream to MemoryStream

+ 2 - 0
csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs

@@ -34,6 +34,8 @@ namespace Google.Protobuf
             message.SetExtension(OptionalBoolExtension, true);
             var serialized = message.ToByteArray();
 
+            MessageParsingHelpers.AssertWritingMessage(message);
+
             MessageParsingHelpers.AssertReadingMessage(
                 TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { OptionalBoolExtension }),
                 serialized,

+ 22 - 2
csharp/src/Google.Protobuf.Test/FieldCodecTest.cs

@@ -124,7 +124,17 @@ namespace Google.Protobuf
             {
                 var stream = new MemoryStream();
                 var codedOutput = new CodedOutputStream(stream);
-                codec.ValueWriter(codedOutput, sampleValue);
+                WriteContext.Initialize(codedOutput, out WriteContext ctx);
+                try
+                {
+                    // only write the value using the codec
+                    codec.ValueWriter(ref ctx, sampleValue);
+                }
+                finally
+                {
+                    ctx.CopyStateTo(codedOutput);
+                }
+                
                 codedOutput.Flush();
                 stream.Position = 0;
                 var codedInput = new CodedInputStream(stream);
@@ -175,7 +185,17 @@ namespace Google.Protobuf
                 if (codec.DefaultValue != null) // This part isn't appropriate for message types.
                 {
                     codedOutput = new CodedOutputStream(stream);
-                    codec.ValueWriter(codedOutput, codec.DefaultValue);
+                    WriteContext.Initialize(codedOutput, out WriteContext ctx);
+                    try
+                    {
+                        // only write the value using the codec
+                        codec.ValueWriter(ref ctx, codec.DefaultValue);
+                    }
+                    finally
+                    {
+                        ctx.CopyStateTo(codedOutput);
+                    }
+                    
                     codedOutput.Flush();
                     Assert.AreNotEqual(0, stream.Position);
                     Assert.AreEqual(stream.Position, codec.ValueSizeCalculator(codec.DefaultValue));

+ 6 - 0
csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs

@@ -344,6 +344,8 @@ namespace Google.Protobuf
                 }
             };
 
+            MessageParsingHelpers.AssertWritingMessage(message);
+
             MessageParsingHelpers.AssertRoundtrip(Proto2.TestAllTypes.Parser, message);
         }
 
@@ -359,6 +361,8 @@ namespace Google.Protobuf
                 new RepeatedGroup_extension { A = 30 }
             });
 
+            MessageParsingHelpers.AssertWritingMessage(message);
+
             MessageParsingHelpers.AssertRoundtrip(
                 TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { UnittestExtensions.OptionalGroupExtension, UnittestExtensions.RepeatedGroupExtension }),
                 message);
@@ -370,6 +374,8 @@ namespace Google.Protobuf
             var message = new TestGroupExtension();
             message.SetExtension(TestNestedExtension.Extensions.OptionalGroupExtension, new TestNestedExtension.Types.OptionalGroup_extension { A = 10 });
 
+            MessageParsingHelpers.AssertWritingMessage(message);
+            
             MessageParsingHelpers.AssertRoundtrip(
                 TestGroupExtension.Parser.WithExtensionRegistry(new ExtensionRegistry() { TestNestedExtension.Extensions.OptionalGroupExtension }),
                 message);

+ 11 - 3
csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs

@@ -132,6 +132,8 @@ namespace Google.Protobuf
             byte[] bytes = message.ToByteArray();
             Assert.AreEqual(0, bytes.Length);
 
+            MessageParsingHelpers.AssertWritingMessage(message);
+
             MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
         }
 
@@ -164,7 +166,7 @@ namespace Google.Protobuf
                 SingleUint64 = ulong.MaxValue
             };
 
-            byte[] bytes = message.ToByteArray();
+            MessageParsingHelpers.AssertWritingMessage(message);
 
             MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
         }
@@ -198,7 +200,7 @@ namespace Google.Protobuf
                 RepeatedUint64 = { ulong.MaxValue, uint.MinValue }
             };
 
-            byte[] bytes = message.ToByteArray();
+            MessageParsingHelpers.AssertWritingMessage(message);
 
             MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
         }
@@ -230,7 +232,7 @@ namespace Google.Protobuf
                 }
             };
 
-            byte[] bytes = message.ToByteArray();
+            MessageParsingHelpers.AssertWritingMessage(message);
 
             MessageParsingHelpers.AssertRoundtrip(TestMap.Parser, message);
         }
@@ -246,6 +248,8 @@ namespace Google.Protobuf
             byte[] bytes = message.ToByteArray();
             Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)
 
+            MessageParsingHelpers.AssertWritingMessage(message);
+
             MessageParsingHelpers.AssertReadingMessage(
                 TestMap.Parser,
                 bytes,
@@ -660,6 +664,8 @@ namespace Google.Protobuf
             var bytes = message.ToByteArray();
             Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - no string!
 
+            MessageParsingHelpers.AssertWritingMessage(message);
+
             MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
             {
                 Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);
@@ -675,6 +681,8 @@ namespace Google.Protobuf
             var bytes = message.ToByteArray();
             Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - it's still serialized
 
+            MessageParsingHelpers.AssertWritingMessage(message);
+
             MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
             {
                 Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);

+ 82 - 19
csharp/src/Google.Protobuf.Test/LegacyGeneratedCodeTest.cs

@@ -35,7 +35,9 @@ using System.Buffers;
 using pb = global::Google.Protobuf;
 using pbr = global::Google.Protobuf.Reflection;
 using NUnit.Framework;
-
+using System.IO;
+using System;
+using Google.Protobuf.Buffers;
 
 namespace Google.Protobuf
 {
@@ -46,14 +48,14 @@ namespace Google.Protobuf
         {
             var message = new ParseContextEnabledMessageB
             {
-              A = new LegacyGeneratedCodeMessageA
-              {
-                Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
-              },
-              OptionalInt32 = 6789
+                A = new LegacyGeneratedCodeMessageA
+                {
+                    Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
+                },
+                OptionalInt32 = 6789
             };
             var data = message.ToByteArray();
-            
+
             // when parsing started using CodedInputStream and a message with legacy generated code
             // is encountered somewhere in the parse tree, we still need to be able to use its
             // MergeFrom(CodedInputStream) method to parse correctly.
@@ -71,11 +73,11 @@ namespace Google.Protobuf
         {
             var message = new ParseContextEnabledMessageB
             {
-              A = new LegacyGeneratedCodeMessageA
-              {
-                Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
-              },
-              OptionalInt32 = 6789
+                A = new LegacyGeneratedCodeMessageA
+                {
+                    Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
+                },
+                OptionalInt32 = 6789
             };
             var data = message.ToByteArray();
 
@@ -86,13 +88,65 @@ namespace Google.Protobuf
             // code up to date.
             var exception = Assert.Throws<InvalidProtocolBufferException>(() =>
             {
-              ParseContext.Initialize(new ReadOnlySequence<byte>(data), out ParseContext parseCtx);
-              var parsed = new ParseContextEnabledMessageB();
-              ParsingPrimitivesMessages.ReadRawMessage(ref parseCtx, parsed);
+                ParseContext.Initialize(new ReadOnlySequence<byte>(data), out ParseContext parseCtx);
+                var parsed = new ParseContextEnabledMessageB();
+                ParsingPrimitivesMessages.ReadRawMessage(ref parseCtx, parsed);
             });
             Assert.AreEqual($"Message {typeof(LegacyGeneratedCodeMessageA).Name} doesn't provide the generated method that enables ParseContext-based parsing. You might need to regenerate the generated protobuf code.", exception.Message);
         }
 
+        [Test]
+        public void IntermixingOfNewAndLegacyGeneratedCodeWorksWithCodedOutputStream()
+        {
+            // when serialization started using CodedOutputStream and a message with legacy generated code
+            // is encountered somewhere in the parse tree, we still need to be able to use its
+            // WriteTo(CodedOutputStream) method to serialize correctly.
+            var ms = new MemoryStream();
+            var codedOutput = new CodedOutputStream(ms);
+            var message = new ParseContextEnabledMessageB
+            {
+                A = new LegacyGeneratedCodeMessageA
+                {
+                    Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
+                },
+                OptionalInt32 = 6789
+            };
+            message.WriteTo(codedOutput);
+            codedOutput.Flush();
+
+            var codedInput = new CodedInputStream(ms.ToArray());
+            var parsed = new ParseContextEnabledMessageB();
+            codedInput.ReadRawMessage(parsed);
+            Assert.IsTrue(codedInput.IsAtEnd);
+
+            Assert.AreEqual(12345, parsed.A.Bb.OptionalInt32);
+            Assert.AreEqual(6789, parsed.OptionalInt32);
+        }
+
+        [Test]
+        public void LegacyGeneratedCodeThrowsWithIBufferWriter()
+        {
+            // if serialization started using IBufferWriter and we don't have a CodedOutputStream
+            // instance at hand, we cannot fall back to the legacy WriteTo(CodedOutputStream)
+            // method and serializatin will fail. As a consequence, one can only use serialization
+            // to IBufferWriter if all the messages in the parsing tree have their generated
+            // code up to date.
+            var message = new ParseContextEnabledMessageB
+            {
+                A = new LegacyGeneratedCodeMessageA
+                {
+                    Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
+                },
+                OptionalInt32 = 6789
+            };
+            var exception = Assert.Throws<InvalidProtocolBufferException>(() =>
+            {
+                WriteContext.Initialize(new ArrayBufferWriter<byte>(), out WriteContext writeCtx);
+                ((IBufferMessage)message).InternalWriteTo(ref writeCtx);
+            });
+            Assert.AreEqual($"Message {typeof(LegacyGeneratedCodeMessageA).Name} doesn't provide the generated method that enables WriteContext-based serialization. You might need to regenerate the generated protobuf code.", exception.Message);
+        }
+
         // hand-modified version of a generated message that only provides the legacy
         // MergeFrom(CodedInputStream) method and doesn't implement IBufferMessage.
         private sealed partial class LegacyGeneratedCodeMessageA : pb::IMessage {
@@ -178,18 +232,27 @@ namespace Google.Protobuf
           }
 
           public void WriteTo(pb::CodedOutputStream output) {
-            if (a_ != null) {
+            output.WriteRawMessage(this);
+          }
+
+          void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output)
+          {
+            if (a_ != null)
+            {
               output.WriteRawTag(10);
               output.WriteMessage(A);
             }
-            if (OptionalInt32 != 0) {
+            if (OptionalInt32 != 0)
+            {
               output.WriteRawTag(16);
               output.WriteInt32(OptionalInt32);
             }
-            if (_unknownFields != null) {
-              _unknownFields.WriteTo(output);
+            if (_unknownFields != null)
+            {
+              _unknownFields.WriteTo(ref output);
             }
           }
+
           public int CalculateSize() {
             int size = 0;
             if (a_ != null) {

+ 42 - 0
csharp/src/Google.Protobuf.Test/MessageParsingHelpers.cs

@@ -33,6 +33,7 @@
 using NUnit.Framework;
 using System;
 using System.Buffers;
+using Google.Protobuf.Buffers;
 
 namespace Google.Protobuf
 {
@@ -81,6 +82,11 @@ namespace Google.Protobuf
         {
             var bytes = message.ToByteArray();
 
+            // also serialize using IBufferWriter and check it leads to the same data
+            var bufferWriter = new ArrayBufferWriter<byte>();
+            message.WriteTo(bufferWriter);
+            Assert.AreEqual(bytes, bufferWriter.WrittenSpan.ToArray(), "Both serialization approaches need to result in the same data.");
+
             // Load content as single segment
             var parsedBuffer = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
             Assert.AreEqual(message, parsedBuffer);
@@ -96,5 +102,41 @@ namespace Google.Protobuf
             Assert.AreEqual(message, parsedStream);
             additionalAssert?.Invoke(parsedStream);
         }
+
+        public static void AssertWritingMessage(IMessage message)
+        {
+            // serialize using CodedOutputStream
+            var bytes = message.ToByteArray();
+
+            int messageSize = message.CalculateSize(); 
+            Assert.AreEqual(message.CalculateSize(), bytes.Length);
+
+            // serialize using IBufferWriter and check it leads to the same output
+            var bufferWriter = new ArrayBufferWriter<byte>();
+            message.WriteTo(bufferWriter);
+            Assert.AreEqual(bytes, bufferWriter.WrittenSpan.ToArray());
+
+            // serialize into a single span and check it leads to the same output
+            var singleSpan = new Span<byte>(new byte[messageSize]);
+            message.WriteTo(singleSpan);
+            Assert.AreEqual(bytes, singleSpan.ToArray());
+
+            // test for different IBufferWriter.GetSpan() segment sizes
+            for (int blockSize = 1; blockSize < 256; blockSize *= 2)
+            {
+                var segmentedBufferWriter = new ArrayBufferWriter<byte>();
+                segmentedBufferWriter.MaxGrowBy = blockSize;
+                message.WriteTo(segmentedBufferWriter);
+                Assert.AreEqual(bytes, segmentedBufferWriter.WrittenSpan.ToArray());
+            }
+
+            // if the full message is small enough, try serializing directly into stack-allocated buffer
+            if (bytes.Length <= 256)
+            {
+                Span<byte> stackAllocBuffer = stackalloc byte[bytes.Length];
+                message.WriteTo(stackAllocBuffer);
+                Assert.AreEqual(bytes, stackAllocBuffer.ToArray());
+            }
+        }
     }
 }

+ 10 - 0
csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs

@@ -71,6 +71,8 @@ namespace Google.Protobuf.WellKnownTypes
                 Uint64Field = 4
             };
 
+            MessageParsingHelpers.AssertWritingMessage(message);
+
             MessageParsingHelpers.AssertRoundtrip(TestWellKnownTypes.Parser, message, parsed =>
             {
                 Assert.AreEqual("x", parsed.StringField);
@@ -101,6 +103,8 @@ namespace Google.Protobuf.WellKnownTypes
                 Uint64Field = 0
             };
 
+            MessageParsingHelpers.AssertWritingMessage(message);
+
             MessageParsingHelpers.AssertRoundtrip(TestWellKnownTypes.Parser, message, parsed =>
             {
                 Assert.AreEqual("", parsed.StringField);
@@ -144,6 +148,8 @@ namespace Google.Protobuf.WellKnownTypes
             // Just to test a single value for sanity...
             Assert.AreEqual("Second", message.StringField[1]);
 
+            MessageParsingHelpers.AssertWritingMessage(message);
+
             MessageParsingHelpers.AssertRoundtrip(RepeatedWellKnownTypes.Parser, message);
         }
 
@@ -169,6 +175,8 @@ namespace Google.Protobuf.WellKnownTypes
             var message = new RepeatedWellKnownTypes { Int32Field = { 5, 0 } };
             var actualBytes = message.ToByteArray();
             Assert.AreEqual(expectedBytes, actualBytes);
+
+            MessageParsingHelpers.AssertWritingMessage(message);
         }
 
         [Test]
@@ -196,6 +204,8 @@ namespace Google.Protobuf.WellKnownTypes
             // Just to test a single value for sanity...
             Assert.AreEqual("Second", message.StringField[12]);
 
+            MessageParsingHelpers.AssertWritingMessage(message);
+
             MessageParsingHelpers.AssertRoundtrip(MapWellKnownTypes.Parser, message);
         }
 

+ 3 - 3
csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs

@@ -132,7 +132,7 @@ namespace Google.Protobuf
         /// </summary>
         public static int ComputeStringSize(String value)
         {
-            int byteArraySize = Utf8Encoding.GetByteCount(value);
+            int byteArraySize = WritingPrimitives.Utf8Encoding.GetByteCount(value);
             return ComputeLengthSize(byteArraySize) + byteArraySize;
         }
 
@@ -208,7 +208,7 @@ namespace Google.Protobuf
         /// </summary>
         public static int ComputeSInt32Size(int value)
         {
-            return ComputeRawVarint32Size(EncodeZigZag32(value));
+            return ComputeRawVarint32Size(WritingPrimitives.EncodeZigZag32(value));
         }
 
         /// <summary>
@@ -217,7 +217,7 @@ namespace Google.Protobuf
         /// </summary>
         public static int ComputeSInt64Size(long value)
         {
-            return ComputeRawVarint64Size(EncodeZigZag64(value));
+            return ComputeRawVarint64Size(WritingPrimitives.EncodeZigZag64(value));
         }
 
         /// <summary>

+ 126 - 289
csharp/src/Google.Protobuf/CodedOutputStream.cs

@@ -33,6 +33,7 @@
 using Google.Protobuf.Collections;
 using System;
 using System.IO;
+using System.Security;
 using System.Text;
 
 namespace Google.Protobuf
@@ -55,11 +56,9 @@ namespace Google.Protobuf
     /// and <c>MapField&lt;TKey, TValue&gt;</c> to serialize such fields.
     /// </para>
     /// </remarks>
+    [SecuritySafeCritical]
     public sealed partial class CodedOutputStream : IDisposable
     {
-        // "Local" copy of Encoding.UTF8, for efficiency. (Yes, it makes a difference.)
-        internal static readonly Encoding Utf8Encoding = Encoding.UTF8;
-
         /// <summary>
         /// The buffer size used by CreateInstance(Stream).
         /// </summary>
@@ -67,8 +66,8 @@ namespace Google.Protobuf
 
         private readonly bool leaveOpen;
         private readonly byte[] buffer;
-        private readonly int limit;
-        private int position;
+        private WriterInternalState state;
+
         private readonly Stream output;
 
         #region Construction
@@ -90,8 +89,9 @@ namespace Google.Protobuf
         {
             this.output = null;
             this.buffer = ProtoPreconditions.CheckNotNull(buffer, nameof(buffer));
-            this.position = offset;
-            this.limit = offset + length;
+            this.state.position = offset;
+            this.state.limit = offset + length;
+            WriteBufferHelper.Initialize(this, out this.state.writeBufferHelper);
             leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference
         }
 
@@ -99,8 +99,9 @@ namespace Google.Protobuf
         {
             this.output = ProtoPreconditions.CheckNotNull(output, nameof(output));
             this.buffer = buffer;
-            this.position = 0;
-            this.limit = buffer.Length;
+            this.state.position = 0;
+            this.state.limit = buffer.Length;
+            WriteBufferHelper.Initialize(this, out this.state.writeBufferHelper);
             this.leaveOpen = leaveOpen;
         }
 
@@ -155,9 +156,9 @@ namespace Google.Protobuf
             {
                 if (output != null)
                 {
-                    return output.Position + position;
+                    return output.Position + state.position;
                 }
-                return position;
+                return state.position;
             }
         }
 
@@ -169,7 +170,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteDouble(double value)
         {
-            WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteDouble(ref span, ref state, value);
         }
 
         /// <summary>
@@ -178,23 +180,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteFloat(float value)
         {
-            byte[] rawBytes = BitConverter.GetBytes(value);
-            if (!BitConverter.IsLittleEndian)
-            {
-                ByteArray.Reverse(rawBytes);
-            }
-
-            if (limit - position >= 4)
-            {
-                buffer[position++] = rawBytes[0];
-                buffer[position++] = rawBytes[1];
-                buffer[position++] = rawBytes[2];
-                buffer[position++] = rawBytes[3];
-            }
-            else
-            {
-                WriteRawBytes(rawBytes, 0, 4);
-            }
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteFloat(ref span, ref state, value);
         }
 
         /// <summary>
@@ -203,7 +190,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteUInt64(ulong value)
         {
-            WriteRawVarint64(value);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteUInt64(ref span, ref state, value);
         }
 
         /// <summary>
@@ -212,7 +200,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteInt64(long value)
         {
-            WriteRawVarint64((ulong) value);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteInt64(ref span, ref state, value);
         }
 
         /// <summary>
@@ -221,15 +210,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteInt32(int value)
         {
-            if (value >= 0)
-            {
-                WriteRawVarint32((uint) value);
-            }
-            else
-            {
-                // Must sign-extend.
-                WriteRawVarint64((ulong) value);
-            }
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteInt32(ref span, ref state, value);
         }
 
         /// <summary>
@@ -238,7 +220,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteFixed64(ulong value)
         {
-            WriteRawLittleEndian64(value);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteFixed64(ref span, ref state, value);
         }
 
         /// <summary>
@@ -247,7 +230,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteFixed32(uint value)
         {
-            WriteRawLittleEndian32(value);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteFixed32(ref span, ref state, value);
         }
 
         /// <summary>
@@ -256,7 +240,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteBool(bool value)
         {
-            WriteRawByte(value ? (byte) 1 : (byte) 0);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteBool(ref span, ref state, value);
         }
 
         /// <summary>
@@ -266,41 +251,52 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteString(string value)
         {
-            // Optimise the case where we have enough space to write
-            // the string directly to the buffer, which should be common.
-            int length = Utf8Encoding.GetByteCount(value);
-            WriteLength(length);
-            if (limit - position >= length)
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteString(ref span, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes a message, without a tag, to the stream.
+        /// The data is length-prefixed.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteMessage(IMessage value)
+        {
+            // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalWriteTo method),
+            // what we're doing here works fine, but could be more efficient.
+            // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it).
+            var span = new Span<byte>(buffer);
+            WriteContext.Initialize(ref span, ref state, out WriteContext ctx);
+            try
             {
-                if (length == value.Length) // Must be all ASCII...
-                {
-                    for (int i = 0; i < length; i++)
-                    {
-                        buffer[position + i] = (byte)value[i];
-                    }
-                }
-                else
-                {
-                    Utf8Encoding.GetBytes(value, 0, value.Length, buffer, position);
-                }
-                position += length;
+                WritingPrimitivesMessages.WriteMessage(ref ctx, value);
             }
-            else
+            finally
             {
-                byte[] bytes = Utf8Encoding.GetBytes(value);
-                WriteRawBytes(bytes);
+                ctx.CopyStateTo(this);
             }
         }
 
         /// <summary>
         /// Writes a message, without a tag, to the stream.
-        /// The data is length-prefixed.
+        /// Only the message data is written, without a length-delimiter.
         /// </summary>
         /// <param name="value">The value to write</param>
-        public void WriteMessage(IMessage value)
-        {
-            WriteLength(value.CalculateSize());
-            value.WriteTo(this);
+        public void WriteRawMessage(IMessage value)
+        {
+            // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalWriteTo method),
+            // what we're doing here works fine, but could be more efficient.
+            // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it).
+            var span = new Span<byte>(buffer);
+            WriteContext.Initialize(ref span, ref state, out WriteContext ctx);
+            try
+            {
+                WritingPrimitivesMessages.WriteRawMessage(ref ctx, value);
+            }
+            finally
+            {
+                ctx.CopyStateTo(this);
+            }
         }
 
         /// <summary>
@@ -309,7 +305,16 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteGroup(IMessage value)
         {
-            value.WriteTo(this);
+            var span = new Span<byte>(buffer);
+            WriteContext.Initialize(ref span, ref state, out WriteContext ctx);
+            try
+            {
+                WritingPrimitivesMessages.WriteGroup(ref ctx, value);
+            }
+            finally
+            {
+                ctx.CopyStateTo(this);
+            }
         }
 
         /// <summary>
@@ -319,8 +324,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteBytes(ByteString value)
         {
-            WriteLength(value.Length);
-            value.WriteRawBytesTo(this);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteBytes(ref span, ref state, value);
         }
 
         /// <summary>
@@ -329,7 +334,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteUInt32(uint value)
         {
-            WriteRawVarint32(value);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteUInt32(ref span, ref state, value);
         }
 
         /// <summary>
@@ -338,7 +344,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteEnum(int value)
         {
-            WriteInt32(value);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteEnum(ref span, ref state, value);
         }
 
         /// <summary>
@@ -347,7 +354,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write.</param>
         public void WriteSFixed32(int value)
         {
-            WriteRawLittleEndian32((uint) value);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteSFixed32(ref span, ref state, value);
         }
 
         /// <summary>
@@ -356,7 +364,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteSFixed64(long value)
         {
-            WriteRawLittleEndian64((ulong) value);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteSFixed64(ref span, ref state, value);
         }
 
         /// <summary>
@@ -365,7 +374,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteSInt32(int value)
         {
-            WriteRawVarint32(EncodeZigZag32(value));
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteSInt32(ref span, ref state, value);
         }
 
         /// <summary>
@@ -374,7 +384,8 @@ namespace Google.Protobuf
         /// <param name="value">The value to write</param>
         public void WriteSInt64(long value)
         {
-            WriteRawVarint64(EncodeZigZag64(value));
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteSInt64(ref span, ref state, value);
         }
 
         /// <summary>
@@ -386,7 +397,8 @@ namespace Google.Protobuf
         /// <param name="length">Length value, in bytes.</param>
         public void WriteLength(int length)
         {
-            WriteRawVarint32((uint) length);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteLength(ref span, ref state, length);
         }
 
         #endregion
@@ -399,7 +411,8 @@ namespace Google.Protobuf
         /// <param name="type">The wire format type of the tag to write</param>
         public void WriteTag(int fieldNumber, WireFormat.WireType type)
         {
-            WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteTag(ref span, ref state, fieldNumber, type);
         }
 
         /// <summary>
@@ -408,7 +421,8 @@ namespace Google.Protobuf
         /// <param name="tag">The encoded tag</param>
         public void WriteTag(uint tag)
         {
-            WriteRawVarint32(tag);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteTag(ref span, ref state, tag);
         }
 
         /// <summary>
@@ -417,7 +431,8 @@ namespace Google.Protobuf
         /// <param name="b1">The encoded tag</param>
         public void WriteRawTag(byte b1)
         {
-            WriteRawByte(b1);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawTag(ref span, ref state, b1);
         }
 
         /// <summary>
@@ -427,8 +442,8 @@ namespace Google.Protobuf
         /// <param name="b2">The second byte of the encoded tag</param>
         public void WriteRawTag(byte b1, byte b2)
         {
-            WriteRawByte(b1);
-            WriteRawByte(b2);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2);
         }
 
         /// <summary>
@@ -439,9 +454,8 @@ namespace Google.Protobuf
         /// <param name="b3">The third byte of the encoded tag</param>
         public void WriteRawTag(byte b1, byte b2, byte b3)
         {
-            WriteRawByte(b1);
-            WriteRawByte(b2);
-            WriteRawByte(b3);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3);
         }
 
         /// <summary>
@@ -453,10 +467,8 @@ namespace Google.Protobuf
         /// <param name="b4">The fourth byte of the encoded tag</param>
         public void WriteRawTag(byte b1, byte b2, byte b3, byte b4)
         {
-            WriteRawByte(b1);
-            WriteRawByte(b2);
-            WriteRawByte(b3);
-            WriteRawByte(b4);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4);
         }
 
         /// <summary>
@@ -469,15 +481,13 @@ namespace Google.Protobuf
         /// <param name="b5">The fifth byte of the encoded tag</param>
         public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)
         {
-            WriteRawByte(b1);
-            WriteRawByte(b2);
-            WriteRawByte(b3);
-            WriteRawByte(b4);
-            WriteRawByte(b5);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4, b5);
         }
         #endregion
 
         #region Underlying writing primitives
+        
         /// <summary>
         /// Writes a 32 bit value as a varint. The fast route is taken when
         /// there's enough buffer space left to whizz through without checking
@@ -485,112 +495,26 @@ namespace Google.Protobuf
         /// </summary>
         internal void WriteRawVarint32(uint value)
         {
-            // Optimize for the common case of a single byte value
-            if (value < 128 && position < limit)
-            {
-                buffer[position++] = (byte)value;
-                return;
-            }
-
-            while (value > 127 && position < limit)
-            {
-                buffer[position++] = (byte) ((value & 0x7F) | 0x80);
-                value >>= 7;
-            }
-            while (value > 127)
-            {
-                WriteRawByte((byte) ((value & 0x7F) | 0x80));
-                value >>= 7;
-            }
-            if (position < limit)
-            {
-                buffer[position++] = (byte) value;
-            }
-            else
-            {
-                WriteRawByte((byte) value);
-            }
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawVarint32(ref span, ref state, value);
         }
 
         internal void WriteRawVarint64(ulong value)
         {
-            while (value > 127 && position < limit)
-            {
-                buffer[position++] = (byte) ((value & 0x7F) | 0x80);
-                value >>= 7;
-            }
-            while (value > 127)
-            {
-                WriteRawByte((byte) ((value & 0x7F) | 0x80));
-                value >>= 7;
-            }
-            if (position < limit)
-            {
-                buffer[position++] = (byte) value;
-            }
-            else
-            {
-                WriteRawByte((byte) value);
-            }
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawVarint64(ref span, ref state, value);
         }
 
         internal void WriteRawLittleEndian32(uint value)
         {
-            if (position + 4 > limit)
-            {
-                WriteRawByte((byte) value);
-                WriteRawByte((byte) (value >> 8));
-                WriteRawByte((byte) (value >> 16));
-                WriteRawByte((byte) (value >> 24));
-            }
-            else
-            {
-                buffer[position++] = ((byte) value);
-                buffer[position++] = ((byte) (value >> 8));
-                buffer[position++] = ((byte) (value >> 16));
-                buffer[position++] = ((byte) (value >> 24));
-            }
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawLittleEndian32(ref span, ref state, value);
         }
 
         internal void WriteRawLittleEndian64(ulong value)
         {
-            if (position + 8 > limit)
-            {
-                WriteRawByte((byte) value);
-                WriteRawByte((byte) (value >> 8));
-                WriteRawByte((byte) (value >> 16));
-                WriteRawByte((byte) (value >> 24));
-                WriteRawByte((byte) (value >> 32));
-                WriteRawByte((byte) (value >> 40));
-                WriteRawByte((byte) (value >> 48));
-                WriteRawByte((byte) (value >> 56));
-            }
-            else
-            {
-                buffer[position++] = ((byte) value);
-                buffer[position++] = ((byte) (value >> 8));
-                buffer[position++] = ((byte) (value >> 16));
-                buffer[position++] = ((byte) (value >> 24));
-                buffer[position++] = ((byte) (value >> 32));
-                buffer[position++] = ((byte) (value >> 40));
-                buffer[position++] = ((byte) (value >> 48));
-                buffer[position++] = ((byte) (value >> 56));
-            }
-        }
-
-        internal void WriteRawByte(byte value)
-        {
-            if (position == limit)
-            {
-                RefreshBuffer();
-            }
-
-            buffer[position++] = value;
-        }
-
-        internal void WriteRawByte(uint value)
-        {
-            WriteRawByte((byte) value);
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawLittleEndian64(ref span, ref state, value);
         }
 
         /// <summary>
@@ -606,85 +530,12 @@ namespace Google.Protobuf
         /// </summary>
         internal void WriteRawBytes(byte[] value, int offset, int length)
         {
-            if (limit - position >= length)
-            {
-                ByteArray.Copy(value, offset, buffer, position, length);
-                // We have room in the current buffer.
-                position += length;
-            }
-            else
-            {
-                // Write extends past current buffer.  Fill the rest of this buffer and
-                // flush.
-                int bytesWritten = limit - position;
-                ByteArray.Copy(value, offset, buffer, position, bytesWritten);
-                offset += bytesWritten;
-                length -= bytesWritten;
-                position = limit;
-                RefreshBuffer();
-
-                // Now deal with the rest.
-                // Since we have an output stream, this is our buffer
-                // and buffer offset == 0
-                if (length <= limit)
-                {
-                    // Fits in new buffer.
-                    ByteArray.Copy(value, offset, buffer, 0, length);
-                    position = length;
-                }
-                else
-                {
-                    // Write is very big.  Let's do it all at once.
-                    output.Write(value, offset, length);
-                }
-            }
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawBytes(ref span, ref state, value, offset, length);
         }
 
         #endregion
 
-        /// <summary>
-        /// Encode a 32-bit value with ZigZag encoding.
-        /// </summary>
-        /// <remarks>
-        /// ZigZag encodes signed integers into values that can be efficiently
-        /// encoded with varint.  (Otherwise, negative values must be 
-        /// sign-extended to 64 bits to be varint encoded, thus always taking
-        /// 10 bytes on the wire.)
-        /// </remarks>
-        internal static uint EncodeZigZag32(int n)
-        {
-            // Note:  the right-shift must be arithmetic
-            return (uint) ((n << 1) ^ (n >> 31));
-        }
-
-        /// <summary>
-        /// Encode a 64-bit value with ZigZag encoding.
-        /// </summary>
-        /// <remarks>
-        /// ZigZag encodes signed integers into values that can be efficiently
-        /// encoded with varint.  (Otherwise, negative values must be 
-        /// sign-extended to 64 bits to be varint encoded, thus always taking
-        /// 10 bytes on the wire.)
-        /// </remarks>
-        internal static ulong EncodeZigZag64(long n)
-        {
-            return (ulong) ((n << 1) ^ (n >> 63));
-        }
-
-        private void RefreshBuffer()
-        {
-            if (output == null)
-            {
-                // We're writing to a single buffer.
-                throw new OutOfSpaceException();
-            }
-
-            // Since we have an output stream, this is our buffer
-            // and buffer offset == 0
-            output.Write(buffer, 0, position);
-            position = 0;
-        }
-
         /// <summary>
         /// Indicates that a CodedOutputStream wrapping a flat byte array
         /// ran out of space.
@@ -726,45 +577,31 @@ namespace Google.Protobuf
         /// </summary>
         public void Flush()
         {
-            if (output != null)
-            {
-                RefreshBuffer();
-            }
+            var span = new Span<byte>(buffer);
+            WriteBufferHelper.Flush(ref span, ref state);
         }
 
         /// <summary>
         /// Verifies that SpaceLeft returns zero. It's common to create a byte array
         /// that is exactly big enough to hold a message, then write to it with
         /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
-        /// the message was actually as big as expected, which can help bugs.
+        /// the message was actually as big as expected, which can help finding bugs.
         /// </summary>
         public void CheckNoSpaceLeft()
         {
-            if (SpaceLeft != 0)
-            {
-                throw new InvalidOperationException("Did not write as much data as expected.");
-            }
+            WriteBufferHelper.CheckNoSpaceLeft(ref state);
         }
 
         /// <summary>
         /// If writing to a flat array, returns the space left in the array. Otherwise,
         /// throws an InvalidOperationException.
         /// </summary>
-        public int SpaceLeft
-        {
-            get
-            {
-                if (output == null)
-                {
-                    return limit - position;
-                }
-                else
-                {
-                    throw new InvalidOperationException(
-                        "SpaceLeft can only be called on CodedOutputStreams that are " +
-                        "writing to a flat array.");
-                }
-            }
-        }
+        public int SpaceLeft => WriteBufferHelper.GetSpaceLeft(ref state);
+
+        internal byte[] InternalBuffer => buffer;
+
+        internal Stream InternalOutputStream => output;
+
+        internal ref WriterInternalState InternalState => ref state;
     }
 }

+ 31 - 4
csharp/src/Google.Protobuf/Collections/MapField.cs

@@ -464,14 +464,34 @@ namespace Google.Protobuf.Collections
         /// <param name="output">The output stream to write to.</param>
         /// <param name="codec">The codec to use for each entry.</param>
         public void WriteTo(CodedOutputStream output, Codec codec)
+        {
+            WriteContext.Initialize(output, out WriteContext ctx);
+            try
+            {
+                WriteTo(ref ctx, codec);
+            }
+            finally
+            {
+                ctx.CopyStateTo(output);
+            }
+        }
+
+        /// <summary>
+        /// Writes the contents of this map to the given write context, using the specified codec
+        /// to encode each entry.
+        /// </summary>
+        /// <param name="ctx">The write context to write to.</param>
+        /// <param name="codec">The codec to use for each entry.</param>
+        [SecuritySafeCritical]
+        public void WriteTo(ref WriteContext ctx, Codec codec)
         {
             var message = new Codec.MessageAdapter(codec);
             foreach (var entry in list)
             {
                 message.Key = entry.Key;
                 message.Value = entry.Value;
-                output.WriteTag(codec.MapTag);
-                output.WriteMessage(message);
+                ctx.WriteTag(codec.MapTag);
+                ctx.WriteMessage(message);
             }
         }
 
@@ -711,8 +731,15 @@ namespace Google.Protobuf.Collections
 
                 public void WriteTo(CodedOutputStream output)
                 {
-                    codec.keyCodec.WriteTagAndValue(output, Key);
-                    codec.valueCodec.WriteTagAndValue(output, Value);
+                    // Message adapter is an internal class and we know that all the writing will happen via InternalWriteTo.
+                    throw new NotImplementedException();
+                }
+
+                [SecuritySafeCritical]
+                public void InternalWriteTo(ref WriteContext ctx)
+                {
+                    codec.keyCodec.WriteTagAndValue(ref ctx, Key);
+                    codec.valueCodec.WriteTagAndValue(ref ctx, Value);
                 }
 
                 public int CalculateSize()

+ 28 - 8
csharp/src/Google.Protobuf/Collections/RepeatedField.cs

@@ -189,7 +189,7 @@ namespace Google.Protobuf.Collections
         /// Calculates the size of this collection based on the given codec.
         /// </summary>
         /// <param name="codec">The codec to use when encoding each field.</param>
-        /// <returns>The number of bytes that would be written to a <see cref="CodedOutputStream"/> by <see cref="WriteTo"/>,
+        /// <returns>The number of bytes that would be written to an output by one of the <c>WriteTo</c> methods,
         /// using the same codec.</returns>
         public int CalculateSize(FieldCodec<T> codec)
         {
@@ -247,6 +247,26 @@ namespace Google.Protobuf.Collections
         /// <param name="output">The output stream to write to.</param>
         /// <param name="codec">The codec to use when encoding each value.</param>
         public void WriteTo(CodedOutputStream output, FieldCodec<T> codec)
+        {
+            WriteContext.Initialize(output, out WriteContext ctx);
+            try
+            {
+                WriteTo(ref ctx, codec);
+            }
+            finally
+            {
+                ctx.CopyStateTo(output);
+            }
+        }
+
+        /// <summary>
+        /// Writes the contents of this collection to the given write context,
+        /// encoding each value using the specified codec.
+        /// </summary>
+        /// <param name="ctx">The write context to write to.</param>
+        /// <param name="codec">The codec to use when encoding each value.</param>
+        [SecuritySafeCritical]
+        public void WriteTo(ref WriteContext ctx, FieldCodec<T> codec)
         {
             if (count == 0)
             {
@@ -257,12 +277,12 @@ namespace Google.Protobuf.Collections
             if (codec.PackedRepeatedField)
             {
                 // Packed primitive type
-                uint size = (uint)CalculatePackedDataSize(codec);
-                output.WriteTag(tag);
-                output.WriteRawVarint32(size);
+                int size = CalculatePackedDataSize(codec);
+                ctx.WriteTag(tag);
+                ctx.WriteLength(size);
                 for (int i = 0; i < count; i++)
                 {
-                    writer(output, array[i]);
+                    writer(ref ctx, array[i]);
                 }
             }
             else
@@ -271,11 +291,11 @@ namespace Google.Protobuf.Collections
                 // Can't use codec.WriteTagAndValue, as that omits default values.
                 for (int i = 0; i < count; i++)
                 {
-                    output.WriteTag(tag);
-                    writer(output, array[i]);
+                    ctx.WriteTag(tag);
+                    writer(ref ctx, array[i]);
                     if (codec.EndTag != 0)
                     {
-                        output.WriteTag(codec.EndTag);
+                        ctx.WriteTag(codec.EndTag);
                     }
                 }
             }

+ 20 - 1
csharp/src/Google.Protobuf/ExtensionSet.cs

@@ -34,6 +34,7 @@ using Google.Protobuf.Collections;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Security;
 
 namespace Google.Protobuf
 {
@@ -343,10 +344,28 @@ namespace Google.Protobuf
         /// Writes the extension values in this set to the output stream
         /// </summary>
         public void WriteTo(CodedOutputStream stream)
+        {
+            
+            WriteContext.Initialize(stream, out WriteContext ctx);
+            try
+            {
+                WriteTo(ref ctx);
+            }
+            finally
+            {
+                ctx.CopyStateTo(stream);
+            }
+        }
+
+        /// <summary>
+        /// Writes the extension values in this set to the write context
+        /// </summary>
+        [SecuritySafeCritical]
+        public void WriteTo(ref WriteContext ctx)
         {
             foreach (var value in ValuesByNumber.Values)
             {
-                value.WriteTo(stream);
+                value.WriteTo(ref ctx);
             }
         }
 

+ 7 - 12
csharp/src/Google.Protobuf/ExtensionValue.cs

@@ -41,7 +41,7 @@ namespace Google.Protobuf
         void MergeFrom(ref ParseContext ctx);
 
         void MergeFrom(IExtensionValue value);
-        void WriteTo(CodedOutputStream output);
+        void WriteTo(ref WriteContext ctx);
         int CalculateSize();
         bool IsInitialized();
     }
@@ -106,13 +106,13 @@ namespace Google.Protobuf
             }
         }
 
-        public void WriteTo(CodedOutputStream output)
+        public void WriteTo(ref WriteContext ctx)
         {
-            output.WriteTag(codec.Tag);
-            codec.ValueWriter(output, field);
+            ctx.WriteTag(codec.Tag);
+            codec.ValueWriter(ref ctx, field);
             if (codec.EndTag != 0)
             {
-                output.WriteTag(codec.EndTag);
+                ctx.WriteTag(codec.EndTag);
             }
         }
 
@@ -181,11 +181,6 @@ namespace Google.Protobuf
             }
         }
 
-        public void MergeFrom(CodedInputStream input)
-        {
-            field.AddEntriesFrom(input, codec);
-        }
-
         public void MergeFrom(ref ParseContext ctx)
         {
             field.AddEntriesFrom(ref ctx, codec);
@@ -199,9 +194,9 @@ namespace Google.Protobuf
             }
         }
 
-        public void WriteTo(CodedOutputStream output)
+        public void WriteTo(ref WriteContext ctx)
         {
-            field.WriteTo(output, codec);
+            field.WriteTo(ref ctx, codec);
         }
 
         public RepeatedField<T> GetValue() => field;

+ 59 - 31
csharp/src/Google.Protobuf/FieldCodec.cs

@@ -219,7 +219,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<string> ForString(uint tag, string defaultValue)
         {
-            return new FieldCodec<string>((ref ParseContext ctx) => ctx.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag, defaultValue);
+            return new FieldCodec<string>((ref ParseContext ctx) => ctx.ReadString(), (ref WriteContext ctx, string value) => ctx.WriteString(value), CodedOutputStream.ComputeStringSize, tag, defaultValue);
         }
 
         /// <summary>
@@ -230,7 +230,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<ByteString> ForBytes(uint tag, ByteString defaultValue)
         {
-            return new FieldCodec<ByteString>((ref ParseContext ctx) => ctx.ReadBytes(), (output, value) => output.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag, defaultValue);
+            return new FieldCodec<ByteString>((ref ParseContext ctx) => ctx.ReadBytes(), (ref WriteContext ctx, ByteString value) => ctx.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag, defaultValue);
         }
 
         /// <summary>
@@ -241,7 +241,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<bool> ForBool(uint tag, bool defaultValue)
         {
-            return new FieldCodec<bool>((ref ParseContext ctx) => ctx.ReadBool(), (output, value) => output.WriteBool(value), CodedOutputStream.BoolSize, tag, defaultValue);
+            return new FieldCodec<bool>((ref ParseContext ctx) => ctx.ReadBool(), (ref WriteContext ctx, bool value) => ctx.WriteBool(value), CodedOutputStream.BoolSize, tag, defaultValue);
         }
 
         /// <summary>
@@ -252,7 +252,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<int> ForInt32(uint tag, int defaultValue)
         {
-            return new FieldCodec<int>((ref ParseContext ctx) => ctx.ReadInt32(), (output, value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag, defaultValue);
+            return new FieldCodec<int>((ref ParseContext ctx) => ctx.ReadInt32(), (ref WriteContext output, int value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag, defaultValue);
         }
 
         /// <summary>
@@ -263,7 +263,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<int> ForSInt32(uint tag, int defaultValue)
         {
-            return new FieldCodec<int>((ref ParseContext ctx) => ctx.ReadSInt32(), (output, value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag, defaultValue);
+            return new FieldCodec<int>((ref ParseContext ctx) => ctx.ReadSInt32(), (ref WriteContext output, int value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag, defaultValue);
         }
 
         /// <summary>
@@ -274,7 +274,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<uint> ForFixed32(uint tag, uint defaultValue)
         {
-            return new FieldCodec<uint>((ref ParseContext ctx) => ctx.ReadFixed32(), (output, value) => output.WriteFixed32(value), 4, tag, defaultValue);
+            return new FieldCodec<uint>((ref ParseContext ctx) => ctx.ReadFixed32(), (ref WriteContext output, uint value) => output.WriteFixed32(value), 4, tag, defaultValue);
         }
 
         /// <summary>
@@ -285,7 +285,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<int> ForSFixed32(uint tag, int defaultValue)
         {
-            return new FieldCodec<int>((ref ParseContext ctx) => ctx.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), 4, tag, defaultValue);
+            return new FieldCodec<int>((ref ParseContext ctx) => ctx.ReadSFixed32(), (ref WriteContext output, int value) => output.WriteSFixed32(value), 4, tag, defaultValue);
         }
 
         /// <summary>
@@ -296,7 +296,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<uint> ForUInt32(uint tag, uint defaultValue)
         {
-            return new FieldCodec<uint>((ref ParseContext ctx) => ctx.ReadUInt32(), (output, value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag, defaultValue);
+            return new FieldCodec<uint>((ref ParseContext ctx) => ctx.ReadUInt32(), (ref WriteContext output, uint value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag, defaultValue);
         }
 
         /// <summary>
@@ -307,7 +307,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<long> ForInt64(uint tag, long defaultValue)
         {
-            return new FieldCodec<long>((ref ParseContext ctx) => ctx.ReadInt64(), (output, value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag, defaultValue);
+            return new FieldCodec<long>((ref ParseContext ctx) => ctx.ReadInt64(), (ref WriteContext output, long value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag, defaultValue);
         }
 
         /// <summary>
@@ -318,7 +318,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<long> ForSInt64(uint tag, long defaultValue)
         {
-            return new FieldCodec<long>((ref ParseContext ctx) => ctx.ReadSInt64(), (output, value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag, defaultValue);
+            return new FieldCodec<long>((ref ParseContext ctx) => ctx.ReadSInt64(), (ref WriteContext output, long value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag, defaultValue);
         }
 
         /// <summary>
@@ -329,7 +329,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<ulong> ForFixed64(uint tag, ulong defaultValue)
         {
-            return new FieldCodec<ulong>((ref ParseContext ctx) => ctx.ReadFixed64(), (output, value) => output.WriteFixed64(value), 8, tag, defaultValue);
+            return new FieldCodec<ulong>((ref ParseContext ctx) => ctx.ReadFixed64(), (ref WriteContext output, ulong value) => output.WriteFixed64(value), 8, tag, defaultValue);
         }
 
         /// <summary>
@@ -340,7 +340,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<long> ForSFixed64(uint tag, long defaultValue)
         {
-            return new FieldCodec<long>((ref ParseContext ctx) => ctx.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), 8, tag, defaultValue);
+            return new FieldCodec<long>((ref ParseContext ctx) => ctx.ReadSFixed64(), (ref WriteContext output, long value) => output.WriteSFixed64(value), 8, tag, defaultValue);
         }
 
         /// <summary>
@@ -351,7 +351,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<ulong> ForUInt64(uint tag, ulong defaultValue)
         {
-            return new FieldCodec<ulong>((ref ParseContext ctx) => ctx.ReadUInt64(), (output, value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag, defaultValue);
+            return new FieldCodec<ulong>((ref ParseContext ctx) => ctx.ReadUInt64(), (ref WriteContext output, ulong value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag, defaultValue);
         }
 
         /// <summary>
@@ -362,7 +362,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<float> ForFloat(uint tag, float defaultValue)
         {
-            return new FieldCodec<float>((ref ParseContext ctx) => ctx.ReadFloat(), (output, value) => output.WriteFloat(value), CodedOutputStream.FloatSize, tag, defaultValue);
+            return new FieldCodec<float>((ref ParseContext ctx) => ctx.ReadFloat(), (ref WriteContext output, float value) => output.WriteFloat(value), CodedOutputStream.FloatSize, tag, defaultValue);
         }
 
         /// <summary>
@@ -373,7 +373,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<double> ForDouble(uint tag, double defaultValue)
         {
-            return new FieldCodec<double>((ref ParseContext ctx) => ctx.ReadDouble(), (output, value) => output.WriteDouble(value), CodedOutputStream.DoubleSize, tag, defaultValue);
+            return new FieldCodec<double>((ref ParseContext ctx) => ctx.ReadDouble(), (ref WriteContext output, double value) => output.WriteDouble(value), CodedOutputStream.DoubleSize, tag, defaultValue);
         }
 
         // Enums are tricky. We can probably use expression trees to build these delegates automatically,
@@ -391,7 +391,7 @@ namespace Google.Protobuf
         {
             return new FieldCodec<T>((ref ParseContext ctx) => fromInt32(
                 ctx.ReadEnum()),
-                (output, value) => output.WriteEnum(toInt32(value)),
+                (ref WriteContext output, T value) => output.WriteEnum(toInt32(value)),
                 value => CodedOutputStream.ComputeEnumSize(toInt32(value)), tag, defaultValue);
         }
 
@@ -410,7 +410,7 @@ namespace Google.Protobuf
                     ctx.ReadMessage(message); 
                     return message; 
                 },
-                (output, value) => output.WriteMessage(value),
+                (ref WriteContext output, T value) => output.WriteMessage(value),
                 (ref ParseContext ctx, ref T v) => 
                 {
                     if (v == null)
@@ -455,7 +455,7 @@ namespace Google.Protobuf
                     ctx.ReadGroup(message);
                     return message;
                 },
-                (output, value) => output.WriteGroup(value), 
+                (ref WriteContext output, T value) => output.WriteGroup(value), 
                 (ref ParseContext ctx, ref T v) => 
                 {
                     if (v == null)
@@ -492,7 +492,7 @@ namespace Google.Protobuf
             var nestedCodec = WrapperCodecs.GetCodec<T>();
             return new FieldCodec<T>(
                 (ref ParseContext ctx) => WrapperCodecs.Read<T>(ref ctx, nestedCodec),
-                (output, value) => WrapperCodecs.Write<T>(output, value, nestedCodec),
+                (ref WriteContext output, T value) => WrapperCodecs.Write<T>(ref output, value, nestedCodec),
                 (ref ParseContext ctx, ref T v) => v = WrapperCodecs.Read<T>(ref ctx, nestedCodec),
                 (ref T v, T v2) => { v = v2; return v == null; },
                 value => WrapperCodecs.CalculateSize<T>(value, nestedCodec),
@@ -509,7 +509,7 @@ namespace Google.Protobuf
             var nestedCodec = WrapperCodecs.GetCodec<T>();
             return new FieldCodec<T?>(
                 WrapperCodecs.GetReader<T>(),
-                (output, value) => WrapperCodecs.Write<T>(output, value.Value, nestedCodec),
+                (ref WriteContext output, T? value) => WrapperCodecs.Write<T>(ref output, value.Value, nestedCodec),
                 (ref ParseContext ctx, ref T? v) => v = WrapperCodecs.Read<T>(ref ctx, nestedCodec),
                 (ref T? v, T? v2) => { if (v2.HasValue) { v = v2; } return v.HasValue; },
                 value => value == null ? 0 : WrapperCodecs.CalculateSize<T>(value.Value, nestedCodec),
@@ -616,10 +616,10 @@ namespace Google.Protobuf
                 return value;
             }
 
-            internal static void Write<T>(CodedOutputStream output, T value, FieldCodec<T> codec)
+            internal static void Write<T>(ref WriteContext ctx, T value, FieldCodec<T> codec)
             {
-                output.WriteLength(codec.CalculateSizeWithTag(value));
-                codec.WriteTagAndValue(output, value);
+                ctx.WriteLength(codec.CalculateSizeWithTag(value));
+                codec.WriteTagAndValue(ref ctx, value);
             }
 
             internal  static int CalculateSize<T>(T value, FieldCodec<T> codec)
@@ -631,6 +631,7 @@ namespace Google.Protobuf
     }
 
     internal delegate TValue ValueReader<out TValue>(ref ParseContext ctx);
+    internal delegate void ValueWriter<T>(ref WriteContext ctx, T value);
 
     /// <summary>
     /// <para>
@@ -685,7 +686,7 @@ namespace Google.Protobuf
         /// <summary>
         /// Returns a delegate to write a value (unconditionally) to a coded output stream.
         /// </summary>
-        internal Action<CodedOutputStream, T> ValueWriter { get; }
+        internal ValueWriter<T> ValueWriter { get; }
 
         /// <summary>
         /// Returns the size calculator for just a value.
@@ -744,7 +745,7 @@ namespace Google.Protobuf
 
         internal FieldCodec(
                 ValueReader<T> reader,
-                Action<CodedOutputStream, T> writer,
+                ValueWriter<T> writer,
                 int fixedSize,
                 uint tag,
                 T defaultValue) : this(reader, writer, _ => fixedSize, tag, defaultValue)
@@ -754,7 +755,7 @@ namespace Google.Protobuf
 
         internal FieldCodec(
             ValueReader<T> reader,
-            Action<CodedOutputStream, T> writer,
+            ValueWriter<T> writer,
             Func<T, int> sizeCalculator,
             uint tag,
             T defaultValue) : this(reader, writer, (ref ParseContext ctx, ref T v) => v = reader(ref ctx), (ref T v, T v2) => { v = v2; return true; }, sizeCalculator, tag, 0, defaultValue)
@@ -763,7 +764,7 @@ namespace Google.Protobuf
 
         internal FieldCodec(
             ValueReader<T> reader,
-            Action<CodedOutputStream, T> writer,
+            ValueWriter<T> writer,
             InputMerger inputMerger,
             ValuesMerger valuesMerger,
             Func<T, int> sizeCalculator,
@@ -774,7 +775,7 @@ namespace Google.Protobuf
 
         internal FieldCodec(
             ValueReader<T> reader,
-            Action<CodedOutputStream, T> writer,
+            ValueWriter<T> writer,
             InputMerger inputMerger,
             ValuesMerger valuesMerger,
             Func<T, int> sizeCalculator,
@@ -802,14 +803,41 @@ namespace Google.Protobuf
         /// Write a tag and the given value, *if* the value is not the default.
         /// </summary>
         public void WriteTagAndValue(CodedOutputStream output, T value)
+        {
+            WriteContext.Initialize(output, out WriteContext ctx);
+            try
+            {
+                WriteTagAndValue(ref ctx, value);
+            }
+            finally
+            {
+                ctx.CopyStateTo(output);
+            }
+
+
+            //if (!IsDefault(value))
+            //{
+            //    output.WriteTag(Tag);
+            //    ValueWriter(output, value);
+            //    if (EndTag != 0)
+            //    {
+            //        output.WriteTag(EndTag);
+            //    }
+            //}
+        }
+
+        /// <summary>
+        /// Write a tag and the given value, *if* the value is not the default.
+        /// </summary>
+        public void WriteTagAndValue(ref WriteContext ctx, T value)
         {
             if (!IsDefault(value))
             {
-                output.WriteTag(Tag);
-                ValueWriter(output, value);
+                ctx.WriteTag(Tag);
+                ValueWriter(ref ctx, value);
                 if (EndTag != 0)
                 {
-                    output.WriteTag(EndTag);
+                    ctx.WriteTag(EndTag);
                 }
             }
         }

+ 7 - 1
csharp/src/Google.Protobuf/IBufferMessage.cs

@@ -35,7 +35,7 @@ namespace Google.Protobuf
 #if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY
     /// <summary>
     /// Interface for a Protocol Buffers message, supporting
-    /// parsing from <see cref="ParseContext"/>.
+    /// parsing from <see cref="ParseContext"/> and writing to <see cref="WriteContext"/>.
     /// </summary>
     public interface IBufferMessage : IMessage
     {
@@ -44,6 +44,12 @@ namespace Google.Protobuf
         /// Users should never invoke this method directly.
         /// </summary>        
         void InternalMergeFrom(ref ParseContext ctx);
+
+        /// <summary>
+        /// Internal implementation of writing this message to a given write context.
+        /// Users should never invoke this method directly.
+        /// </summary>        
+        void InternalWriteTo(ref WriteContext ctx);
     }
 #endif
 }

+ 35 - 1
csharp/src/Google.Protobuf/MessageExtensions.cs

@@ -33,6 +33,7 @@
 using Google.Protobuf.Reflection;
 using System.Buffers;
 using System.Collections;
+using System;
 using System.IO;
 using System.Linq;
 using System.Security;
@@ -129,7 +130,7 @@ namespace Google.Protobuf
             ProtoPreconditions.CheckNotNull(message, "message");
             ProtoPreconditions.CheckNotNull(output, "output");
             CodedOutputStream codedOutput = new CodedOutputStream(output);
-            codedOutput.WriteRawVarint32((uint)message.CalculateSize());
+            codedOutput.WriteLength(message.CalculateSize());
             message.WriteTo(codedOutput);
             codedOutput.Flush();
         }
@@ -145,6 +146,39 @@ namespace Google.Protobuf
             return ByteString.AttachBytes(message.ToByteArray());
         }
 
+        /// <summary>
+        /// Writes the given message data to the given buffer writer in protobuf encoding.
+        /// </summary>
+        /// <param name="message">The message to write to the stream.</param>
+        /// <param name="output">The stream to write to.</param>
+        [SecuritySafeCritical]
+        public static void WriteTo(this IMessage message, IBufferWriter<byte> output)
+        {
+            ProtoPreconditions.CheckNotNull(message, nameof(message));
+            ProtoPreconditions.CheckNotNull(output, nameof(output));
+
+            WriteContext.Initialize(output, out WriteContext ctx);
+            WritingPrimitivesMessages.WriteRawMessage(ref ctx, message);
+            ctx.Flush();
+        }
+
+        /// <summary>
+        /// Writes the given message data to the given span in protobuf encoding.
+        /// The size of the destination span needs to fit the serialized size
+        /// of the message exactly, otherwise an exception is thrown.
+        /// </summary>
+        /// <param name="message">The message to write to the stream.</param>
+        /// <param name="output">The span to write to. Size must match size of the message exactly.</param>
+        [SecuritySafeCritical]
+        public static void WriteTo(this IMessage message, Span<byte> output)
+        {
+            ProtoPreconditions.CheckNotNull(message, nameof(message));
+
+            WriteContext.Initialize(ref output, out WriteContext ctx);
+            WritingPrimitivesMessages.WriteRawMessage(ref ctx, message);
+            ctx.CheckNoSpaceLeft();
+        }
+
         /// <summary>
         /// Checks if all required fields in a message have values set. For proto3 messages, this returns true
         /// </summary>

+ 1 - 1
csharp/src/Google.Protobuf/ParserInternalState.cs

@@ -47,7 +47,7 @@ namespace Google.Protobuf
     // warning: this is a mutable struct, so it needs to be only passed as a ref!
     internal struct ParserInternalState
     {
-        // NOTE: the Span representing the current buffer is kept separate so that this doesn't have to be a ref struct and so it can live
+        // NOTE: the Span representing the current buffer is kept separate so that this doesn't have to be a ref struct and so it can
         // be included in CodedInputStream's internal state
 
         /// <summary>

+ 3 - 3
csharp/src/Google.Protobuf/ParsingPrimitives.cs

@@ -629,7 +629,7 @@ namespace Google.Protobuf
                 {
                     fixed (byte* sourceBytes = &MemoryMarshal.GetReference(data))
                     {
-                        value = CodedOutputStream.Utf8Encoding.GetString(sourceBytes, length);
+                        value = WritingPrimitives.Utf8Encoding.GetString(sourceBytes, length);
                     }
                 }
 
@@ -638,7 +638,7 @@ namespace Google.Protobuf
             }
 #endif
 
-            var decoder = CodedOutputStream.Utf8Encoding.GetDecoder();
+            var decoder = WritingPrimitives.Utf8Encoding.GetDecoder();
 
             // TODO: even if GOOGLE_PROTOBUF_SUPPORT_FAST_STRING is not supported,
             // we could still create a string efficiently by using Utf8Encoding.GetString(byte[] bytes, int index, int count)
@@ -649,7 +649,7 @@ namespace Google.Protobuf
             // creating a string from that array might be more efficient than creating a string from the copied bytes.
 
             // Slow path: Build a byte array first then copy it.
-            return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(ref buffer, ref state, length), 0, length);
+            return WritingPrimitives.Utf8Encoding.GetString(ReadRawBytes(ref buffer, ref state, length), 0, length);
         }
 
         [SecuritySafeCritical]

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 683 - 12
csharp/src/Google.Protobuf/Reflection/Descriptor.cs


+ 3 - 3
csharp/src/Google.Protobuf/UnknownField.cs

@@ -101,8 +101,8 @@ namespace Google.Protobuf
         /// <paramref name="output"/>
         /// </summary>
         /// <param name="fieldNumber">The unknown field number.</param>
-        /// <param name="output">The CodedOutputStream to write to.</param>
-        internal void WriteTo(int fieldNumber, CodedOutputStream output)
+        /// <param name="output">The write context to write to.</param>
+        internal void WriteTo(int fieldNumber, ref WriteContext output)
         {
             if (varintList != null)
             {
@@ -141,7 +141,7 @@ namespace Google.Protobuf
                 foreach (UnknownFieldSet value in groupList)
                 {
                     output.WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
-                    value.WriteTo(output);
+                    value.WriteTo(ref output);
                     output.WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
                 }
             }

+ 18 - 1
csharp/src/Google.Protobuf/UnknownFieldSet.cs

@@ -71,10 +71,27 @@ namespace Google.Protobuf
         /// Serializes the set and writes it to <paramref name="output"/>.
         /// </summary>
         public void WriteTo(CodedOutputStream output)
+        {
+            WriteContext.Initialize(output, out WriteContext ctx);
+            try
+            {
+                WriteTo(ref ctx);
+            }
+            finally
+            {
+                ctx.CopyStateTo(output);
+            }
+        }
+
+        /// <summary>
+        /// Serializes the set and writes it to <paramref name="ctx"/>.
+        /// </summary>
+        [SecuritySafeCritical]
+        public void WriteTo(ref WriteContext ctx)
         {
             foreach (KeyValuePair<int, UnknownField> entry in fields)
             {
-                entry.Value.WriteTo(entry.Key, output);
+                entry.Value.WriteTo(entry.Key, ref ctx);
             }
         }
 

+ 21 - 0
csharp/src/Google.Protobuf/WellKnownTypes/Any.cs

@@ -248,6 +248,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (TypeUrl.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(TypeUrl);
@@ -259,7 +262,25 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TypeUrl.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TypeUrl);
+      }
+      if (Value.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteBytes(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 91 - 0
csharp/src/Google.Protobuf/WellKnownTypes/Api.cs

@@ -269,6 +269,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Name.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Name);
@@ -291,8 +294,37 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+      methods_.WriteTo(ref output, _repeated_methods_codec);
+      options_.WriteTo(ref output, _repeated_options_codec);
+      if (Version.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Version);
+      }
+      if (sourceContext_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(SourceContext);
+      }
+      mixins_.WriteTo(ref output, _repeated_mixins_codec);
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) Syntax);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -627,6 +659,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Name.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Name);
@@ -655,7 +690,42 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+      if (RequestTypeUrl.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(RequestTypeUrl);
+      }
+      if (RequestStreaming != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(RequestStreaming);
+      }
+      if (ResponseTypeUrl.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ResponseTypeUrl);
+      }
+      if (ResponseStreaming != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(ResponseStreaming);
+      }
+      options_.WriteTo(ref output, _repeated_options_codec);
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) Syntax);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -984,6 +1054,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Name.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Name);
@@ -995,8 +1068,26 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+      if (Root.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Root);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;

+ 21 - 0
csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs

@@ -210,6 +210,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Seconds != 0L) {
         output.WriteRawTag(8);
         output.WriteInt64(Seconds);
@@ -221,7 +224,25 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Seconds != 0L) {
+        output.WriteRawTag(8);
+        output.WriteInt64(Seconds);
+      }
+      if (Nanos != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Nanos);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 13 - 0
csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs

@@ -119,10 +119,23 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 14 - 0
csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs

@@ -325,11 +325,25 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       paths_.WriteTo(output, _repeated_paths_codec);
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      paths_.WriteTo(ref output, _repeated_paths_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 17 - 0
csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs

@@ -131,6 +131,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (FileName.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(FileName);
@@ -138,7 +141,21 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (FileName.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(FileName);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 65 - 0
csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs

@@ -162,11 +162,25 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       fields_.WriteTo(output, _map_fields_codec);
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      fields_.WriteTo(ref output, _map_fields_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -446,6 +460,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (kindCase_ == KindOneofCase.NullValue) {
         output.WriteRawTag(8);
         output.WriteEnum((int) NullValue);
@@ -473,7 +490,41 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (kindCase_ == KindOneofCase.NullValue) {
+        output.WriteRawTag(8);
+        output.WriteEnum((int) NullValue);
+      }
+      if (kindCase_ == KindOneofCase.NumberValue) {
+        output.WriteRawTag(17);
+        output.WriteDouble(NumberValue);
+      }
+      if (kindCase_ == KindOneofCase.StringValue) {
+        output.WriteRawTag(26);
+        output.WriteString(StringValue);
+      }
+      if (kindCase_ == KindOneofCase.BoolValue) {
+        output.WriteRawTag(32);
+        output.WriteBool(BoolValue);
+      }
+      if (kindCase_ == KindOneofCase.StructValue) {
+        output.WriteRawTag(42);
+        output.WriteMessage(StructValue);
+      }
+      if (kindCase_ == KindOneofCase.ListValue) {
+        output.WriteRawTag(50);
+        output.WriteMessage(ListValue);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -729,12 +780,26 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       values_.WriteTo(output, _repeated_values_codec);
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      values_.WriteTo(ref output, _repeated_values_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;

+ 21 - 0
csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs

@@ -231,6 +231,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Seconds != 0L) {
         output.WriteRawTag(8);
         output.WriteInt64(Seconds);
@@ -242,7 +245,25 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Seconds != 0L) {
+        output.WriteRawTag(8);
+        output.WriteInt64(Seconds);
+      }
+      if (Nanos != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Nanos);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {

+ 148 - 0
csharp/src/Google.Protobuf/WellKnownTypes/Type.cs

@@ -262,6 +262,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Name.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Name);
@@ -280,7 +283,32 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+      fields_.WriteTo(ref output, _repeated_fields_codec);
+      oneofs_.WriteTo(ref output, _repeated_oneofs_codec);
+      options_.WriteTo(ref output, _repeated_options_codec);
+      if (sourceContext_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(SourceContext);
+      }
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
+        output.WriteRawTag(48);
+        output.WriteEnum((int) Syntax);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -655,6 +683,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Kind != global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TypeUnknown) {
         output.WriteRawTag(8);
         output.WriteEnum((int) Kind);
@@ -695,7 +726,54 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Kind != global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TypeUnknown) {
+        output.WriteRawTag(8);
+        output.WriteEnum((int) Kind);
+      }
+      if (Cardinality != global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.Unknown) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Cardinality);
+      }
+      if (Number != 0) {
+        output.WriteRawTag(24);
+        output.WriteInt32(Number);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Name);
+      }
+      if (TypeUrl.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(TypeUrl);
+      }
+      if (OneofIndex != 0) {
+        output.WriteRawTag(56);
+        output.WriteInt32(OneofIndex);
+      }
+      if (Packed != false) {
+        output.WriteRawTag(64);
+        output.WriteBool(Packed);
+      }
+      options_.WriteTo(ref output, _repeated_options_codec);
+      if (JsonName.Length != 0) {
+        output.WriteRawTag(82);
+        output.WriteString(JsonName);
+      }
+      if (DefaultValue.Length != 0) {
+        output.WriteRawTag(90);
+        output.WriteString(DefaultValue);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1148,6 +1226,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Name.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Name);
@@ -1165,7 +1246,31 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+      enumvalue_.WriteTo(ref output, _repeated_enumvalue_codec);
+      options_.WriteTo(ref output, _repeated_options_codec);
+      if (sourceContext_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(SourceContext);
+      }
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
+        output.WriteRawTag(40);
+        output.WriteEnum((int) Syntax);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1409,6 +1514,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Name.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Name);
@@ -1421,8 +1529,27 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+      if (Number != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Number);
+      }
+      options_.WriteTo(ref output, _repeated_options_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -1623,6 +1750,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Name.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Name);
@@ -1634,8 +1764,26 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+      if (value_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;

+ 153 - 0
csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs

@@ -143,6 +143,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Value != 0D) {
         output.WriteRawTag(9);
         output.WriteDouble(Value);
@@ -150,7 +153,21 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Value != 0D) {
+        output.WriteRawTag(9);
+        output.WriteDouble(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -306,6 +323,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Value != 0F) {
         output.WriteRawTag(13);
         output.WriteFloat(Value);
@@ -313,7 +333,21 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Value != 0F) {
+        output.WriteRawTag(13);
+        output.WriteFloat(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -469,6 +503,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Value != 0L) {
         output.WriteRawTag(8);
         output.WriteInt64(Value);
@@ -476,8 +513,22 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Value != 0L) {
+        output.WriteRawTag(8);
+        output.WriteInt64(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -632,6 +683,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Value != 0UL) {
         output.WriteRawTag(8);
         output.WriteUInt64(Value);
@@ -639,8 +693,22 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Value != 0UL) {
+        output.WriteRawTag(8);
+        output.WriteUInt64(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -795,6 +863,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Value != 0) {
         output.WriteRawTag(8);
         output.WriteInt32(Value);
@@ -802,7 +873,21 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Value != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -958,6 +1043,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Value != 0) {
         output.WriteRawTag(8);
         output.WriteUInt32(Value);
@@ -965,7 +1053,21 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Value != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
     }
+    #endif
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
@@ -1121,6 +1223,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Value != false) {
         output.WriteRawTag(8);
         output.WriteBool(Value);
@@ -1128,8 +1233,22 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Value != false) {
+        output.WriteRawTag(8);
+        output.WriteBool(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -1284,6 +1403,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Value.Length != 0) {
         output.WriteRawTag(10);
         output.WriteString(Value);
@@ -1291,8 +1413,22 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Value.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
@@ -1447,6 +1583,9 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
       if (Value.Length != 0) {
         output.WriteRawTag(10);
         output.WriteBytes(Value);
@@ -1454,8 +1593,22 @@ namespace Google.Protobuf.WellKnownTypes {
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
+    #endif
     }
 
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Value.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;

+ 166 - 0
csharp/src/Google.Protobuf/WriteBufferHelper.cs

@@ -0,0 +1,166 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Buffers;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Security;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Abstraction for writing to a steam / IBufferWriter
+    /// </summary>
+    [SecuritySafeCritical]
+    internal struct WriteBufferHelper
+    {
+        private IBufferWriter<byte> bufferWriter;
+        private CodedOutputStream codedOutputStream;
+
+        public CodedOutputStream CodedOutputStream => codedOutputStream;
+
+        /// <summary>
+        /// Initialize an instance with a coded output stream.
+        /// This approach is faster than using a constructor because the instance to initialize is passed by reference
+        /// and we can write directly into it without copying.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void Initialize(CodedOutputStream codedOutputStream, out WriteBufferHelper instance)
+        {
+            instance.bufferWriter = null;
+            instance.codedOutputStream = codedOutputStream;
+        }
+
+        /// <summary>
+        /// Initialize an instance with a buffer writer.
+        /// This approach is faster than using a constructor because the instance to initialize is passed by reference
+        /// and we can write directly into it without copying.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void Initialize(IBufferWriter<byte> bufferWriter, out WriteBufferHelper instance, out Span<byte> buffer)
+        {
+            instance.bufferWriter = bufferWriter;
+            instance.codedOutputStream = null;
+            buffer = default;  // TODO: initialize the initial buffer so that the first write is not via slowpath.
+        }
+
+        /// <summary>
+        /// Initialize an instance with a buffer represented by a single span (i.e. buffer cannot be refreshed)
+        /// This approach is faster than using a constructor because the instance to initialize is passed by reference
+        /// and we can write directly into it without copying.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void InitializeNonRefreshable(out WriteBufferHelper instance)
+        {
+            instance.bufferWriter = null;
+            instance.codedOutputStream = null;
+        }
+
+        /// <summary>
+        /// Verifies that SpaceLeft returns zero.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void CheckNoSpaceLeft(ref WriterInternalState state)
+        {
+            if (GetSpaceLeft(ref state) != 0)
+            {
+                throw new InvalidOperationException("Did not write as much data as expected.");
+            }
+        }
+
+        /// <summary>
+        /// If writing to a flat array, returns the space left in the array. Otherwise,
+        /// throws an InvalidOperationException.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static int GetSpaceLeft(ref WriterInternalState state)
+        {
+            if (state.writeBufferHelper.codedOutputStream?.InternalOutputStream == null && state.writeBufferHelper.bufferWriter == null)
+            {
+                return state.limit - state.position;
+            }
+            else
+            {
+                throw new InvalidOperationException(
+                    "SpaceLeft can only be called on CodedOutputStreams that are " +
+                        "writing to a flat array or when writing to a single span.");
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static void RefreshBuffer(ref Span<byte> buffer, ref WriterInternalState state)
+        {
+            if (state.writeBufferHelper.codedOutputStream?.InternalOutputStream != null)
+            {
+                // because we're using coded output stream, we know that "buffer" and codedOutputStream.InternalBuffer are identical.
+                state.writeBufferHelper.codedOutputStream.InternalOutputStream.Write(state.writeBufferHelper.codedOutputStream.InternalBuffer, 0, state.position);
+                // reset position, limit stays the same because we are reusing the codedOutputStream's internal buffer.
+                state.position = 0;
+            }
+            else if (state.writeBufferHelper.bufferWriter != null)
+            {
+                // commit the bytes and get a new buffer to write to.
+                state.writeBufferHelper.bufferWriter.Advance(state.position);
+                state.position = 0;
+                buffer = state.writeBufferHelper.bufferWriter.GetSpan();
+                state.limit = buffer.Length;
+            }
+            else
+            {
+                // We're writing to a single buffer.
+                throw new CodedOutputStream.OutOfSpaceException();
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void Flush(ref Span<byte> buffer, ref WriterInternalState state)
+        {
+            if (state.writeBufferHelper.codedOutputStream?.InternalOutputStream != null)
+            {
+                // because we're using coded output stream, we know that "buffer" and codedOutputStream.InternalBuffer are identical.
+                state.writeBufferHelper.codedOutputStream.InternalOutputStream.Write(state.writeBufferHelper.codedOutputStream.InternalBuffer, 0, state.position);
+                state.position = 0;
+            }
+            else if (state.writeBufferHelper.bufferWriter != null)
+            {
+                // calling Advance invalidates the current buffer and we must not continue writing to it,
+                // so we set the current buffer to point to an empty Span. If any subsequent writes happen,
+                // the first subsequent write will trigger refresing of the buffer.
+                state.writeBufferHelper.bufferWriter.Advance(state.position);
+                state.position = 0;
+                state.limit = 0;
+                buffer = default;  // invalidate the current buffer
+            }
+        }
+    }
+}

+ 371 - 0
csharp/src/Google.Protobuf/WriteContext.cs

@@ -0,0 +1,371 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Buffers;
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Text;
+using Google.Protobuf.Collections;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// An opaque struct that represents the current serialization state and is passed along
+    /// as the serialization proceeds.
+    /// All the public methods are intended to be invoked only by the generated code,
+    /// users should never invoke them directly.
+    /// </summary>
+    [SecuritySafeCritical]
+    public ref struct WriteContext
+    {
+        internal Span<byte> buffer;
+        internal WriterInternalState state;
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static void Initialize(ref Span<byte> buffer, ref WriterInternalState state, out WriteContext ctx)
+        {
+            ctx.buffer = buffer;
+            ctx.state = state;
+        }
+
+        /// <summary>
+        /// Creates a WriteContext instance from CodedOutputStream.
+        /// WARNING: internally this copies the CodedOutputStream's state, so after done with the WriteContext,
+        /// the CodedOutputStream's state needs to be updated.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static void Initialize(CodedOutputStream output, out WriteContext ctx)
+        {
+            ctx.buffer = new Span<byte>(output.InternalBuffer);
+            // ideally we would use a reference to the original state, but that doesn't seem possible
+            // so we just copy the struct that holds the state. We will need to later store the state back
+            // into CodedOutputStream if we want to keep it usable.
+            ctx.state = output.InternalState;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static void Initialize(IBufferWriter<byte> output, out WriteContext ctx)
+        {
+            ctx.buffer = default;
+            ctx.state = default;
+            WriteBufferHelper.Initialize(output, out ctx.state.writeBufferHelper, out ctx.buffer);
+            ctx.state.limit = ctx.buffer.Length;
+            ctx.state.position = 0;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static void Initialize(ref Span<byte> buffer, out WriteContext ctx)
+        {
+            ctx.buffer = buffer;
+            ctx.state = default;
+            ctx.state.limit = ctx.buffer.Length;
+            ctx.state.position = 0;
+            WriteBufferHelper.InitializeNonRefreshable(out ctx.state.writeBufferHelper);
+        }
+
+        /// <summary>
+        /// Writes a double field value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteDouble(double value)
+        {
+            WritingPrimitives.WriteDouble(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes a float field value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteFloat(float value)
+        {
+            WritingPrimitives.WriteFloat(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes a uint64 field value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteUInt64(ulong value)
+        {
+            WritingPrimitives.WriteUInt64(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes an int64 field value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteInt64(long value)
+        {
+            WritingPrimitives.WriteInt64(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes an int32 field value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteInt32(int value)
+        {
+            WritingPrimitives.WriteInt32(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes a fixed64 field value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteFixed64(ulong value)
+        {
+            WritingPrimitives.WriteFixed64(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes a fixed32 field value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteFixed32(uint value)
+        {
+            WritingPrimitives.WriteFixed32(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes a bool field value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteBool(bool value)
+        {
+            WritingPrimitives.WriteBool(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes a string field value, without a tag.
+        /// The data is length-prefixed.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteString(string value)
+        {
+            WritingPrimitives.WriteString(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes a message, without a tag.
+        /// The data is length-prefixed.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteMessage(IMessage value)
+        {
+            WritingPrimitivesMessages.WriteMessage(ref this, value);
+        }
+
+        /// <summary>
+        /// Writes a group, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteGroup(IMessage value)
+        {
+            WritingPrimitivesMessages.WriteGroup(ref this, value);
+        }
+
+        /// <summary>
+        /// Write a byte string, without a tag, to the stream.
+        /// The data is length-prefixed.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteBytes(ByteString value)
+        {
+            WritingPrimitives.WriteBytes(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes a uint32 value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteUInt32(uint value)
+        {
+            WritingPrimitives.WriteUInt32(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes an enum value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteEnum(int value)
+        {
+            WritingPrimitives.WriteEnum(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes an sfixed32 value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write.</param>
+        public void WriteSFixed32(int value)
+        {
+            WritingPrimitives.WriteSFixed32(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes an sfixed64 value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteSFixed64(long value)
+        {
+            WritingPrimitives.WriteSFixed64(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes an sint32 value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteSInt32(int value)
+        {
+            WritingPrimitives.WriteSInt32(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes an sint64 value, without a tag.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteSInt64(long value)
+        {
+            WritingPrimitives.WriteSInt64(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes a length (in bytes) for length-delimited data.
+        /// </summary>
+        /// <remarks>
+        /// This method simply writes a rawint, but exists for clarity in calling code.
+        /// </remarks>
+        /// <param name="length">Length value, in bytes.</param>
+        public void WriteLength(int length)
+        {
+            WritingPrimitives.WriteLength(ref buffer, ref state, length);
+        }
+
+        /// <summary>
+        /// Encodes and writes a tag.
+        /// </summary>
+        /// <param name="fieldNumber">The number of the field to write the tag for</param>
+        /// <param name="type">The wire format type of the tag to write</param>
+        public void WriteTag(int fieldNumber, WireFormat.WireType type)
+        {
+            WritingPrimitives.WriteTag(ref buffer, ref state, fieldNumber, type);
+        }
+
+        /// <summary>
+        /// Writes an already-encoded tag.
+        /// </summary>
+        /// <param name="tag">The encoded tag</param>
+        public void WriteTag(uint tag)
+        {
+            WritingPrimitives.WriteTag(ref buffer, ref state, tag);
+        }
+
+        /// <summary>
+        /// Writes the given single-byte tag.
+        /// </summary>
+        /// <param name="b1">The encoded tag</param>
+        public void WriteRawTag(byte b1)
+        {
+            WritingPrimitives.WriteRawTag(ref buffer, ref state, b1);
+        }
+
+        /// <summary>
+        /// Writes the given two-byte tag.
+        /// </summary>
+        /// <param name="b1">The first byte of the encoded tag</param>
+        /// <param name="b2">The second byte of the encoded tag</param>
+        public void WriteRawTag(byte b1, byte b2)
+        {
+            WritingPrimitives.WriteRawTag(ref buffer, ref state, b1, b2);
+        }
+
+        /// <summary>
+        /// Writes the given three-byte tag.
+        /// </summary>
+        /// <param name="b1">The first byte of the encoded tag</param>
+        /// <param name="b2">The second byte of the encoded tag</param>
+        /// <param name="b3">The third byte of the encoded tag</param>
+        public void WriteRawTag(byte b1, byte b2, byte b3)
+        {
+            WritingPrimitives.WriteRawTag(ref buffer, ref state, b1, b2, b3);
+        }
+
+        /// <summary>
+        /// Writes the given four-byte tag.
+        /// </summary>
+        /// <param name="b1">The first byte of the encoded tag</param>
+        /// <param name="b2">The second byte of the encoded tag</param>
+        /// <param name="b3">The third byte of the encoded tag</param>
+        /// <param name="b4">The fourth byte of the encoded tag</param>
+        public void WriteRawTag(byte b1, byte b2, byte b3, byte b4)
+        {
+            WritingPrimitives.WriteRawTag(ref buffer, ref state, b1, b2, b3, b4);
+        }
+
+        /// <summary>
+        /// Writes the given five-byte tag.
+        /// </summary>
+        /// <param name="b1">The first byte of the encoded tag</param>
+        /// <param name="b2">The second byte of the encoded tag</param>
+        /// <param name="b3">The third byte of the encoded tag</param>
+        /// <param name="b4">The fourth byte of the encoded tag</param>
+        /// <param name="b5">The fifth byte of the encoded tag</param>
+        public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)
+        {
+            WritingPrimitives.WriteRawTag(ref buffer, ref state, b1, b2, b3, b4, b5);
+        }
+
+        internal void Flush()
+        {
+            WriteBufferHelper.Flush(ref buffer, ref state);
+        }
+
+        internal void CheckNoSpaceLeft()
+        {
+            WriteBufferHelper.CheckNoSpaceLeft(ref state);
+        }
+
+        internal void CopyStateTo(CodedOutputStream output)
+        {
+            output.InternalState = state;
+        }
+
+        internal void LoadStateFrom(CodedOutputStream output)
+        {
+            state = output.InternalState;
+        }
+    }
+}

+ 62 - 0
csharp/src/Google.Protobuf/WriterInternalState.cs

@@ -0,0 +1,62 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Buffers;
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Text;
+using Google.Protobuf.Collections;
+
+namespace Google.Protobuf
+{
+    
+    // warning: this is a mutable struct, so it needs to be only passed as a ref!
+    internal struct WriterInternalState
+    {
+        // NOTE: the Span representing the current buffer is kept separate so that this doesn't have to be a ref struct and so it can
+        // be included in CodedOutputStream's internal state
+
+        internal int limit;  // the size of the current buffer
+        internal int position;  // position in the current buffer
+
+        internal WriteBufferHelper writeBufferHelper;
+
+        // If non-null, the top level parse method was started with given coded output stream as an argument
+        // which also means we can potentially fallback to calling WriteTo(CodedOutputStream cos) if needed.
+        internal CodedOutputStream CodedOutputStream => writeBufferHelper.CodedOutputStream;
+    }
+}

+ 638 - 0
csharp/src/Google.Protobuf/WritingPrimitives.cs

@@ -0,0 +1,638 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Buffers.Binary;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Text;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Primitives for encoding protobuf wire format.
+    /// </summary>
+    [SecuritySafeCritical]
+    internal static class WritingPrimitives
+    {
+        // "Local" copy of Encoding.UTF8, for efficiency. (Yes, it makes a difference.)
+        internal static readonly Encoding Utf8Encoding = Encoding.UTF8;
+
+        #region Writing of values (not including tags)
+
+        /// <summary>
+        /// Writes a double field value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteDouble(ref Span<byte> buffer, ref WriterInternalState state, double value)
+        {
+            WriteRawLittleEndian64(ref buffer, ref state, (ulong)BitConverter.DoubleToInt64Bits(value));
+        }
+
+        /// <summary>
+        /// Writes a float field value, without a tag, to the stream.
+        /// </summary>
+        public static unsafe void WriteFloat(ref Span<byte> buffer, ref WriterInternalState state, float value)
+        {
+            const int length = sizeof(float);
+            if (buffer.Length - state.position >= length)
+            {
+                // if there's enough space in the buffer, write the float directly into the buffer
+                var floatSpan = buffer.Slice(state.position, length);
+                Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(floatSpan), value);
+
+                if (!BitConverter.IsLittleEndian)
+                {
+                    floatSpan.Reverse();
+                }
+                state.position += length;
+            }
+            else
+            {
+                WriteFloatSlowPath(ref buffer, ref state, value);
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static unsafe void WriteFloatSlowPath(ref Span<byte> buffer, ref WriterInternalState state, float value)
+        {
+            const int length = sizeof(float);
+
+            // TODO(jtattermusch): deduplicate the code. Populating the span is the same as for the fastpath.
+            Span<byte> floatSpan = stackalloc byte[length];
+            Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(floatSpan), value);
+            if (!BitConverter.IsLittleEndian)
+            {
+                floatSpan.Reverse();
+            }
+
+            WriteRawByte(ref buffer, ref state, floatSpan[0]);
+            WriteRawByte(ref buffer, ref state, floatSpan[1]);
+            WriteRawByte(ref buffer, ref state, floatSpan[2]);
+            WriteRawByte(ref buffer, ref state, floatSpan[3]);
+        }
+
+        /// <summary>
+        /// Writes a uint64 field value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteUInt64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)
+        {
+            WriteRawVarint64(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes an int64 field value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteInt64(ref Span<byte> buffer, ref WriterInternalState state, long value)
+        {
+            WriteRawVarint64(ref buffer, ref state, (ulong)value);
+        }
+
+        /// <summary>
+        /// Writes an int32 field value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteInt32(ref Span<byte> buffer, ref WriterInternalState state, int value)
+        {
+            if (value >= 0)
+            {
+                WriteRawVarint32(ref buffer, ref state, (uint)value);
+            }
+            else
+            {
+                // Must sign-extend.
+                WriteRawVarint64(ref buffer, ref state, (ulong)value);
+            }
+        }
+
+        /// <summary>
+        /// Writes a fixed64 field value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteFixed64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)
+        {
+            WriteRawLittleEndian64(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes a fixed32 field value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteFixed32(ref Span<byte> buffer, ref WriterInternalState state, uint value)
+        {
+            WriteRawLittleEndian32(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes a bool field value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteBool(ref Span<byte> buffer, ref WriterInternalState state, bool value)
+        {
+            WriteRawByte(ref buffer, ref state, value ? (byte)1 : (byte)0);
+        }
+
+        /// <summary>
+        /// Writes a string field value, without a tag, to the stream.
+        /// The data is length-prefixed.
+        /// </summary>
+        public static void WriteString(ref Span<byte> buffer, ref WriterInternalState state, string value)
+        {
+            // Optimise the case where we have enough space to write
+            // the string directly to the buffer, which should be common.
+            int length = Utf8Encoding.GetByteCount(value);
+            WriteLength(ref buffer, ref state, length);
+            if (buffer.Length - state.position >= length)
+            {
+                if (length == value.Length) // Must be all ASCII...
+                {
+                    for (int i = 0; i < length; i++)
+                    {
+                        buffer[state.position + i] = (byte)value[i];
+                    }
+                    state.position += length;
+                }
+                else
+                {
+#if NETSTANDARD1_1
+                    // slowpath when Encoding.GetBytes(Char*, Int32, Byte*, Int32) is not available
+                    byte[] bytes = Utf8Encoding.GetBytes(value);
+                    WriteRawBytes(ref buffer, ref state, bytes);
+#else
+                    ReadOnlySpan<char> source = value.AsSpan();
+                    int bytesUsed;
+                    unsafe
+                    {
+                        fixed (char* sourceChars = &MemoryMarshal.GetReference(source))
+                        fixed (byte* destinationBytes = &MemoryMarshal.GetReference(buffer.Slice(state.position)))
+                        {
+                            bytesUsed = Utf8Encoding.GetBytes(sourceChars, source.Length, destinationBytes, buffer.Length);
+                        }
+                    }
+                    state.position += bytesUsed;
+#endif
+                }
+            }
+            else
+            {
+                // Opportunity for future optimization:
+                // Large strings that don't fit into the current buffer segment
+                // can probably be optimized by using Utf8Encoding.GetEncoder()
+                // but more benchmarks would need to be added as evidence.
+                byte[] bytes = Utf8Encoding.GetBytes(value);
+                WriteRawBytes(ref buffer, ref state, bytes);
+            }
+        }
+
+        /// <summary>
+        /// Write a byte string, without a tag, to the stream.
+        /// The data is length-prefixed.
+        /// </summary>
+        public static void WriteBytes(ref Span<byte> buffer, ref WriterInternalState state, ByteString value)
+        {
+            WriteLength(ref buffer, ref state, value.Length);
+            WriteRawBytes(ref buffer, ref state, value.Span);
+        }
+
+        /// <summary>
+        /// Writes a uint32 value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteUInt32(ref Span<byte> buffer, ref WriterInternalState state, uint value)
+        {
+            WriteRawVarint32(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes an enum value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteEnum(ref Span<byte> buffer, ref WriterInternalState state, int value)
+        {
+            WriteInt32(ref buffer, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes an sfixed32 value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteSFixed32(ref Span<byte> buffer, ref WriterInternalState state, int value)
+        {
+            WriteRawLittleEndian32(ref buffer, ref state, (uint)value);
+        }
+
+        /// <summary>
+        /// Writes an sfixed64 value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteSFixed64(ref Span<byte> buffer, ref WriterInternalState state, long value)
+        {
+            WriteRawLittleEndian64(ref buffer, ref state, (ulong)value);
+        }
+
+        /// <summary>
+        /// Writes an sint32 value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteSInt32(ref Span<byte> buffer, ref WriterInternalState state, int value)
+        {
+            WriteRawVarint32(ref buffer, ref state, EncodeZigZag32(value));
+        }
+
+        /// <summary>
+        /// Writes an sint64 value, without a tag, to the stream.
+        /// </summary>
+        public static void WriteSInt64(ref Span<byte> buffer, ref WriterInternalState state, long value)
+        {
+            WriteRawVarint64(ref buffer, ref state, EncodeZigZag64(value));
+        }
+
+        /// <summary>
+        /// Writes a length (in bytes) for length-delimited data.
+        /// </summary>
+        /// <remarks>
+        /// This method simply writes a rawint, but exists for clarity in calling code.
+        /// </remarks>
+        public static void WriteLength(ref Span<byte> buffer, ref WriterInternalState state, int length)
+        {
+            WriteRawVarint32(ref buffer, ref state, (uint)length);
+        }
+
+        #endregion
+
+        #region Writing primitives
+        /// <summary>
+        /// Writes a 32 bit value as a varint. The fast route is taken when
+        /// there's enough buffer space left to whizz through without checking
+        /// for each byte; otherwise, we resort to calling WriteRawByte each time.
+        /// </summary>
+        public static void WriteRawVarint32(ref Span<byte> buffer, ref WriterInternalState state, uint value)
+        {
+            // Optimize for the common case of a single byte value
+            if (value < 128 && state.position < buffer.Length)
+            {
+                buffer[state.position++] = (byte)value;
+                return;
+            }
+
+            // Fast path when capacity is available
+            while (state.position < buffer.Length)
+            {
+                if (value > 127)
+                {
+                    buffer[state.position++] = (byte)((value & 0x7F) | 0x80);
+                    value >>= 7;
+                }
+                else
+                {
+                    buffer[state.position++] = (byte)value;
+                    return;
+                }
+            }
+
+            while (value > 127)
+            {
+                WriteRawByte(ref buffer, ref state, (byte)((value & 0x7F) | 0x80));
+                value >>= 7;
+            }
+
+            WriteRawByte(ref buffer, ref state, (byte)value);
+        }
+
+        public static void WriteRawVarint64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)
+        {
+            // Optimize for the common case of a single byte value
+            if (value < 128 && state.position < buffer.Length)
+            {
+                buffer[state.position++] = (byte)value;
+                return;
+            }
+
+            // Fast path when capacity is available
+            while (state.position < buffer.Length)
+            {
+                if (value > 127)
+                {
+                    buffer[state.position++] = (byte)((value & 0x7F) | 0x80);
+                    value >>= 7;
+                }
+                else
+                {
+                    buffer[state.position++] = (byte)value;
+                    return;
+                }
+            }
+
+            while (value > 127)
+            {
+                WriteRawByte(ref buffer, ref state, (byte)((value & 0x7F) | 0x80));
+                value >>= 7;
+            }
+
+            WriteRawByte(ref buffer, ref state, (byte)value);
+        }
+
+        public static void WriteRawLittleEndian32(ref Span<byte> buffer, ref WriterInternalState state, uint value)
+        {
+            const int length = sizeof(uint);
+            if (state.position + length > buffer.Length)
+            {
+                WriteRawLittleEndian32SlowPath(ref buffer, ref state, value);
+            }
+            else
+            {
+                BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(state.position), value);
+                state.position += length;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static void WriteRawLittleEndian32SlowPath(ref Span<byte> buffer, ref WriterInternalState state, uint value)
+        {
+            WriteRawByte(ref buffer, ref state, (byte)value);
+            WriteRawByte(ref buffer, ref state, (byte)(value >> 8));
+            WriteRawByte(ref buffer, ref state, (byte)(value >> 16));
+            WriteRawByte(ref buffer, ref state, (byte)(value >> 24));
+        }
+
+        public static void WriteRawLittleEndian64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)
+        {
+            const int length = sizeof(ulong);
+            if (state.position + length > buffer.Length)
+            {
+                WriteRawLittleEndian64SlowPath(ref buffer, ref state, value);
+            }
+            else
+            {
+                // TODO(jtattermusch): According to the benchmarks, writing byte-by-byte is actually faster
+                // than using BinaryPrimitives.WriteUInt64LittleEndian.
+                // This is strange especially because WriteUInt32LittleEndian seems to be much faster
+                // in terms of throughput.
+                buffer[state.position++] = ((byte)value);
+                buffer[state.position++] = ((byte)(value >> 8));
+                buffer[state.position++] = ((byte)(value >> 16));
+                buffer[state.position++] = ((byte)(value >> 24));
+                buffer[state.position++] = ((byte)(value >> 32));
+                buffer[state.position++] = ((byte)(value >> 40));
+                buffer[state.position++] = ((byte)(value >> 48));
+                buffer[state.position++] = ((byte)(value >> 56));
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static void WriteRawLittleEndian64SlowPath(ref Span<byte> buffer, ref WriterInternalState state, ulong value)
+        {
+            WriteRawByte(ref buffer, ref state, (byte)value);
+            WriteRawByte(ref buffer, ref state, (byte)(value >> 8));
+            WriteRawByte(ref buffer, ref state, (byte)(value >> 16));
+            WriteRawByte(ref buffer, ref state, (byte)(value >> 24));
+            WriteRawByte(ref buffer, ref state, (byte)(value >> 32));
+            WriteRawByte(ref buffer, ref state, (byte)(value >> 40));
+            WriteRawByte(ref buffer, ref state, (byte)(value >> 48));
+            WriteRawByte(ref buffer, ref state, (byte)(value >> 56));
+        }
+
+        private static void WriteRawByte(ref Span<byte> buffer, ref WriterInternalState state, byte value)
+        {
+            if (state.position == buffer.Length)
+            {
+                WriteBufferHelper.RefreshBuffer(ref buffer, ref state);
+            }
+
+            buffer[state.position++] = value;
+        }
+
+        /// <summary>
+        /// Writes out an array of bytes.
+        /// </summary>
+        public static void WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, byte[] value)
+        {
+            WriteRawBytes(ref buffer, ref state, new ReadOnlySpan<byte>(value));
+        }
+
+        /// <summary>
+        /// Writes out part of an array of bytes.
+        /// </summary>
+        public static void WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, byte[] value, int offset, int length)
+        {
+            WriteRawBytes(ref buffer, ref state, new ReadOnlySpan<byte>(value, offset, length));
+        }
+
+        /// <summary>
+        /// Writes out part of an array of bytes.
+        /// </summary>
+        public static void WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, ReadOnlySpan<byte> value)
+        {
+            if (buffer.Length - state.position >= value.Length)
+            {
+                // We have room in the current buffer.    
+                value.CopyTo(buffer.Slice(state.position, value.Length));
+                state.position += value.Length;
+            }
+            else
+            {
+                // When writing to a CodedOutputStream backed by a Stream, we could avoid
+                // copying the data twice (first copying to the current buffer and
+                // and later writing from the current buffer to the underlying Stream)
+                // in some circumstances by writing the data directly to the underlying Stream.
+                // Current this is not being done to avoid specialcasing the code for
+                // CodedOutputStream vs IBufferWriter<byte>.
+                int bytesWritten = 0;
+                while (buffer.Length - state.position < value.Length - bytesWritten)
+                {
+                    int length = buffer.Length - state.position;
+                    value.Slice(bytesWritten, length).CopyTo(buffer.Slice(state.position, length));
+                    bytesWritten += length;
+                    state.position += length;
+                    WriteBufferHelper.RefreshBuffer(ref buffer, ref state);
+                }
+
+                // copy the remaining data
+                int remainderLength = value.Length - bytesWritten;
+                value.Slice(bytesWritten, remainderLength).CopyTo(buffer.Slice(state.position, remainderLength));
+                state.position += remainderLength;
+            }
+        }
+        #endregion
+
+        #region Raw tag writing
+        /// <summary>
+        /// Encodes and writes a tag.
+        /// </summary>
+        public static void WriteTag(ref Span<byte> buffer, ref WriterInternalState state, int fieldNumber, WireFormat.WireType type)
+        {
+            WriteRawVarint32(ref buffer, ref state, WireFormat.MakeTag(fieldNumber, type));
+        }
+
+        /// <summary>
+        /// Writes an already-encoded tag.
+        /// </summary>
+        public static void WriteTag(ref Span<byte> buffer, ref WriterInternalState state, uint tag)
+        {
+            WriteRawVarint32(ref buffer, ref state, tag);
+        }
+
+        /// <summary>
+        /// Writes the given single-byte tag directly to the stream.
+        /// </summary>
+        public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1)
+        {
+            WriteRawByte(ref buffer, ref state, b1);
+        }
+
+        /// <summary>
+        /// Writes the given two-byte tag directly to the stream.
+        /// </summary>
+        public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2)
+        {
+            if (state.position + 2 > buffer.Length)
+            {
+                WriteRawTagSlowPath(ref buffer, ref state, b1, b2);
+            }
+            else
+            {
+                buffer[state.position++] = b1;
+                buffer[state.position++] = b2;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static void WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2)
+        {
+            WriteRawByte(ref buffer, ref state, b1);
+            WriteRawByte(ref buffer, ref state, b2);
+        }
+
+        /// <summary>
+        /// Writes the given three-byte tag directly to the stream.
+        /// </summary>
+        public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3)
+        {
+            if (state.position + 3 > buffer.Length)
+            {
+                WriteRawTagSlowPath(ref buffer, ref state, b1, b2, b3);
+            }
+            else
+            {
+                buffer[state.position++] = b1;
+                buffer[state.position++] = b2;
+                buffer[state.position++] = b3;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static void WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3)
+        {
+            WriteRawByte(ref buffer, ref state, b1);
+            WriteRawByte(ref buffer, ref state, b2);
+            WriteRawByte(ref buffer, ref state, b3);
+        }
+
+        /// <summary>
+        /// Writes the given four-byte tag directly to the stream.
+        /// </summary>
+        public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4)
+        {
+            if (state.position + 4 > buffer.Length)
+            {
+                WriteRawTagSlowPath(ref buffer, ref state, b1, b2, b3, b4);
+            }
+            else
+            {
+                buffer[state.position++] = b1;
+                buffer[state.position++] = b2;
+                buffer[state.position++] = b3;
+                buffer[state.position++] = b4;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+
+        private static void WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4)
+        {
+            WriteRawByte(ref buffer, ref state, b1);
+            WriteRawByte(ref buffer, ref state, b2);
+            WriteRawByte(ref buffer, ref state, b3);
+            WriteRawByte(ref buffer, ref state, b4);
+        }
+
+        /// <summary>
+        /// Writes the given five-byte tag directly to the stream.
+        /// </summary>
+        public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4, byte b5)
+        {
+            if (state.position + 5 > buffer.Length)
+            {
+                WriteRawTagSlowPath(ref buffer, ref state, b1, b2, b3, b4, b5);
+            }
+            else
+            {
+                buffer[state.position++] = b1;
+                buffer[state.position++] = b2;
+                buffer[state.position++] = b3;
+                buffer[state.position++] = b4;
+                buffer[state.position++] = b5;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static void WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4, byte b5)
+        {
+            WriteRawByte(ref buffer, ref state, b1);
+            WriteRawByte(ref buffer, ref state, b2);
+            WriteRawByte(ref buffer, ref state, b3);
+            WriteRawByte(ref buffer, ref state, b4);
+            WriteRawByte(ref buffer, ref state, b5);
+        }
+        #endregion
+
+        /// <summary>
+        /// Encode a 32-bit value with ZigZag encoding.
+        /// </summary>
+        /// <remarks>
+        /// ZigZag encodes signed integers into values that can be efficiently
+        /// encoded with varint.  (Otherwise, negative values must be 
+        /// sign-extended to 64 bits to be varint encoded, thus always taking
+        /// 10 bytes on the wire.)
+        /// </remarks>
+        public static uint EncodeZigZag32(int n)
+        {
+            // Note:  the right-shift must be arithmetic
+            return (uint)((n << 1) ^ (n >> 31));
+        }
+
+        /// <summary>
+        /// Encode a 64-bit value with ZigZag encoding.
+        /// </summary>
+        /// <remarks>
+        /// ZigZag encodes signed integers into values that can be efficiently
+        /// encoded with varint.  (Otherwise, negative values must be 
+        /// sign-extended to 64 bits to be varint encoded, thus always taking
+        /// 10 bytes on the wire.)
+        /// </remarks>
+        public static ulong EncodeZigZag64(long n)
+        {
+            return (ulong)((n << 1) ^ (n >> 63));
+        }
+    }
+}

+ 112 - 0
csharp/src/Google.Protobuf/WritingPrimitivesMessages.cs

@@ -0,0 +1,112 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices.ComTypes;
+using System.Security;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Writing messages / groups.
+    /// </summary>
+    [SecuritySafeCritical]
+    internal static class WritingPrimitivesMessages
+    {
+        /// <summary>
+        /// Writes a message, without a tag.
+        /// The data is length-prefixed.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void WriteMessage(ref WriteContext ctx, IMessage value)
+        {
+            WritingPrimitives.WriteLength(ref ctx.buffer, ref ctx.state, value.CalculateSize());
+            WriteRawMessage(ref ctx, value);
+        }
+
+        /// <summary>
+        /// Writes a group, without a tag.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void WriteGroup(ref WriteContext ctx, IMessage value)
+        {
+            WriteRawMessage(ref ctx, value);
+        }
+
+        /// <summary>
+        /// Writes a message, without a tag.
+        /// Message will be written without a length prefix.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void WriteRawMessage(ref WriteContext ctx, IMessage message)
+        {
+            if (message is IBufferMessage bufferMessage)
+            {
+                bufferMessage.InternalWriteTo(ref ctx);
+            }
+            else
+            {
+                // If we reached here, it means we've ran into a nested message with older generated code
+                // which doesn't provide the InternalWriteTo method that takes a WriteContext.
+                // With a slight performance overhead, we can still serialize this message just fine,
+                // but we need to find the original CodedOutputStream instance that initiated this
+                // serialization process and make sure its internal state is up to date.
+                // Note that this performance overhead is not very high (basically copying contents of a struct)
+                // and it will only be incurred in case the application mixes older and newer generated code.
+                // Regenerating the code from .proto files will remove this overhead because it will
+                // generate the InternalWriteTo method we need.
+
+                if (ctx.state.CodedOutputStream == null)
+                {
+                    // This can only happen when the serialization started without providing a CodedOutputStream instance
+                    // (e.g. WriteContext was created directly from a IBufferWriter).
+                    // That also means that one of the new parsing APIs was used at the top level
+                    // and in such case it is reasonable to require that all the nested message provide
+                    // up-to-date generated code with WriteContext support (and fail otherwise).
+                    throw new InvalidProtocolBufferException($"Message {message.GetType().Name} doesn't provide the generated method that enables WriteContext-based serialization. You might need to regenerate the generated protobuf code.");
+                }
+
+                ctx.CopyStateTo(ctx.state.CodedOutputStream);
+                try
+                {
+                    // fallback parse using the CodedOutputStream that started current serialization tree
+                    message.WriteTo(ctx.state.CodedOutputStream);
+                }
+                finally
+                {
+                    ctx.LoadStateFrom(ctx.state.CodedOutputStream);
+                }
+            }
+        }
+    }
+}

+ 6 - 0
src/google/protobuf/compiler/csharp/csharp_field_base.cc

@@ -167,6 +167,12 @@ void FieldGeneratorBase::GenerateParsingCode(io::Printer* printer, bool use_pars
   GenerateParsingCode(printer);
 }
 
+void FieldGeneratorBase::GenerateSerializationCode(io::Printer* printer, bool use_write_context) {
+  // for some field types the value of "use_write_context" doesn't matter,
+  // so we fallback to the default implementation.
+  GenerateSerializationCode(printer);
+}
+
 void FieldGeneratorBase::AddDeprecatedFlag(io::Printer* printer) {
   if (descriptor_->options().deprecated()) {
     printer->Print("[global::System.ObsoleteAttribute]\n");

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_field_base.h

@@ -63,6 +63,7 @@ class FieldGeneratorBase : public SourceGeneratorBase {
   virtual void GenerateParsingCode(io::Printer* printer) = 0;
   virtual void GenerateParsingCode(io::Printer* printer, bool use_parse_context);
   virtual void GenerateSerializationCode(io::Printer* printer) = 0;
+  virtual void GenerateSerializationCode(io::Printer* printer, bool use_write_context);
   virtual void GenerateSerializedSizeCode(io::Printer* printer) = 0;
 
   virtual void WriteHash(io::Printer* printer) = 0;

+ 7 - 1
src/google/protobuf/compiler/csharp/csharp_map_field.cc

@@ -106,9 +106,15 @@ void MapFieldGenerator::GenerateParsingCode(io::Printer* printer, bool use_parse
 }
 
 void MapFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
+  GenerateSerializationCode(printer, true);
+}
+
+void MapFieldGenerator::GenerateSerializationCode(io::Printer* printer, bool use_write_context) {
   printer->Print(
     variables_,
-    "$name$_.WriteTo(output, _map_$name$_codec);\n");
+    use_write_context
+    ? "$name$_.WriteTo(ref output, _map_$name$_codec);\n"
+    : "$name$_.WriteTo(output, _map_$name$_codec);\n");
 }
 
 void MapFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_map_field.h

@@ -58,6 +58,7 @@ class MapFieldGenerator : public FieldGeneratorBase {
   virtual void GenerateParsingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer, bool use_parse_context);
   virtual void GenerateSerializationCode(io::Printer* printer);
+  virtual void GenerateSerializationCode(io::Printer* printer, bool use_write_context);
   virtual void GenerateSerializedSizeCode(io::Printer* printer);
 
   virtual void WriteHash(io::Printer* printer);

+ 50 - 25
src/google/protobuf/compiler/csharp/csharp_message.cc

@@ -520,34 +520,26 @@ void MessageGenerator::GenerateMessageSerializationMethods(io::Printer* printer)
   WriteGeneratedCodeAttributes(printer);
   printer->Print(
       "public void WriteTo(pb::CodedOutputStream output) {\n");
+  printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
   printer->Indent();
+  printer->Print("output.WriteRawMessage(this);\n");
+  printer->Outdent();
+  printer->Print("#else\n");
+  printer->Indent();
+  GenerateWriteToBody(printer, false);
+  printer->Outdent();
+  printer->Print("#endif\n");
+  printer->Print("}\n\n");
 
-  // Serialize all the fields
-  for (int i = 0; i < fields_by_number().size(); i++) {
-    std::unique_ptr<FieldGeneratorBase> generator(
-      CreateFieldGeneratorInternal(fields_by_number()[i]));
-    generator->GenerateSerializationCode(printer);
-  }
-
-  if (has_extension_ranges_) {
-    // Serialize extensions
-    printer->Print(
-      "if (_extensions != null) {\n"
-      "  _extensions.WriteTo(output);\n"
-      "}\n");
-  }
-
-  // Serialize unknown fields
-  printer->Print(
-    "if (_unknownFields != null) {\n"
-    "  _unknownFields.WriteTo(output);\n"
-    "}\n");
-
-  // TODO(jonskeet): Memoize size of frozen messages?
+  printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
+  WriteGeneratedCodeAttributes(printer);
+  printer->Print("void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {\n");
+  printer->Indent();
+  GenerateWriteToBody(printer, true);
   printer->Outdent();
-  printer->Print(
-    "}\n"
-    "\n");
+  printer->Print("}\n");
+  printer->Print("#endif\n\n");
+
   WriteGeneratedCodeAttributes(printer);
   printer->Print(
     "public int CalculateSize() {\n");
@@ -576,6 +568,39 @@ void MessageGenerator::GenerateMessageSerializationMethods(io::Printer* printer)
   printer->Print("}\n\n");
 }
 
+void MessageGenerator::GenerateWriteToBody(io::Printer* printer, bool use_write_context) {
+  // Serialize all the fields
+  for (int i = 0; i < fields_by_number().size(); i++) {
+    std::unique_ptr<FieldGeneratorBase> generator(
+      CreateFieldGeneratorInternal(fields_by_number()[i]));
+    generator->GenerateSerializationCode(printer, use_write_context);
+  }
+
+  if (has_extension_ranges_) {
+    // Serialize extensions
+    printer->Print(
+      use_write_context
+      ? "if (_extensions != null) {\n"
+        "  _extensions.WriteTo(ref output);\n"
+        "}\n"
+      : "if (_extensions != null) {\n"
+        "  _extensions.WriteTo(output);\n"
+        "}\n");
+  }
+
+  // Serialize unknown fields
+  printer->Print(
+    use_write_context
+    ? "if (_unknownFields != null) {\n"
+      "  _unknownFields.WriteTo(ref output);\n"
+      "}\n"
+    : "if (_unknownFields != null) {\n"
+      "  _unknownFields.WriteTo(output);\n"
+      "}\n");
+
+  // TODO(jonskeet): Memoize size of frozen messages?
+}
+
 void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
   // Note:  These are separate from GenerateMessageSerializationMethods()
   //   because they need to be generated even for messages that are optimized

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_message.h

@@ -66,6 +66,7 @@ class MessageGenerator : public SourceGeneratorBase {
   bool has_extension_ranges_;
 
   void GenerateMessageSerializationMethods(io::Printer* printer);
+  void GenerateWriteToBody(io::Printer* printer, bool use_write_context);
   void GenerateMergingMethods(io::Printer* printer);
   void GenerateMainParseLoop(io::Printer* printer, bool use_parse_context);
 

+ 7 - 1
src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc

@@ -90,9 +90,15 @@ void RepeatedEnumFieldGenerator::GenerateParsingCode(io::Printer* printer, bool
 }
 
 void RepeatedEnumFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
+  GenerateSerializationCode(printer, true);
+}
+
+void RepeatedEnumFieldGenerator::GenerateSerializationCode(io::Printer* printer, bool use_write_context) {
   printer->Print(
     variables_,
-    "$name$_.WriteTo(output, _repeated_$name$_codec);\n");
+    use_write_context
+    ? "$name$_.WriteTo(ref output, _repeated_$name$_codec);\n"
+    : "$name$_.WriteTo(output, _repeated_$name$_codec);\n");
 }
 
 void RepeatedEnumFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.h

@@ -61,6 +61,7 @@ class RepeatedEnumFieldGenerator : public FieldGeneratorBase {
   virtual void GenerateParsingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer, bool use_parse_context);
   virtual void GenerateSerializationCode(io::Printer* printer);
+  virtual void GenerateSerializationCode(io::Printer* printer, bool use_write_context);
   virtual void GenerateSerializedSizeCode(io::Printer* printer);
   virtual void GenerateExtensionCode(io::Printer* printer);
 

+ 7 - 1
src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc

@@ -105,9 +105,15 @@ void RepeatedMessageFieldGenerator::GenerateParsingCode(io::Printer* printer, bo
 }
 
 void RepeatedMessageFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
+  GenerateSerializationCode(printer, true);
+}
+
+void RepeatedMessageFieldGenerator::GenerateSerializationCode(io::Printer* printer, bool use_write_context) {
   printer->Print(
     variables_,
-    "$name$_.WriteTo(output, _repeated_$name$_codec);\n");
+    use_write_context
+    ? "$name$_.WriteTo(ref output, _repeated_$name$_codec);\n"
+    : "$name$_.WriteTo(output, _repeated_$name$_codec);\n");
 }
 
 void RepeatedMessageFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_repeated_message_field.h

@@ -61,6 +61,7 @@ class RepeatedMessageFieldGenerator : public FieldGeneratorBase {
   virtual void GenerateParsingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer, bool use_parse_context);
   virtual void GenerateSerializationCode(io::Printer* printer);
+  virtual void GenerateSerializationCode(io::Printer* printer, bool use_write_context);
   virtual void GenerateSerializedSizeCode(io::Printer* printer);
   virtual void GenerateExtensionCode(io::Printer* printer);
 

+ 7 - 1
src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc

@@ -90,9 +90,15 @@ void RepeatedPrimitiveFieldGenerator::GenerateParsingCode(io::Printer* printer,
 }
 
 void RepeatedPrimitiveFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
+  GenerateSerializationCode(printer, true);
+}
+
+void RepeatedPrimitiveFieldGenerator::GenerateSerializationCode(io::Printer* printer, bool use_write_context) {
   printer->Print(
     variables_,
-    "$name$_.WriteTo(output, _repeated_$name$_codec);\n");
+    use_write_context
+    ? "$name$_.WriteTo(ref output, _repeated_$name$_codec);\n"
+    : "$name$_.WriteTo(output, _repeated_$name$_codec);\n");
 }
 
 void RepeatedPrimitiveFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h

@@ -57,6 +57,7 @@ class RepeatedPrimitiveFieldGenerator : public FieldGeneratorBase {
   virtual void GenerateParsingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer, bool use_parse_context);
   virtual void GenerateSerializationCode(io::Printer* printer);
+  virtual void GenerateSerializationCode(io::Printer* printer, bool use_write_context);
   virtual void GenerateSerializedSizeCode(io::Printer* printer);
   virtual void GenerateExtensionCode(io::Printer* printer);
 

+ 22 - 6
src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc

@@ -132,11 +132,19 @@ void WrapperFieldGenerator::GenerateParsingCode(io::Printer* printer, bool use_p
 }
 
 void WrapperFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
+  GenerateSerializationCode(printer, true);
+}
+
+void WrapperFieldGenerator::GenerateSerializationCode(io::Printer* printer, bool use_write_context) {
   printer->Print(
     variables_,
-    "if ($has_property_check$) {\n"
-    "  _single_$name$_codec.WriteTagAndValue(output, $property_name$);\n"
-    "}\n");
+    use_write_context
+    ? "if ($has_property_check$) {\n"
+      "  _single_$name$_codec.WriteTagAndValue(ref output, $property_name$);\n"
+      "}\n"
+    : "if ($has_property_check$) {\n"
+      "  _single_$name$_codec.WriteTagAndValue(output, $property_name$);\n"
+      "}\n");
 }
 
 void WrapperFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
@@ -269,12 +277,20 @@ void WrapperOneofFieldGenerator::GenerateParsingCode(io::Printer* printer, bool
 }
 
 void WrapperOneofFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
+  GenerateSerializationCode(printer, true);
+}
+
+void WrapperOneofFieldGenerator::GenerateSerializationCode(io::Printer* printer, bool use_write_context) {
   // TODO: I suspect this is wrong...
   printer->Print(
     variables_,
-    "if ($has_property_check$) {\n"
-    "  _oneof_$name$_codec.WriteTagAndValue(output, ($type_name$) $oneof_name$_);\n"
-    "}\n");
+    use_write_context
+    ? "if ($has_property_check$) {\n"
+      "  _oneof_$name$_codec.WriteTagAndValue(ref output, ($type_name$) $oneof_name$_);\n"
+      "}\n"
+    : "if ($has_property_check$) {\n"
+      "  _oneof_$name$_codec.WriteTagAndValue(output, ($type_name$) $oneof_name$_);\n"
+      "}\n");
 }
 
 void WrapperOneofFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {

+ 2 - 0
src/google/protobuf/compiler/csharp/csharp_wrapper_field.h

@@ -60,6 +60,7 @@ class WrapperFieldGenerator : public FieldGeneratorBase {
   virtual void GenerateParsingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer, bool use_parse_context);
   virtual void GenerateSerializationCode(io::Printer* printer);
+  virtual void GenerateSerializationCode(io::Printer* printer, bool use_write_context);
   virtual void GenerateSerializedSizeCode(io::Printer* printer);
   virtual void GenerateExtensionCode(io::Printer* printer);
 
@@ -86,6 +87,7 @@ class WrapperOneofFieldGenerator : public WrapperFieldGenerator {
   virtual void GenerateParsingCode(io::Printer* printer);
   virtual void GenerateParsingCode(io::Printer* printer, bool use_parse_context);
   virtual void GenerateSerializationCode(io::Printer* printer);
+  virtual void GenerateSerializationCode(io::Printer* printer, bool use_write_context);
   virtual void GenerateSerializedSizeCode(io::Printer* printer);
 };
 

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно