浏览代码

Sync from Piper @310014675

PROTOBUF_SYNC_PIPER
Joshua Haberman 5 年之前
父节点
当前提交
2ac29ae5d4
共有 84 个文件被更改,包括 5555 次插入2017 次删除
  1. 11 1
      Makefile.am
  2. 18 18
      csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedInputStreamTest.cs
  3. 12 12
      csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
  4. 2 2
      csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/FieldCodecTest.cs
  5. 23 8
      csharp/src/AddressBook/Addressbook.cs
  6. 15 5
      csharp/src/Google.Protobuf.Benchmarks/BenchmarkMessage1Proto3.cs
  7. 8 3
      csharp/src/Google.Protobuf.Benchmarks/Benchmarks.cs
  8. 3 0
      csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj
  9. 49 0
      csharp/src/Google.Protobuf.Benchmarks/ParseMessagesBenchmark.cs
  10. 202 6
      csharp/src/Google.Protobuf.Benchmarks/ParseRawPrimitivesBenchmark.cs
  11. 123 113
      csharp/src/Google.Protobuf.Benchmarks/WrapperBenchmarkMessages.cs
  12. 29 9
      csharp/src/Google.Protobuf.Conformance/Conformance.cs
  13. 1 1
      csharp/src/Google.Protobuf.Test.TestProtos/Google.Protobuf.Test.TestProtos.csproj
  14. 86 51
      csharp/src/Google.Protobuf.Test.TestProtos/MapUnittestProto3.cs
  15. 134 89
      csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs
  16. 114 99
      csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs
  17. 242 92
      csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs
  18. 149 44
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestCustomOptionsProto3.cs
  19. 7 2
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestImport.cs
  20. 7 2
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestImportProto3.cs
  21. 7 2
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestImportPublic.cs
  22. 7 2
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestImportPublicProto3.cs
  23. 7 2
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssue6936B.cs
  24. 7 2
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssue6936C.cs
  25. 96 31
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs
  26. 252 112
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3.cs
  27. 14 4
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.cs
  28. 82 62
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestWellKnownTypes.cs
  29. 142 62
      csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
  30. 12 12
      csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
  31. 28 0
      csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs
  32. 25 7
      csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs
  33. 3 3
      csharp/src/Google.Protobuf.Test/FieldCodecTest.cs
  34. 9 11
      csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs
  35. 98 44
      csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
  36. 233 0
      csharp/src/Google.Protobuf.Test/LegacyGeneratedCodeTest.cs
  37. 100 0
      csharp/src/Google.Protobuf.Test/MessageParsingHelpers.cs
  38. 128 0
      csharp/src/Google.Protobuf.Test/ReadOnlySequenceFactory.cs
  39. 4 4
      csharp/src/Google.Protobuf.Test/UnknownFieldSetTest.cs
  40. 48 43
      csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs
  41. 2 2
      csharp/src/Google.Protobuf/ByteString.cs
  42. 82 716
      csharp/src/Google.Protobuf/CodedInputStream.cs
  43. 52 9
      csharp/src/Google.Protobuf/Collections/MapField.cs
  44. 28 8
      csharp/src/Google.Protobuf/Collections/RepeatedField.cs
  45. 2 2
      csharp/src/Google.Protobuf/ExtensionRegistry.cs
  46. 22 5
      csharp/src/Google.Protobuf/ExtensionSet.cs
  47. 30 24
      csharp/src/Google.Protobuf/ExtensionValue.cs
  48. 81 56
      csharp/src/Google.Protobuf/FieldCodec.cs
  49. 14 12
      csharp/src/Google.Protobuf/Google.Protobuf.csproj
  50. 49 0
      csharp/src/Google.Protobuf/IBufferMessage.cs
  51. 12 0
      csharp/src/Google.Protobuf/MessageExtensions.cs
  52. 28 0
      csharp/src/Google.Protobuf/MessageParser.cs
  53. 329 0
      csharp/src/Google.Protobuf/ParseContext.cs
  54. 115 0
      csharp/src/Google.Protobuf/ParserInternalState.cs
  55. 735 0
      csharp/src/Google.Protobuf/ParsingPrimitives.cs
  56. 229 0
      csharp/src/Google.Protobuf/ParsingPrimitivesMessages.cs
  57. 353 0
      csharp/src/Google.Protobuf/ParsingPrimitivesWrappers.cs
  58. 7 0
      csharp/src/Google.Protobuf/Properties/AssemblyInfo.cs
  59. 234 99
      csharp/src/Google.Protobuf/Reflection/Descriptor.cs
  60. 296 0
      csharp/src/Google.Protobuf/SegmentedBufferHelper.cs
  61. 44 19
      csharp/src/Google.Protobuf/UnknownFieldSet.cs
  62. 7 2
      csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
  63. 25 10
      csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
  64. 7 2
      csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs
  65. 7 2
      csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs
  66. 8 3
      csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
  67. 7 2
      csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs
  68. 23 8
      csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs
  69. 7 2
      csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs
  70. 42 17
      csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
  71. 63 18
      csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs
  72. 1 2
      src/google/protobuf/compiler/cpp/cpp_unittest.inc
  73. 1 1
      src/google/protobuf/compiler/csharp/csharp_map_field.cc
  74. 11 3
      src/google/protobuf/compiler/csharp/csharp_message.cc
  75. 1 1
      src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc
  76. 1 1
      src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc
  77. 1 1
      src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc
  78. 2 2
      src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc
  79. 25 25
      src/google/protobuf/map_test.cc
  80. 20 0
      src/google/protobuf/port_def.inc
  81. 10 0
      src/google/protobuf/port_undef.inc
  82. 3 2
      src/google/protobuf/util/field_mask_util.h
  83. 1 0
      src/google/protobuf/util/internal/default_value_objectwriter_test.cc
  84. 1 1
      src/google/protobuf/util/internal/protostream_objectwriter_test.cc

+ 11 - 1
Makefile.am

@@ -90,8 +90,8 @@ csharp_EXTRA_DIST=                                                           \
   csharp/src/Google.Protobuf.Benchmarks/Benchmarks.cs                        \
   csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj    \
   csharp/src/Google.Protobuf.Benchmarks/GoogleMessageBenchmark.cs            \
-  csharp/src/Google.Protobuf.Benchmarks/ParseRawPrimitivesBenchmark.cs       \
   csharp/src/Google.Protobuf.Benchmarks/ParseMessagesBenchmark.cs            \
+  csharp/src/Google.Protobuf.Benchmarks/ParseRawPrimitivesBenchmark.cs       \
   csharp/src/Google.Protobuf.Benchmarks/Program.cs                           \
   csharp/src/Google.Protobuf.Benchmarks/wrapper_benchmark_messages.proto     \
   csharp/src/Google.Protobuf.Benchmarks/WrapperBenchmarkMessages.cs          \
@@ -122,7 +122,10 @@ csharp_EXTRA_DIST=                                                           \
   csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs                       \
   csharp/src/Google.Protobuf.Test/JsonParserTest.cs                          \
   csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs                       \
+  csharp/src/Google.Protobuf.Test/LegacyGeneratedCodeTest.cs                 \
+  csharp/src/Google.Protobuf.Test/MessageParsingHelpers.cs                   \
   csharp/src/Google.Protobuf.Test/Proto3OptionalTest.cs                      \
+  csharp/src/Google.Protobuf.Test/ReadOnlySequenceFactory.cs                 \
   csharp/src/Google.Protobuf.Test/Reflection/CustomOptionsTest.cs            \
   csharp/src/Google.Protobuf.Test/Reflection/DescriptorDeclarationTest.cs    \
   csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs              \
@@ -182,6 +185,7 @@ csharp_EXTRA_DIST=                                                           \
   csharp/src/Google.Protobuf/FieldMaskTree.cs                                \
   csharp/src/Google.Protobuf/FrameworkPortability.cs                         \
   csharp/src/Google.Protobuf/Google.Protobuf.csproj                          \
+  csharp/src/Google.Protobuf/IBufferMessage.cs                               \
   csharp/src/Google.Protobuf/ICustomDiagnosticMessage.cs                     \
   csharp/src/Google.Protobuf/IDeepCloneable.cs                               \
   csharp/src/Google.Protobuf/IExtendableMessage.cs                           \
@@ -196,7 +200,13 @@ csharp_EXTRA_DIST=                                                           \
   csharp/src/Google.Protobuf/MessageExtensions.cs                            \
   csharp/src/Google.Protobuf/MessageParser.cs                                \
   csharp/src/Google.Protobuf/ObjectIntPair.cs                                \
+  csharp/src/Google.Protobuf/ParseContext.cs                                 \
+  csharp/src/Google.Protobuf/ParserInternalState.cs                          \
+  csharp/src/Google.Protobuf/ParsingPrimitives.cs                            \
+  csharp/src/Google.Protobuf/ParsingPrimitivesMessages.cs                    \
+  csharp/src/Google.Protobuf/ParsingPrimitivesWrappers.cs                    \
   csharp/src/Google.Protobuf/ProtoPreconditions.cs                           \
+  csharp/src/Google.Protobuf/SegmentedBufferHelper.cs                        \
   csharp/src/Google.Protobuf/Properties/AssemblyInfo.cs                      \
   csharp/src/Google.Protobuf/Reflection/CustomOptions.cs                     \
   csharp/src/Google.Protobuf/Reflection/Descriptor.cs                        \

+ 18 - 18
csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedInputStreamTest.cs

@@ -201,29 +201,29 @@ namespace Google.Protobuf
         [Test]
         public void DecodeZigZag32()
         {
-            Assert.AreEqual(0, CodedInputStream.DecodeZigZag32(0));
-            Assert.AreEqual(-1, CodedInputStream.DecodeZigZag32(1));
-            Assert.AreEqual(1, CodedInputStream.DecodeZigZag32(2));
-            Assert.AreEqual(-2, CodedInputStream.DecodeZigZag32(3));
-            Assert.AreEqual(0x3FFFFFFF, CodedInputStream.DecodeZigZag32(0x7FFFFFFE));
-            Assert.AreEqual(unchecked((int) 0xC0000000), CodedInputStream.DecodeZigZag32(0x7FFFFFFF));
-            Assert.AreEqual(0x7FFFFFFF, CodedInputStream.DecodeZigZag32(0xFFFFFFFE));
-            Assert.AreEqual(unchecked((int) 0x80000000), CodedInputStream.DecodeZigZag32(0xFFFFFFFF));
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(0));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(1));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(2));
+            Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag32(3));
+            Assert.AreEqual(0x3FFFFFFF, ParsingPrimitives.DecodeZigZag32(0x7FFFFFFE));
+            Assert.AreEqual(unchecked((int) 0xC0000000), ParsingPrimitives.DecodeZigZag32(0x7FFFFFFF));
+            Assert.AreEqual(0x7FFFFFFF, ParsingPrimitives.DecodeZigZag32(0xFFFFFFFE));
+            Assert.AreEqual(unchecked((int) 0x80000000), ParsingPrimitives.DecodeZigZag32(0xFFFFFFFF));
         }
 
         [Test]
         public void DecodeZigZag64()
         {
-            Assert.AreEqual(0, CodedInputStream.DecodeZigZag64(0));
-            Assert.AreEqual(-1, CodedInputStream.DecodeZigZag64(1));
-            Assert.AreEqual(1, CodedInputStream.DecodeZigZag64(2));
-            Assert.AreEqual(-2, CodedInputStream.DecodeZigZag64(3));
-            Assert.AreEqual(0x000000003FFFFFFFL, CodedInputStream.DecodeZigZag64(0x000000007FFFFFFEL));
-            Assert.AreEqual(unchecked((long) 0xFFFFFFFFC0000000L), CodedInputStream.DecodeZigZag64(0x000000007FFFFFFFL));
-            Assert.AreEqual(0x000000007FFFFFFFL, CodedInputStream.DecodeZigZag64(0x00000000FFFFFFFEL));
-            Assert.AreEqual(unchecked((long) 0xFFFFFFFF80000000L), CodedInputStream.DecodeZigZag64(0x00000000FFFFFFFFL));
-            Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
-            Assert.AreEqual(unchecked((long) 0x8000000000000000L), CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(0));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(1));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(2));
+            Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag64(3));
+            Assert.AreEqual(0x000000003FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFEL));
+            Assert.AreEqual(unchecked((long) 0xFFFFFFFFC0000000L), ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFFL));
+            Assert.AreEqual(0x000000007FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFEL));
+            Assert.AreEqual(unchecked((long) 0xFFFFFFFF80000000L), ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFFL));
+            Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
+            Assert.AreEqual(unchecked((long) 0x8000000000000000L), ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
         }
         
         [Test]

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

@@ -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, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(0)));
-            Assert.AreEqual(1, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(1)));
-            Assert.AreEqual(-1, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(-1)));
-            Assert.AreEqual(14927, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(14927)));
-            Assert.AreEqual(-3612, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(-3612)));
+            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)));
         }
 
         [Test]
         public void RoundTripZigZag64()
         {
-            Assert.AreEqual(0, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(0)));
-            Assert.AreEqual(1, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(1)));
-            Assert.AreEqual(-1, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-1)));
-            Assert.AreEqual(14927, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(14927)));
-            Assert.AreEqual(-3612, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-3612)));
+            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(856912304801416L,
-                            CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(856912304801416L)));
+                            ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(856912304801416L)));
             Assert.AreEqual(-75123905439571256L,
-                            CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-75123905439571256L)));
+                            ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-75123905439571256L)));
         }
 
         [Test]

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

@@ -128,7 +128,7 @@ namespace Google.Protobuf
                 codedOutput.Flush();
                 stream.Position = 0;
                 var codedInput = new CodedInputStream(stream);
-                Assert.AreEqual(sampleValue, codec.ValueReader(codedInput));
+                Assert.AreEqual(sampleValue, codec.Read(codedInput));
                 Assert.IsTrue(codedInput.IsAtEnd);
             }
 
@@ -178,7 +178,7 @@ namespace Google.Protobuf
                     Assert.AreEqual(stream.Position, codec.ValueSizeCalculator(codec.DefaultValue));
                     stream.Position = 0;
                     var codedInput = new CodedInputStream(stream);
-                    Assert.AreEqual(codec.DefaultValue, codec.ValueReader(codedInput));
+                    Assert.AreEqual(codec.DefaultValue, codec.Read(codedInput));
                 }
             }
 

+ 23 - 8
csharp/src/AddressBook/Addressbook.cs

@@ -49,7 +49,7 @@ namespace Google.Protobuf.Examples.AddressBook {
   /// <summary>
   /// [START messages]
   /// </summary>
-  public sealed partial class Person : pb::IMessage<Person> {
+  public sealed partial class Person : pb::IMessage<Person>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Person> _parser = new pb::MessageParser<Person>(() => new Person());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -256,11 +256,16 @@ namespace Google.Protobuf.Examples.AddressBook {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
@@ -275,7 +280,7 @@ namespace Google.Protobuf.Examples.AddressBook {
             break;
           }
           case 34: {
-            phones_.AddEntriesFrom(input, _repeated_phones_codec);
+            phones_.AddEntriesFrom(ref input, _repeated_phones_codec);
             break;
           }
           case 42: {
@@ -299,7 +304,7 @@ namespace Google.Protobuf.Examples.AddressBook {
         [pbr::OriginalName("WORK")] Work = 2,
       }
 
-      public sealed partial class PhoneNumber : pb::IMessage<PhoneNumber> {
+      public sealed partial class PhoneNumber : pb::IMessage<PhoneNumber>, pb::IBufferMessage {
         private static readonly pb::MessageParser<PhoneNumber> _parser = new pb::MessageParser<PhoneNumber>(() => new PhoneNumber());
         private pb::UnknownFieldSet _unknownFields;
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -436,11 +441,16 @@ namespace Google.Protobuf.Examples.AddressBook {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 10: {
                 Number = input.ReadString();
@@ -464,7 +474,7 @@ namespace Google.Protobuf.Examples.AddressBook {
   /// <summary>
   /// Our address book file is just one of these.
   /// </summary>
-  public sealed partial class AddressBook : pb::IMessage<AddressBook> {
+  public sealed partial class AddressBook : pb::IMessage<AddressBook>, pb::IBufferMessage {
     private static readonly pb::MessageParser<AddressBook> _parser = new pb::MessageParser<AddressBook>(() => new AddressBook());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -569,14 +579,19 @@ namespace Google.Protobuf.Examples.AddressBook {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            people_.AddEntriesFrom(input, _repeated_people_codec);
+            people_.AddEntriesFrom(ref input, _repeated_people_codec);
             break;
           }
         }

+ 15 - 5
csharp/src/Google.Protobuf.Benchmarks/BenchmarkMessage1Proto3.cs

@@ -64,7 +64,7 @@ namespace Benchmarks.Proto3 {
 
   }
   #region Messages
-  public sealed partial class GoogleMessage1 : pb::IMessage<GoogleMessage1> {
+  public sealed partial class GoogleMessage1 : pb::IMessage<GoogleMessage1>, pb::IBufferMessage {
     private static readonly pb::MessageParser<GoogleMessage1> _parser = new pb::MessageParser<GoogleMessage1>(() => new GoogleMessage1());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1132,11 +1132,16 @@ namespace Benchmarks.Proto3 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Field1 = input.ReadString();
@@ -1156,7 +1161,7 @@ namespace Benchmarks.Proto3 {
           }
           case 42:
           case 41: {
-            field5_.AddEntriesFrom(input, _repeated_field5_codec);
+            field5_.AddEntriesFrom(ref input, _repeated_field5_codec);
             break;
           }
           case 48: {
@@ -1312,7 +1317,7 @@ namespace Benchmarks.Proto3 {
 
   }
 
-  public sealed partial class GoogleMessage1SubMessage : pb::IMessage<GoogleMessage1SubMessage> {
+  public sealed partial class GoogleMessage1SubMessage : pb::IMessage<GoogleMessage1SubMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<GoogleMessage1SubMessage> _parser = new pb::MessageParser<GoogleMessage1SubMessage>(() => new GoogleMessage1SubMessage());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1881,11 +1886,16 @@ namespace Benchmarks.Proto3 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Field1 = input.ReadInt32();

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

@@ -38,7 +38,7 @@ namespace Benchmarks {
 
   }
   #region Messages
-  public sealed partial class BenchmarkDataset : pb::IMessage<BenchmarkDataset> {
+  public sealed partial class BenchmarkDataset : pb::IMessage<BenchmarkDataset>, pb::IBufferMessage {
     private static readonly pb::MessageParser<BenchmarkDataset> _parser = new pb::MessageParser<BenchmarkDataset>(() => new BenchmarkDataset());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -219,11 +219,16 @@ namespace Benchmarks {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
@@ -234,7 +239,7 @@ namespace Benchmarks {
             break;
           }
           case 26: {
-            payload_.AddEntriesFrom(input, _repeated_payload_codec);
+            payload_.AddEntriesFrom(ref input, _repeated_payload_codec);
             break;
           }
         }

+ 3 - 0
csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj

@@ -3,6 +3,9 @@
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TargetFramework>netcoreapp2.1</TargetFramework>
+    <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
+    <SignAssembly>true</SignAssembly>
+    <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
     <IsPackable>False</IsPackable>
   </PropertyGroup>
 

+ 49 - 0
csharp/src/Google.Protobuf.Benchmarks/ParseMessagesBenchmark.cs

@@ -65,18 +65,36 @@ namespace Google.Protobuf.Benchmarks
             return manyWrapperFieldsTest.ParseFromByteArray();
         }
 
+        [Benchmark]
+        public IMessage ManyWrapperFieldsMessage_ParseFromReadOnlySequence()
+        {
+            return manyWrapperFieldsTest.ParseFromReadOnlySequence();
+        }
+
         [Benchmark]
         public IMessage ManyPrimitiveFieldsMessage_ParseFromByteArray()
         {
             return manyPrimitiveFieldsTest.ParseFromByteArray();
         }
 
+        [Benchmark]
+        public IMessage ManyPrimitiveFieldsMessage_ParseFromReadOnlySequence()
+        {
+            return manyPrimitiveFieldsTest.ParseFromReadOnlySequence();
+        }
+
         [Benchmark]
         public IMessage EmptyMessage_ParseFromByteArray()
         {
             return emptyMessageTest.ParseFromByteArray();
         }
 
+        [Benchmark]
+        public IMessage EmptyMessage_ParseFromReadOnlySequence()
+        {
+            return emptyMessageTest.ParseFromReadOnlySequence();
+        }
+
         [Benchmark]
         [ArgumentsSource(nameof(MessageCountValues))]
         public void ManyWrapperFieldsMessage_ParseDelimitedMessagesFromByteArray(int messageCount)
@@ -84,6 +102,13 @@ namespace Google.Protobuf.Benchmarks
             manyWrapperFieldsTest.ParseDelimitedMessagesFromByteArray(messageCount);
         }
 
+        [Benchmark]
+        [ArgumentsSource(nameof(MessageCountValues))]
+        public void ManyWrapperFieldsMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount)
+        {
+            manyWrapperFieldsTest.ParseDelimitedMessagesFromReadOnlySequence(messageCount);
+        }
+
         [Benchmark]
         [ArgumentsSource(nameof(MessageCountValues))]
         public void ManyPrimitiveFieldsMessage_ParseDelimitedMessagesFromByteArray(int messageCount)
@@ -91,6 +116,13 @@ namespace Google.Protobuf.Benchmarks
             manyPrimitiveFieldsTest.ParseDelimitedMessagesFromByteArray(messageCount);
         }
 
+        [Benchmark]
+        [ArgumentsSource(nameof(MessageCountValues))]
+        public void ManyPrimitiveFieldsMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount)
+        {
+            manyPrimitiveFieldsTest.ParseDelimitedMessagesFromReadOnlySequence(messageCount);
+        }
+
         private static ManyWrapperFieldsMessage CreateManyWrapperFieldsMessage()
         {
             // Example data match data of an internal benchmarks
@@ -133,6 +165,9 @@ namespace Google.Protobuf.Benchmarks
             private readonly byte[] data;
             private readonly byte[] multipleMessagesData;
 
+            private ReadOnlySequence<byte> dataSequence;
+            private ReadOnlySequence<byte> multipleMessagesDataSequence;
+
             public SubTest(IMessage message, MessageParser parser, Func<IMessage> factory, int maxMessageCount)
             {
                 this.message = message;
@@ -140,10 +175,14 @@ namespace Google.Protobuf.Benchmarks
                 this.factory = factory;
                 this.data = message.ToByteArray();
                 this.multipleMessagesData = CreateBufferWithMultipleMessages(message, maxMessageCount);
+                this.dataSequence = new ReadOnlySequence<byte>(this.data);
+                this.multipleMessagesDataSequence = new ReadOnlySequence<byte>(this.multipleMessagesData);
             }
 
             public IMessage ParseFromByteArray() => parser.ParseFrom(data);
 
+            public IMessage ParseFromReadOnlySequence() => parser.ParseFrom(dataSequence);
+
             public void ParseDelimitedMessagesFromByteArray(int messageCount)
             {
                 var input = new CodedInputStream(multipleMessagesData);
@@ -154,6 +193,16 @@ namespace Google.Protobuf.Benchmarks
                 }
             }
 
+            public void ParseDelimitedMessagesFromReadOnlySequence(int messageCount)
+            {
+                ParseContext.Initialize(multipleMessagesDataSequence, out ParseContext ctx);
+                for (int i = 0; i < messageCount; i++)
+                {
+                    var msg = factory();
+                    ctx.ReadMessage(msg);
+                }
+            }
+
             private static byte[] CreateBufferWithMultipleMessages(IMessage msg, int msgCount)
             {
                 var ms = new MemoryStream();

+ 202 - 6
csharp/src/Google.Protobuf.Benchmarks/ParseRawPrimitivesBenchmark.cs

@@ -35,6 +35,7 @@ using System;
 using System.Buffers.Binary;
 using System.Collections.Generic;
 using System.IO;
+using System.Buffers;
 
 namespace Google.Protobuf.Benchmarks
 {
@@ -51,8 +52,13 @@ namespace Google.Protobuf.Benchmarks
         byte[] floatInputBuffer;
         byte[] fixedIntInputBuffer;
 
+        // key is the encodedSize of string values
+        Dictionary<int, byte[]> stringInputBuffers;
+
         Random random = new Random(417384220);  // random but deterministic seed
 
+        public IEnumerable<int> StringEncodedSizes => new[] { 1, 4, 10, 105, 10080 };
+
         [GlobalSetup]
         public void GlobalSetup()
         {
@@ -70,6 +76,13 @@ namespace Google.Protobuf.Benchmarks
             doubleInputBuffer = CreateBufferWithRandomDoubles(random, BytesToParse / sizeof(double), paddingValueCount);
             floatInputBuffer = CreateBufferWithRandomFloats(random, BytesToParse / sizeof(float), paddingValueCount);
             fixedIntInputBuffer = CreateBufferWithRandomData(random, BytesToParse / sizeof(long), sizeof(long), paddingValueCount);
+
+            stringInputBuffers = new Dictionary<int, byte[]>();
+            foreach(var encodedSize in StringEncodedSizes)
+            {
+                byte[] buffer = CreateBufferWithStrings(BytesToParse / encodedSize, encodedSize, encodedSize < 10 ? 10 : 1 );
+                stringInputBuffers.Add(encodedSize, buffer);
+            }
         }
 
         // Total number of bytes that each benchmark will parse.
@@ -85,7 +98,7 @@ namespace Google.Protobuf.Benchmarks
         [Arguments(3)]
         [Arguments(4)]
         [Arguments(5)]
-        public int ParseRawVarint32(int encodedSize)
+        public int ParseRawVarint32_CodedInputStream(int encodedSize)
         {
             CodedInputStream cis = new CodedInputStream(varintInputBuffers[encodedSize]);
             int sum = 0;
@@ -96,6 +109,23 @@ namespace Google.Protobuf.Benchmarks
             return sum;
         }
 
+        [Benchmark]
+        [Arguments(1)]
+        [Arguments(2)]
+        [Arguments(3)]
+        [Arguments(4)]
+        [Arguments(5)]
+        public int ParseRawVarint32_ParseContext(int encodedSize)
+        {
+            InitializeParseContext(varintInputBuffers[encodedSize], out ParseContext ctx);
+            int sum = 0;
+            for (int i = 0; i < BytesToParse / encodedSize; i++)
+            {
+                sum += ctx.ReadInt32();
+            }
+            return sum;
+        }
+
         [Benchmark]
         [Arguments(1)]
         [Arguments(2)]
@@ -107,7 +137,7 @@ namespace Google.Protobuf.Benchmarks
         [Arguments(8)]
         [Arguments(9)]
         [Arguments(10)]
-        public long ParseRawVarint64(int encodedSize)
+        public long ParseRawVarint64_CodedInputStream(int encodedSize)
         {
             CodedInputStream cis = new CodedInputStream(varintInputBuffers[encodedSize]);
             long sum = 0;
@@ -119,7 +149,29 @@ namespace Google.Protobuf.Benchmarks
         }
 
         [Benchmark]
-        public uint ParseFixed32()
+        [Arguments(1)]
+        [Arguments(2)]
+        [Arguments(3)]
+        [Arguments(4)]
+        [Arguments(5)]
+        [Arguments(6)]
+        [Arguments(7)]
+        [Arguments(8)]
+        [Arguments(9)]
+        [Arguments(10)]
+        public long ParseRawVarint64_ParseContext(int encodedSize)
+        {
+            InitializeParseContext(varintInputBuffers[encodedSize], out ParseContext ctx);
+            long sum = 0;
+            for (int i = 0; i < BytesToParse / encodedSize; i++)
+            {
+                sum += ctx.ReadInt64();
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        public uint ParseFixed32_CodedInputStream()
         {
             const int encodedSize = sizeof(uint);
             CodedInputStream cis = new CodedInputStream(fixedIntInputBuffer);
@@ -132,7 +184,20 @@ namespace Google.Protobuf.Benchmarks
         }
 
         [Benchmark]
-        public ulong ParseFixed64()
+        public uint ParseFixed32_ParseContext()
+        {
+            const int encodedSize = sizeof(uint);
+            InitializeParseContext(fixedIntInputBuffer, out ParseContext ctx);
+            uint sum = 0;
+            for (uint i = 0; i < BytesToParse / encodedSize; i++)
+            {
+                sum += ctx.ReadFixed32();
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        public ulong ParseFixed64_CodedInputStream()
         {
             const int encodedSize = sizeof(ulong);
             CodedInputStream cis = new CodedInputStream(fixedIntInputBuffer);
@@ -145,7 +210,20 @@ namespace Google.Protobuf.Benchmarks
         }
 
         [Benchmark]
-        public float ParseRawFloat()
+        public ulong ParseFixed64_ParseContext()
+        {
+            const int encodedSize = sizeof(ulong);
+            InitializeParseContext(fixedIntInputBuffer, out ParseContext ctx);
+            ulong sum = 0;
+            for (int i = 0; i < BytesToParse / encodedSize; i++)
+            {
+                sum += ctx.ReadFixed64();
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        public float ParseRawFloat_CodedInputStream()
         {
             const int encodedSize = sizeof(float);
             CodedInputStream cis = new CodedInputStream(floatInputBuffer);
@@ -158,7 +236,20 @@ namespace Google.Protobuf.Benchmarks
         }
 
         [Benchmark]
-        public double ParseRawDouble()
+        public float ParseRawFloat_ParseContext()
+        {
+            const int encodedSize = sizeof(float);
+            InitializeParseContext(floatInputBuffer, out ParseContext ctx);
+            float sum = 0;
+            for (int i = 0; i < BytesToParse / encodedSize; i++)
+            {
+               sum += ctx.ReadFloat();
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        public double ParseRawDouble_CodedInputStream()
         {
             const int encodedSize = sizeof(double);
             CodedInputStream cis = new CodedInputStream(doubleInputBuffer);
@@ -170,6 +261,76 @@ namespace Google.Protobuf.Benchmarks
             return sum;
         }
 
+        [Benchmark]
+        public double ParseRawDouble_ParseContext()
+        {
+            const int encodedSize = sizeof(double);
+            InitializeParseContext(doubleInputBuffer, out ParseContext ctx);
+            double sum = 0;
+            for (int i = 0; i < BytesToParse / encodedSize; i++)
+            {
+                sum += ctx.ReadDouble();
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(StringEncodedSizes))]
+        public int ParseString_CodedInputStream(int encodedSize)
+        {
+            CodedInputStream cis = new CodedInputStream(stringInputBuffers[encodedSize]);
+            int sum = 0;
+            for (int i = 0; i < BytesToParse / encodedSize; i++)
+            {
+                sum += cis.ReadString().Length;
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(StringEncodedSizes))]
+        public int ParseString_ParseContext(int encodedSize)
+        {
+            InitializeParseContext(stringInputBuffers[encodedSize], out ParseContext ctx);
+            int sum = 0;
+            for (int i = 0; i < BytesToParse / encodedSize; i++)
+            {
+                sum += ctx.ReadString().Length;
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(StringEncodedSizes))]
+        public int ParseBytes_CodedInputStream(int encodedSize)
+        {
+            CodedInputStream cis = new CodedInputStream(stringInputBuffers[encodedSize]);
+            int sum = 0;
+            for (int i = 0; i < BytesToParse / encodedSize; i++)
+            {
+                sum += cis.ReadBytes().Length;
+            }
+            return sum;
+        }
+
+        [Benchmark]
+        [ArgumentsSource(nameof(StringEncodedSizes))]
+        public int ParseBytes_ParseContext(int encodedSize)
+        {
+            InitializeParseContext(stringInputBuffers[encodedSize], out ParseContext ctx);
+            int sum = 0;
+            for (int i = 0; i < BytesToParse / encodedSize; i++)
+            {
+                sum += ctx.ReadBytes().Length;
+            }
+            return sum;
+        }
+
+        private static void InitializeParseContext(byte[] buffer, out ParseContext ctx)
+        {
+            ParseContext.Initialize(new ReadOnlySequence<byte>(buffer), out ctx);
+        }
+
         private static byte[] CreateBufferWithRandomVarints(Random random, int valueCount, int encodedSize, int paddingValueCount)
         {
             MemoryStream ms = new MemoryStream();
@@ -261,5 +422,40 @@ namespace Google.Protobuf.Benchmarks
             }
             return result;
         }
+
+        private static byte[] CreateBufferWithStrings(int valueCount, int encodedSize, int paddingValueCount)
+        {
+            var str = CreateStringWithEncodedSize(encodedSize);
+
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream cos = new CodedOutputStream(ms);
+            for (int i = 0; i < valueCount + paddingValueCount; i++)
+            {
+                cos.WriteString(str);
+            }
+            cos.Flush();
+            var buffer = ms.ToArray();
+
+            if (buffer.Length != encodedSize * (valueCount + paddingValueCount))
+            {
+                throw new InvalidOperationException($"Unexpected output buffer length {buffer.Length}");
+            }
+            return buffer;
+        }
+
+        private static string CreateStringWithEncodedSize(int encodedSize)
+        {
+            var str = new string('a', encodedSize);
+            while (CodedOutputStream.ComputeStringSize(str) > encodedSize)
+            {
+                str = str.Substring(1);
+            }
+
+            if (CodedOutputStream.ComputeStringSize(str) != encodedSize)
+            {
+                throw new InvalidOperationException($"Generated string with wrong encodedSize");
+            }
+            return str;
+        }
     }
 }

+ 123 - 113
csharp/src/Google.Protobuf.Benchmarks/WrapperBenchmarkMessages.cs

@@ -237,7 +237,7 @@ namespace Google.Protobuf.Benchmarks {
   /// a message that has a large number of wrapper fields
   /// obfuscated version of an internal message
   /// </summary>
-  public sealed partial class ManyWrapperFieldsMessage : pb::IMessage<ManyWrapperFieldsMessage> {
+  public sealed partial class ManyWrapperFieldsMessage : pb::IMessage<ManyWrapperFieldsMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ManyWrapperFieldsMessage> _parser = new pb::MessageParser<ManyWrapperFieldsMessage>(() => new ManyWrapperFieldsMessage());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -3303,434 +3303,439 @@ namespace Google.Protobuf.Benchmarks {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            double? value = _single_doubleField1_codec.Read(input);
+            double? value = _single_doubleField1_codec.Read(ref input);
             if (doubleField1_ == null || value != 0D) {
               DoubleField1 = value;
             }
             break;
           }
           case 18: {
-            long? value = _single_int64Field2_codec.Read(input);
+            long? value = _single_int64Field2_codec.Read(ref input);
             if (int64Field2_ == null || value != 0L) {
               Int64Field2 = value;
             }
             break;
           }
           case 26: {
-            long? value = _single_int64Field3_codec.Read(input);
+            long? value = _single_int64Field3_codec.Read(ref input);
             if (int64Field3_ == null || value != 0L) {
               Int64Field3 = value;
             }
             break;
           }
           case 34: {
-            long? value = _single_int64Field4_codec.Read(input);
+            long? value = _single_int64Field4_codec.Read(ref input);
             if (int64Field4_ == null || value != 0L) {
               Int64Field4 = value;
             }
             break;
           }
           case 58: {
-            double? value = _single_doubleField7_codec.Read(input);
+            double? value = _single_doubleField7_codec.Read(ref input);
             if (doubleField7_ == null || value != 0D) {
               DoubleField7 = value;
             }
             break;
           }
           case 66: {
-            double? value = _single_doubleField8_codec.Read(input);
+            double? value = _single_doubleField8_codec.Read(ref input);
             if (doubleField8_ == null || value != 0D) {
               DoubleField8 = value;
             }
             break;
           }
           case 74: {
-            double? value = _single_doubleField9_codec.Read(input);
+            double? value = _single_doubleField9_codec.Read(ref input);
             if (doubleField9_ == null || value != 0D) {
               DoubleField9 = value;
             }
             break;
           }
           case 82: {
-            double? value = _single_doubleField10_codec.Read(input);
+            double? value = _single_doubleField10_codec.Read(ref input);
             if (doubleField10_ == null || value != 0D) {
               DoubleField10 = value;
             }
             break;
           }
           case 90: {
-            double? value = _single_doubleField11_codec.Read(input);
+            double? value = _single_doubleField11_codec.Read(ref input);
             if (doubleField11_ == null || value != 0D) {
               DoubleField11 = value;
             }
             break;
           }
           case 114: {
-            double? value = _single_doubleField14_codec.Read(input);
+            double? value = _single_doubleField14_codec.Read(ref input);
             if (doubleField14_ == null || value != 0D) {
               DoubleField14 = value;
             }
             break;
           }
           case 122: {
-            double? value = _single_doubleField15_codec.Read(input);
+            double? value = _single_doubleField15_codec.Read(ref input);
             if (doubleField15_ == null || value != 0D) {
               DoubleField15 = value;
             }
             break;
           }
           case 154: {
-            long? value = _single_int64Field19_codec.Read(input);
+            long? value = _single_int64Field19_codec.Read(ref input);
             if (int64Field19_ == null || value != 0L) {
               Int64Field19 = value;
             }
             break;
           }
           case 162: {
-            double? value = _single_doubleField20_codec.Read(input);
+            double? value = _single_doubleField20_codec.Read(ref input);
             if (doubleField20_ == null || value != 0D) {
               DoubleField20 = value;
             }
             break;
           }
           case 170: {
-            double? value = _single_doubleField21_codec.Read(input);
+            double? value = _single_doubleField21_codec.Read(ref input);
             if (doubleField21_ == null || value != 0D) {
               DoubleField21 = value;
             }
             break;
           }
           case 178: {
-            double? value = _single_doubleField22_codec.Read(input);
+            double? value = _single_doubleField22_codec.Read(ref input);
             if (doubleField22_ == null || value != 0D) {
               DoubleField22 = value;
             }
             break;
           }
           case 202: {
-            double? value = _single_doubleField25_codec.Read(input);
+            double? value = _single_doubleField25_codec.Read(ref input);
             if (doubleField25_ == null || value != 0D) {
               DoubleField25 = value;
             }
             break;
           }
           case 210: {
-            long? value = _single_int64Field26_codec.Read(input);
+            long? value = _single_int64Field26_codec.Read(ref input);
             if (int64Field26_ == null || value != 0L) {
               Int64Field26 = value;
             }
             break;
           }
           case 226: {
-            double? value = _single_doubleField28_codec.Read(input);
+            double? value = _single_doubleField28_codec.Read(ref input);
             if (doubleField28_ == null || value != 0D) {
               DoubleField28 = value;
             }
             break;
           }
           case 234: {
-            double? value = _single_doubleField29_codec.Read(input);
+            double? value = _single_doubleField29_codec.Read(ref input);
             if (doubleField29_ == null || value != 0D) {
               DoubleField29 = value;
             }
             break;
           }
           case 242: {
-            double? value = _single_doubleField30_codec.Read(input);
+            double? value = _single_doubleField30_codec.Read(ref input);
             if (doubleField30_ == null || value != 0D) {
               DoubleField30 = value;
             }
             break;
           }
           case 250: {
-            double? value = _single_doubleField31_codec.Read(input);
+            double? value = _single_doubleField31_codec.Read(ref input);
             if (doubleField31_ == null || value != 0D) {
               DoubleField31 = value;
             }
             break;
           }
           case 258: {
-            long? value = _single_int64Field32_codec.Read(input);
+            long? value = _single_int64Field32_codec.Read(ref input);
             if (int64Field32_ == null || value != 0L) {
               Int64Field32 = value;
             }
             break;
           }
           case 298: {
-            long? value = _single_int64Field37_codec.Read(input);
+            long? value = _single_int64Field37_codec.Read(ref input);
             if (int64Field37_ == null || value != 0L) {
               Int64Field37 = value;
             }
             break;
           }
           case 306: {
-            double? value = _single_doubleField38_codec.Read(input);
+            double? value = _single_doubleField38_codec.Read(ref input);
             if (doubleField38_ == null || value != 0D) {
               DoubleField38 = value;
             }
             break;
           }
           case 314: {
-            long? value = _single_interactions_codec.Read(input);
+            long? value = _single_interactions_codec.Read(ref input);
             if (interactions_ == null || value != 0L) {
               Interactions = value;
             }
             break;
           }
           case 322: {
-            double? value = _single_doubleField40_codec.Read(input);
+            double? value = _single_doubleField40_codec.Read(ref input);
             if (doubleField40_ == null || value != 0D) {
               DoubleField40 = value;
             }
             break;
           }
           case 330: {
-            long? value = _single_int64Field41_codec.Read(input);
+            long? value = _single_int64Field41_codec.Read(ref input);
             if (int64Field41_ == null || value != 0L) {
               Int64Field41 = value;
             }
             break;
           }
           case 338: {
-            double? value = _single_doubleField42_codec.Read(input);
+            double? value = _single_doubleField42_codec.Read(ref input);
             if (doubleField42_ == null || value != 0D) {
               DoubleField42 = value;
             }
             break;
           }
           case 346: {
-            long? value = _single_int64Field43_codec.Read(input);
+            long? value = _single_int64Field43_codec.Read(ref input);
             if (int64Field43_ == null || value != 0L) {
               Int64Field43 = value;
             }
             break;
           }
           case 354: {
-            long? value = _single_int64Field44_codec.Read(input);
+            long? value = _single_int64Field44_codec.Read(ref input);
             if (int64Field44_ == null || value != 0L) {
               Int64Field44 = value;
             }
             break;
           }
           case 362: {
-            double? value = _single_doubleField45_codec.Read(input);
+            double? value = _single_doubleField45_codec.Read(ref input);
             if (doubleField45_ == null || value != 0D) {
               DoubleField45 = value;
             }
             break;
           }
           case 370: {
-            double? value = _single_doubleField46_codec.Read(input);
+            double? value = _single_doubleField46_codec.Read(ref input);
             if (doubleField46_ == null || value != 0D) {
               DoubleField46 = value;
             }
             break;
           }
           case 378: {
-            double? value = _single_doubleField47_codec.Read(input);
+            double? value = _single_doubleField47_codec.Read(ref input);
             if (doubleField47_ == null || value != 0D) {
               DoubleField47 = value;
             }
             break;
           }
           case 386: {
-            double? value = _single_doubleField48_codec.Read(input);
+            double? value = _single_doubleField48_codec.Read(ref input);
             if (doubleField48_ == null || value != 0D) {
               DoubleField48 = value;
             }
             break;
           }
           case 394: {
-            double? value = _single_doubleField49_codec.Read(input);
+            double? value = _single_doubleField49_codec.Read(ref input);
             if (doubleField49_ == null || value != 0D) {
               DoubleField49 = value;
             }
             break;
           }
           case 402: {
-            double? value = _single_doubleField50_codec.Read(input);
+            double? value = _single_doubleField50_codec.Read(ref input);
             if (doubleField50_ == null || value != 0D) {
               DoubleField50 = value;
             }
             break;
           }
           case 410: {
-            double? value = _single_doubleField51_codec.Read(input);
+            double? value = _single_doubleField51_codec.Read(ref input);
             if (doubleField51_ == null || value != 0D) {
               DoubleField51 = value;
             }
             break;
           }
           case 418: {
-            double? value = _single_doubleField52_codec.Read(input);
+            double? value = _single_doubleField52_codec.Read(ref input);
             if (doubleField52_ == null || value != 0D) {
               DoubleField52 = value;
             }
             break;
           }
           case 426: {
-            double? value = _single_doubleField53_codec.Read(input);
+            double? value = _single_doubleField53_codec.Read(ref input);
             if (doubleField53_ == null || value != 0D) {
               DoubleField53 = value;
             }
             break;
           }
           case 434: {
-            double? value = _single_doubleField54_codec.Read(input);
+            double? value = _single_doubleField54_codec.Read(ref input);
             if (doubleField54_ == null || value != 0D) {
               DoubleField54 = value;
             }
             break;
           }
           case 442: {
-            double? value = _single_doubleField55_codec.Read(input);
+            double? value = _single_doubleField55_codec.Read(ref input);
             if (doubleField55_ == null || value != 0D) {
               DoubleField55 = value;
             }
             break;
           }
           case 450: {
-            double? value = _single_doubleField56_codec.Read(input);
+            double? value = _single_doubleField56_codec.Read(ref input);
             if (doubleField56_ == null || value != 0D) {
               DoubleField56 = value;
             }
             break;
           }
           case 458: {
-            double? value = _single_doubleField57_codec.Read(input);
+            double? value = _single_doubleField57_codec.Read(ref input);
             if (doubleField57_ == null || value != 0D) {
               DoubleField57 = value;
             }
             break;
           }
           case 466: {
-            double? value = _single_doubleField58_codec.Read(input);
+            double? value = _single_doubleField58_codec.Read(ref input);
             if (doubleField58_ == null || value != 0D) {
               DoubleField58 = value;
             }
             break;
           }
           case 474: {
-            long? value = _single_int64Field59_codec.Read(input);
+            long? value = _single_int64Field59_codec.Read(ref input);
             if (int64Field59_ == null || value != 0L) {
               Int64Field59 = value;
             }
             break;
           }
           case 482: {
-            long? value = _single_int64Field60_codec.Read(input);
+            long? value = _single_int64Field60_codec.Read(ref input);
             if (int64Field60_ == null || value != 0L) {
               Int64Field60 = value;
             }
             break;
           }
           case 498: {
-            double? value = _single_doubleField62_codec.Read(input);
+            double? value = _single_doubleField62_codec.Read(ref input);
             if (doubleField62_ == null || value != 0D) {
               DoubleField62 = value;
             }
             break;
           }
           case 522: {
-            double? value = _single_doubleField65_codec.Read(input);
+            double? value = _single_doubleField65_codec.Read(ref input);
             if (doubleField65_ == null || value != 0D) {
               DoubleField65 = value;
             }
             break;
           }
           case 530: {
-            double? value = _single_doubleField66_codec.Read(input);
+            double? value = _single_doubleField66_codec.Read(ref input);
             if (doubleField66_ == null || value != 0D) {
               DoubleField66 = value;
             }
             break;
           }
           case 538: {
-            double? value = _single_doubleField67_codec.Read(input);
+            double? value = _single_doubleField67_codec.Read(ref input);
             if (doubleField67_ == null || value != 0D) {
               DoubleField67 = value;
             }
             break;
           }
           case 546: {
-            double? value = _single_doubleField68_codec.Read(input);
+            double? value = _single_doubleField68_codec.Read(ref input);
             if (doubleField68_ == null || value != 0D) {
               DoubleField68 = value;
             }
             break;
           }
           case 554: {
-            double? value = _single_doubleField69_codec.Read(input);
+            double? value = _single_doubleField69_codec.Read(ref input);
             if (doubleField69_ == null || value != 0D) {
               DoubleField69 = value;
             }
             break;
           }
           case 562: {
-            double? value = _single_doubleField70_codec.Read(input);
+            double? value = _single_doubleField70_codec.Read(ref input);
             if (doubleField70_ == null || value != 0D) {
               DoubleField70 = value;
             }
             break;
           }
           case 570: {
-            double? value = _single_doubleField71_codec.Read(input);
+            double? value = _single_doubleField71_codec.Read(ref input);
             if (doubleField71_ == null || value != 0D) {
               DoubleField71 = value;
             }
             break;
           }
           case 578: {
-            double? value = _single_doubleField72_codec.Read(input);
+            double? value = _single_doubleField72_codec.Read(ref input);
             if (doubleField72_ == null || value != 0D) {
               DoubleField72 = value;
             }
             break;
           }
           case 586: {
-            string value = _single_stringField73_codec.Read(input);
+            string value = _single_stringField73_codec.Read(ref input);
             if (stringField73_ == null || value != "") {
               StringField73 = value;
             }
             break;
           }
           case 594: {
-            string value = _single_stringField74_codec.Read(input);
+            string value = _single_stringField74_codec.Read(ref input);
             if (stringField74_ == null || value != "") {
               StringField74 = value;
             }
             break;
           }
           case 602: {
-            double? value = _single_doubleField75_codec.Read(input);
+            double? value = _single_doubleField75_codec.Read(ref input);
             if (doubleField75_ == null || value != 0D) {
               DoubleField75 = value;
             }
             break;
           }
           case 618: {
-            double? value = _single_doubleField77_codec.Read(input);
+            double? value = _single_doubleField77_codec.Read(ref input);
             if (doubleField77_ == null || value != 0D) {
               DoubleField77 = value;
             }
             break;
           }
           case 626: {
-            double? value = _single_doubleField78_codec.Read(input);
+            double? value = _single_doubleField78_codec.Read(ref input);
             if (doubleField78_ == null || value != 0D) {
               DoubleField78 = value;
             }
             break;
           }
           case 634: {
-            double? value = _single_doubleField79_codec.Read(input);
+            double? value = _single_doubleField79_codec.Read(ref input);
             if (doubleField79_ == null || value != 0D) {
               DoubleField79 = value;
             }
@@ -3745,7 +3750,7 @@ namespace Google.Protobuf.Benchmarks {
             break;
           }
           case 658: {
-            long? value = _single_int64Field82_codec.Read(input);
+            long? value = _single_int64Field82_codec.Read(ref input);
             if (int64Field82_ == null || value != 0L) {
               Int64Field82 = value;
             }
@@ -3756,112 +3761,112 @@ namespace Google.Protobuf.Benchmarks {
             break;
           }
           case 674: {
-            double? value = _single_doubleField84_codec.Read(input);
+            double? value = _single_doubleField84_codec.Read(ref input);
             if (doubleField84_ == null || value != 0D) {
               DoubleField84 = value;
             }
             break;
           }
           case 682: {
-            long? value = _single_int64Field85_codec.Read(input);
+            long? value = _single_int64Field85_codec.Read(ref input);
             if (int64Field85_ == null || value != 0L) {
               Int64Field85 = value;
             }
             break;
           }
           case 690: {
-            long? value = _single_int64Field86_codec.Read(input);
+            long? value = _single_int64Field86_codec.Read(ref input);
             if (int64Field86_ == null || value != 0L) {
               Int64Field86 = value;
             }
             break;
           }
           case 698: {
-            long? value = _single_int64Field87_codec.Read(input);
+            long? value = _single_int64Field87_codec.Read(ref input);
             if (int64Field87_ == null || value != 0L) {
               Int64Field87 = value;
             }
             break;
           }
           case 706: {
-            double? value = _single_doubleField88_codec.Read(input);
+            double? value = _single_doubleField88_codec.Read(ref input);
             if (doubleField88_ == null || value != 0D) {
               DoubleField88 = value;
             }
             break;
           }
           case 714: {
-            double? value = _single_doubleField89_codec.Read(input);
+            double? value = _single_doubleField89_codec.Read(ref input);
             if (doubleField89_ == null || value != 0D) {
               DoubleField89 = value;
             }
             break;
           }
           case 722: {
-            double? value = _single_doubleField90_codec.Read(input);
+            double? value = _single_doubleField90_codec.Read(ref input);
             if (doubleField90_ == null || value != 0D) {
               DoubleField90 = value;
             }
             break;
           }
           case 730: {
-            double? value = _single_doubleField91_codec.Read(input);
+            double? value = _single_doubleField91_codec.Read(ref input);
             if (doubleField91_ == null || value != 0D) {
               DoubleField91 = value;
             }
             break;
           }
           case 738: {
-            double? value = _single_doubleField92_codec.Read(input);
+            double? value = _single_doubleField92_codec.Read(ref input);
             if (doubleField92_ == null || value != 0D) {
               DoubleField92 = value;
             }
             break;
           }
           case 746: {
-            double? value = _single_doubleField93_codec.Read(input);
+            double? value = _single_doubleField93_codec.Read(ref input);
             if (doubleField93_ == null || value != 0D) {
               DoubleField93 = value;
             }
             break;
           }
           case 754: {
-            double? value = _single_doubleField94_codec.Read(input);
+            double? value = _single_doubleField94_codec.Read(ref input);
             if (doubleField94_ == null || value != 0D) {
               DoubleField94 = value;
             }
             break;
           }
           case 762: {
-            double? value = _single_doubleField95_codec.Read(input);
+            double? value = _single_doubleField95_codec.Read(ref input);
             if (doubleField95_ == null || value != 0D) {
               DoubleField95 = value;
             }
             break;
           }
           case 770: {
-            double? value = _single_doubleField96_codec.Read(input);
+            double? value = _single_doubleField96_codec.Read(ref input);
             if (doubleField96_ == null || value != 0D) {
               DoubleField96 = value;
             }
             break;
           }
           case 778: {
-            double? value = _single_doubleField97_codec.Read(input);
+            double? value = _single_doubleField97_codec.Read(ref input);
             if (doubleField97_ == null || value != 0D) {
               DoubleField97 = value;
             }
             break;
           }
           case 786: {
-            double? value = _single_doubleField98_codec.Read(input);
+            double? value = _single_doubleField98_codec.Read(ref input);
             if (doubleField98_ == null || value != 0D) {
               DoubleField98 = value;
             }
             break;
           }
           case 794: {
-            double? value = _single_doubleField99_codec.Read(input);
+            double? value = _single_doubleField99_codec.Read(ref input);
             if (doubleField99_ == null || value != 0D) {
               DoubleField99 = value;
             }
@@ -3869,207 +3874,207 @@ namespace Google.Protobuf.Benchmarks {
           }
           case 802:
           case 800: {
-            repeatedIntField100_.AddEntriesFrom(input, _repeated_repeatedIntField100_codec);
+            repeatedIntField100_.AddEntriesFrom(ref input, _repeated_repeatedIntField100_codec);
             break;
           }
           case 810: {
-            double? value = _single_doubleField101_codec.Read(input);
+            double? value = _single_doubleField101_codec.Read(ref input);
             if (doubleField101_ == null || value != 0D) {
               DoubleField101 = value;
             }
             break;
           }
           case 818: {
-            double? value = _single_doubleField102_codec.Read(input);
+            double? value = _single_doubleField102_codec.Read(ref input);
             if (doubleField102_ == null || value != 0D) {
               DoubleField102 = value;
             }
             break;
           }
           case 826: {
-            double? value = _single_doubleField103_codec.Read(input);
+            double? value = _single_doubleField103_codec.Read(ref input);
             if (doubleField103_ == null || value != 0D) {
               DoubleField103 = value;
             }
             break;
           }
           case 834: {
-            double? value = _single_doubleField104_codec.Read(input);
+            double? value = _single_doubleField104_codec.Read(ref input);
             if (doubleField104_ == null || value != 0D) {
               DoubleField104 = value;
             }
             break;
           }
           case 842: {
-            double? value = _single_doubleField105_codec.Read(input);
+            double? value = _single_doubleField105_codec.Read(ref input);
             if (doubleField105_ == null || value != 0D) {
               DoubleField105 = value;
             }
             break;
           }
           case 850: {
-            double? value = _single_doubleField106_codec.Read(input);
+            double? value = _single_doubleField106_codec.Read(ref input);
             if (doubleField106_ == null || value != 0D) {
               DoubleField106 = value;
             }
             break;
           }
           case 858: {
-            long? value = _single_int64Field107_codec.Read(input);
+            long? value = _single_int64Field107_codec.Read(ref input);
             if (int64Field107_ == null || value != 0L) {
               Int64Field107 = value;
             }
             break;
           }
           case 866: {
-            double? value = _single_doubleField108_codec.Read(input);
+            double? value = _single_doubleField108_codec.Read(ref input);
             if (doubleField108_ == null || value != 0D) {
               DoubleField108 = value;
             }
             break;
           }
           case 874: {
-            double? value = _single_doubleField109_codec.Read(input);
+            double? value = _single_doubleField109_codec.Read(ref input);
             if (doubleField109_ == null || value != 0D) {
               DoubleField109 = value;
             }
             break;
           }
           case 882: {
-            long? value = _single_int64Field110_codec.Read(input);
+            long? value = _single_int64Field110_codec.Read(ref input);
             if (int64Field110_ == null || value != 0L) {
               Int64Field110 = value;
             }
             break;
           }
           case 890: {
-            double? value = _single_doubleField111_codec.Read(input);
+            double? value = _single_doubleField111_codec.Read(ref input);
             if (doubleField111_ == null || value != 0D) {
               DoubleField111 = value;
             }
             break;
           }
           case 898: {
-            long? value = _single_int64Field112_codec.Read(input);
+            long? value = _single_int64Field112_codec.Read(ref input);
             if (int64Field112_ == null || value != 0L) {
               Int64Field112 = value;
             }
             break;
           }
           case 906: {
-            double? value = _single_doubleField113_codec.Read(input);
+            double? value = _single_doubleField113_codec.Read(ref input);
             if (doubleField113_ == null || value != 0D) {
               DoubleField113 = value;
             }
             break;
           }
           case 914: {
-            long? value = _single_int64Field114_codec.Read(input);
+            long? value = _single_int64Field114_codec.Read(ref input);
             if (int64Field114_ == null || value != 0L) {
               Int64Field114 = value;
             }
             break;
           }
           case 922: {
-            long? value = _single_int64Field115_codec.Read(input);
+            long? value = _single_int64Field115_codec.Read(ref input);
             if (int64Field115_ == null || value != 0L) {
               Int64Field115 = value;
             }
             break;
           }
           case 930: {
-            double? value = _single_doubleField116_codec.Read(input);
+            double? value = _single_doubleField116_codec.Read(ref input);
             if (doubleField116_ == null || value != 0D) {
               DoubleField116 = value;
             }
             break;
           }
           case 938: {
-            long? value = _single_int64Field117_codec.Read(input);
+            long? value = _single_int64Field117_codec.Read(ref input);
             if (int64Field117_ == null || value != 0L) {
               Int64Field117 = value;
             }
             break;
           }
           case 946: {
-            double? value = _single_doubleField118_codec.Read(input);
+            double? value = _single_doubleField118_codec.Read(ref input);
             if (doubleField118_ == null || value != 0D) {
               DoubleField118 = value;
             }
             break;
           }
           case 954: {
-            double? value = _single_doubleField119_codec.Read(input);
+            double? value = _single_doubleField119_codec.Read(ref input);
             if (doubleField119_ == null || value != 0D) {
               DoubleField119 = value;
             }
             break;
           }
           case 962: {
-            double? value = _single_doubleField120_codec.Read(input);
+            double? value = _single_doubleField120_codec.Read(ref input);
             if (doubleField120_ == null || value != 0D) {
               DoubleField120 = value;
             }
             break;
           }
           case 970: {
-            double? value = _single_doubleField121_codec.Read(input);
+            double? value = _single_doubleField121_codec.Read(ref input);
             if (doubleField121_ == null || value != 0D) {
               DoubleField121 = value;
             }
             break;
           }
           case 978: {
-            double? value = _single_doubleField122_codec.Read(input);
+            double? value = _single_doubleField122_codec.Read(ref input);
             if (doubleField122_ == null || value != 0D) {
               DoubleField122 = value;
             }
             break;
           }
           case 986: {
-            double? value = _single_doubleField123_codec.Read(input);
+            double? value = _single_doubleField123_codec.Read(ref input);
             if (doubleField123_ == null || value != 0D) {
               DoubleField123 = value;
             }
             break;
           }
           case 994: {
-            double? value = _single_doubleField124_codec.Read(input);
+            double? value = _single_doubleField124_codec.Read(ref input);
             if (doubleField124_ == null || value != 0D) {
               DoubleField124 = value;
             }
             break;
           }
           case 1002: {
-            long? value = _single_int64Field125_codec.Read(input);
+            long? value = _single_int64Field125_codec.Read(ref input);
             if (int64Field125_ == null || value != 0L) {
               Int64Field125 = value;
             }
             break;
           }
           case 1010: {
-            long? value = _single_int64Field126_codec.Read(input);
+            long? value = _single_int64Field126_codec.Read(ref input);
             if (int64Field126_ == null || value != 0L) {
               Int64Field126 = value;
             }
             break;
           }
           case 1018: {
-            long? value = _single_int64Field127_codec.Read(input);
+            long? value = _single_int64Field127_codec.Read(ref input);
             if (int64Field127_ == null || value != 0L) {
               Int64Field127 = value;
             }
             break;
           }
           case 1026: {
-            double? value = _single_doubleField128_codec.Read(input);
+            double? value = _single_doubleField128_codec.Read(ref input);
             if (doubleField128_ == null || value != 0D) {
               DoubleField128 = value;
             }
             break;
           }
           case 1034: {
-            double? value = _single_doubleField129_codec.Read(input);
+            double? value = _single_doubleField129_codec.Read(ref input);
             if (doubleField129_ == null || value != 0D) {
               DoubleField129 = value;
             }
@@ -4085,7 +4090,7 @@ namespace Google.Protobuf.Benchmarks {
   /// same as ManyWrapperFieldsMessages, but with primitive fields
   /// for comparison.
   /// </summary>
-  public sealed partial class ManyPrimitiveFieldsMessage : pb::IMessage<ManyPrimitiveFieldsMessage> {
+  public sealed partial class ManyPrimitiveFieldsMessage : pb::IMessage<ManyPrimitiveFieldsMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ManyPrimitiveFieldsMessage> _parser = new pb::MessageParser<ManyPrimitiveFieldsMessage>(() => new ManyPrimitiveFieldsMessage());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -6830,11 +6835,16 @@ namespace Google.Protobuf.Benchmarks {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 9: {
             DoubleField1 = input.ReadDouble();
@@ -7162,7 +7172,7 @@ namespace Google.Protobuf.Benchmarks {
           }
           case 802:
           case 800: {
-            repeatedIntField100_.AddEntriesFrom(input, _repeated_repeatedIntField100_codec);
+            repeatedIntField100_.AddEntriesFrom(ref input, _repeated_repeatedIntField100_codec);
             break;
           }
           case 809: {

+ 29 - 9
csharp/src/Google.Protobuf.Conformance/Conformance.cs

@@ -107,7 +107,7 @@ namespace Conformance {
   /// This will be known by message_type == "conformance.FailureSet", a conformance
   /// test should return a serialized FailureSet in protobuf_payload.
   /// </summary>
-  public sealed partial class FailureSet : pb::IMessage<FailureSet> {
+  public sealed partial class FailureSet : pb::IMessage<FailureSet>, pb::IBufferMessage {
     private static readonly pb::MessageParser<FailureSet> _parser = new pb::MessageParser<FailureSet>(() => new FailureSet());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -212,14 +212,19 @@ namespace Conformance {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            failure_.AddEntriesFrom(input, _repeated_failure_codec);
+            failure_.AddEntriesFrom(ref input, _repeated_failure_codec);
             break;
           }
         }
@@ -235,7 +240,7 @@ namespace Conformance {
   ///   2. parse the protobuf or JSON payload in "payload" (which may fail)
   ///   3. if the parse succeeded, serialize the message in the requested format.
   /// </summary>
-  public sealed partial class ConformanceRequest : pb::IMessage<ConformanceRequest> {
+  public sealed partial class ConformanceRequest : pb::IMessage<ConformanceRequest>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ConformanceRequest> _parser = new pb::MessageParser<ConformanceRequest>(() => new ConformanceRequest());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -603,11 +608,16 @@ namespace Conformance {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             ProtobufPayload = input.ReadBytes();
@@ -657,7 +667,7 @@ namespace Conformance {
   /// <summary>
   /// Represents a single test case's output.
   /// </summary>
-  public sealed partial class ConformanceResponse : pb::IMessage<ConformanceResponse> {
+  public sealed partial class ConformanceResponse : pb::IMessage<ConformanceResponse>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ConformanceResponse> _parser = new pb::MessageParser<ConformanceResponse>(() => new ConformanceResponse());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1025,11 +1035,16 @@ namespace Conformance {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             ParseError = input.ReadString();
@@ -1072,7 +1087,7 @@ namespace Conformance {
   /// <summary>
   /// Encoding options for jspb format.
   /// </summary>
-  public sealed partial class JspbEncodingConfig : pb::IMessage<JspbEncodingConfig> {
+  public sealed partial class JspbEncodingConfig : pb::IMessage<JspbEncodingConfig>, pb::IBufferMessage {
     private static readonly pb::MessageParser<JspbEncodingConfig> _parser = new pb::MessageParser<JspbEncodingConfig>(() => new JspbEncodingConfig());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1188,11 +1203,16 @@ namespace Conformance {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             UseJspbArrayAnyFormat = input.ReadBool();

+ 1 - 1
csharp/src/Google.Protobuf.Test.TestProtos/Google.Protobuf.Test.TestProtos.csproj

@@ -6,7 +6,7 @@
     and without the internal visibility from the test project (all of which have caused issues in the past).
   -->
   <PropertyGroup>
-    <TargetFrameworks>net45;netstandard1.0;netstandard2.0</TargetFrameworks>
+    <TargetFrameworks>net45;netstandard1.1;netstandard2.0</TargetFrameworks>
     <LangVersion>3.0</LangVersion>
     <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>

+ 86 - 51
csharp/src/Google.Protobuf.Test.TestProtos/MapUnittestProto3.cs

@@ -176,7 +176,7 @@ namespace Google.Protobuf.TestProtos {
   /// <summary>
   /// Tests maps.
   /// </summary>
-  public sealed partial class TestMap : pb::IMessage<TestMap> {
+  public sealed partial class TestMap : pb::IMessage<TestMap>, pb::IBufferMessage {
     private static readonly pb::MessageParser<TestMap> _parser = new pb::MessageParser<TestMap>(() => new TestMap());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -537,78 +537,83 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            mapInt32Int32_.AddEntriesFrom(input, _map_mapInt32Int32_codec);
+            mapInt32Int32_.AddEntriesFrom(ref input, _map_mapInt32Int32_codec);
             break;
           }
           case 18: {
-            mapInt64Int64_.AddEntriesFrom(input, _map_mapInt64Int64_codec);
+            mapInt64Int64_.AddEntriesFrom(ref input, _map_mapInt64Int64_codec);
             break;
           }
           case 26: {
-            mapUint32Uint32_.AddEntriesFrom(input, _map_mapUint32Uint32_codec);
+            mapUint32Uint32_.AddEntriesFrom(ref input, _map_mapUint32Uint32_codec);
             break;
           }
           case 34: {
-            mapUint64Uint64_.AddEntriesFrom(input, _map_mapUint64Uint64_codec);
+            mapUint64Uint64_.AddEntriesFrom(ref input, _map_mapUint64Uint64_codec);
             break;
           }
           case 42: {
-            mapSint32Sint32_.AddEntriesFrom(input, _map_mapSint32Sint32_codec);
+            mapSint32Sint32_.AddEntriesFrom(ref input, _map_mapSint32Sint32_codec);
             break;
           }
           case 50: {
-            mapSint64Sint64_.AddEntriesFrom(input, _map_mapSint64Sint64_codec);
+            mapSint64Sint64_.AddEntriesFrom(ref input, _map_mapSint64Sint64_codec);
             break;
           }
           case 58: {
-            mapFixed32Fixed32_.AddEntriesFrom(input, _map_mapFixed32Fixed32_codec);
+            mapFixed32Fixed32_.AddEntriesFrom(ref input, _map_mapFixed32Fixed32_codec);
             break;
           }
           case 66: {
-            mapFixed64Fixed64_.AddEntriesFrom(input, _map_mapFixed64Fixed64_codec);
+            mapFixed64Fixed64_.AddEntriesFrom(ref input, _map_mapFixed64Fixed64_codec);
             break;
           }
           case 74: {
-            mapSfixed32Sfixed32_.AddEntriesFrom(input, _map_mapSfixed32Sfixed32_codec);
+            mapSfixed32Sfixed32_.AddEntriesFrom(ref input, _map_mapSfixed32Sfixed32_codec);
             break;
           }
           case 82: {
-            mapSfixed64Sfixed64_.AddEntriesFrom(input, _map_mapSfixed64Sfixed64_codec);
+            mapSfixed64Sfixed64_.AddEntriesFrom(ref input, _map_mapSfixed64Sfixed64_codec);
             break;
           }
           case 90: {
-            mapInt32Float_.AddEntriesFrom(input, _map_mapInt32Float_codec);
+            mapInt32Float_.AddEntriesFrom(ref input, _map_mapInt32Float_codec);
             break;
           }
           case 98: {
-            mapInt32Double_.AddEntriesFrom(input, _map_mapInt32Double_codec);
+            mapInt32Double_.AddEntriesFrom(ref input, _map_mapInt32Double_codec);
             break;
           }
           case 106: {
-            mapBoolBool_.AddEntriesFrom(input, _map_mapBoolBool_codec);
+            mapBoolBool_.AddEntriesFrom(ref input, _map_mapBoolBool_codec);
             break;
           }
           case 114: {
-            mapStringString_.AddEntriesFrom(input, _map_mapStringString_codec);
+            mapStringString_.AddEntriesFrom(ref input, _map_mapStringString_codec);
             break;
           }
           case 122: {
-            mapInt32Bytes_.AddEntriesFrom(input, _map_mapInt32Bytes_codec);
+            mapInt32Bytes_.AddEntriesFrom(ref input, _map_mapInt32Bytes_codec);
             break;
           }
           case 130: {
-            mapInt32Enum_.AddEntriesFrom(input, _map_mapInt32Enum_codec);
+            mapInt32Enum_.AddEntriesFrom(ref input, _map_mapInt32Enum_codec);
             break;
           }
           case 138: {
-            mapInt32ForeignMessage_.AddEntriesFrom(input, _map_mapInt32ForeignMessage_codec);
+            mapInt32ForeignMessage_.AddEntriesFrom(ref input, _map_mapInt32ForeignMessage_codec);
             break;
           }
         }
@@ -617,7 +622,7 @@ namespace Google.Protobuf.TestProtos {
 
   }
 
-  public sealed partial class TestMapSubmessage : pb::IMessage<TestMapSubmessage> {
+  public sealed partial class TestMapSubmessage : pb::IMessage<TestMapSubmessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<TestMapSubmessage> _parser = new pb::MessageParser<TestMapSubmessage>(() => new TestMapSubmessage());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -733,11 +738,16 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             if (testMap_ == null) {
@@ -752,7 +762,7 @@ namespace Google.Protobuf.TestProtos {
 
   }
 
-  public sealed partial class TestMessageMap : pb::IMessage<TestMessageMap> {
+  public sealed partial class TestMessageMap : pb::IMessage<TestMessageMap>, pb::IBufferMessage {
     private static readonly pb::MessageParser<TestMessageMap> _parser = new pb::MessageParser<TestMessageMap>(() => new TestMessageMap());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -857,14 +867,19 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            mapInt32Message_.AddEntriesFrom(input, _map_mapInt32Message_codec);
+            mapInt32Message_.AddEntriesFrom(ref input, _map_mapInt32Message_codec);
             break;
           }
         }
@@ -876,7 +891,7 @@ namespace Google.Protobuf.TestProtos {
   /// <summary>
   /// Two map fields share the same entry default instance.
   /// </summary>
-  public sealed partial class TestSameTypeMap : pb::IMessage<TestSameTypeMap> {
+  public sealed partial class TestSameTypeMap : pb::IMessage<TestSameTypeMap>, pb::IBufferMessage {
     private static readonly pb::MessageParser<TestSameTypeMap> _parser = new pb::MessageParser<TestSameTypeMap>(() => new TestSameTypeMap());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -997,18 +1012,23 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            map1_.AddEntriesFrom(input, _map_map1_codec);
+            map1_.AddEntriesFrom(ref input, _map_map1_codec);
             break;
           }
           case 18: {
-            map2_.AddEntriesFrom(input, _map_map2_codec);
+            map2_.AddEntriesFrom(ref input, _map_map2_codec);
             break;
           }
         }
@@ -1017,7 +1037,7 @@ namespace Google.Protobuf.TestProtos {
 
   }
 
-  public sealed partial class TestArenaMap : pb::IMessage<TestArenaMap> {
+  public sealed partial class TestArenaMap : pb::IMessage<TestArenaMap>, pb::IBufferMessage {
     private static readonly pb::MessageParser<TestArenaMap> _parser = new pb::MessageParser<TestArenaMap>(() => new TestArenaMap());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1346,70 +1366,75 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            mapInt32Int32_.AddEntriesFrom(input, _map_mapInt32Int32_codec);
+            mapInt32Int32_.AddEntriesFrom(ref input, _map_mapInt32Int32_codec);
             break;
           }
           case 18: {
-            mapInt64Int64_.AddEntriesFrom(input, _map_mapInt64Int64_codec);
+            mapInt64Int64_.AddEntriesFrom(ref input, _map_mapInt64Int64_codec);
             break;
           }
           case 26: {
-            mapUint32Uint32_.AddEntriesFrom(input, _map_mapUint32Uint32_codec);
+            mapUint32Uint32_.AddEntriesFrom(ref input, _map_mapUint32Uint32_codec);
             break;
           }
           case 34: {
-            mapUint64Uint64_.AddEntriesFrom(input, _map_mapUint64Uint64_codec);
+            mapUint64Uint64_.AddEntriesFrom(ref input, _map_mapUint64Uint64_codec);
             break;
           }
           case 42: {
-            mapSint32Sint32_.AddEntriesFrom(input, _map_mapSint32Sint32_codec);
+            mapSint32Sint32_.AddEntriesFrom(ref input, _map_mapSint32Sint32_codec);
             break;
           }
           case 50: {
-            mapSint64Sint64_.AddEntriesFrom(input, _map_mapSint64Sint64_codec);
+            mapSint64Sint64_.AddEntriesFrom(ref input, _map_mapSint64Sint64_codec);
             break;
           }
           case 58: {
-            mapFixed32Fixed32_.AddEntriesFrom(input, _map_mapFixed32Fixed32_codec);
+            mapFixed32Fixed32_.AddEntriesFrom(ref input, _map_mapFixed32Fixed32_codec);
             break;
           }
           case 66: {
-            mapFixed64Fixed64_.AddEntriesFrom(input, _map_mapFixed64Fixed64_codec);
+            mapFixed64Fixed64_.AddEntriesFrom(ref input, _map_mapFixed64Fixed64_codec);
             break;
           }
           case 74: {
-            mapSfixed32Sfixed32_.AddEntriesFrom(input, _map_mapSfixed32Sfixed32_codec);
+            mapSfixed32Sfixed32_.AddEntriesFrom(ref input, _map_mapSfixed32Sfixed32_codec);
             break;
           }
           case 82: {
-            mapSfixed64Sfixed64_.AddEntriesFrom(input, _map_mapSfixed64Sfixed64_codec);
+            mapSfixed64Sfixed64_.AddEntriesFrom(ref input, _map_mapSfixed64Sfixed64_codec);
             break;
           }
           case 90: {
-            mapInt32Float_.AddEntriesFrom(input, _map_mapInt32Float_codec);
+            mapInt32Float_.AddEntriesFrom(ref input, _map_mapInt32Float_codec);
             break;
           }
           case 98: {
-            mapInt32Double_.AddEntriesFrom(input, _map_mapInt32Double_codec);
+            mapInt32Double_.AddEntriesFrom(ref input, _map_mapInt32Double_codec);
             break;
           }
           case 106: {
-            mapBoolBool_.AddEntriesFrom(input, _map_mapBoolBool_codec);
+            mapBoolBool_.AddEntriesFrom(ref input, _map_mapBoolBool_codec);
             break;
           }
           case 114: {
-            mapInt32Enum_.AddEntriesFrom(input, _map_mapInt32Enum_codec);
+            mapInt32Enum_.AddEntriesFrom(ref input, _map_mapInt32Enum_codec);
             break;
           }
           case 122: {
-            mapInt32ForeignMessage_.AddEntriesFrom(input, _map_mapInt32ForeignMessage_codec);
+            mapInt32ForeignMessage_.AddEntriesFrom(ref input, _map_mapInt32ForeignMessage_codec);
             break;
           }
         }
@@ -1422,7 +1447,7 @@ namespace Google.Protobuf.TestProtos {
   /// Previously, message containing enum called Type cannot be used as value of
   /// map field.
   /// </summary>
-  public sealed partial class MessageContainingEnumCalledType : pb::IMessage<MessageContainingEnumCalledType> {
+  public sealed partial class MessageContainingEnumCalledType : pb::IMessage<MessageContainingEnumCalledType>, pb::IBufferMessage {
     private static readonly pb::MessageParser<MessageContainingEnumCalledType> _parser = new pb::MessageParser<MessageContainingEnumCalledType>(() => new MessageContainingEnumCalledType());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1527,14 +1552,19 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            type_.AddEntriesFrom(input, _map_type_codec);
+            type_.AddEntriesFrom(ref input, _map_type_codec);
             break;
           }
         }
@@ -1557,7 +1587,7 @@ namespace Google.Protobuf.TestProtos {
   /// <summary>
   /// Previously, message cannot contain map field called "entry".
   /// </summary>
-  public sealed partial class MessageContainingMapCalledEntry : pb::IMessage<MessageContainingMapCalledEntry> {
+  public sealed partial class MessageContainingMapCalledEntry : pb::IMessage<MessageContainingMapCalledEntry>, pb::IBufferMessage {
     private static readonly pb::MessageParser<MessageContainingMapCalledEntry> _parser = new pb::MessageParser<MessageContainingMapCalledEntry>(() => new MessageContainingMapCalledEntry());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1662,14 +1692,19 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            entry_.AddEntriesFrom(input, _map_entry_codec);
+            entry_.AddEntriesFrom(ref input, _map_entry_codec);
             break;
           }
         }

+ 134 - 89
csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs

@@ -244,7 +244,7 @@ namespace ProtobufTestMessages.Proto2 {
   /// could trigger bugs that occur in any message type in this file.  We verify
   /// this stays true in a unit test.
   /// </summary>
-  public sealed partial class TestAllTypesProto2 : pb::IExtendableMessage<TestAllTypesProto2> {
+  public sealed partial class TestAllTypesProto2 : pb::IExtendableMessage<TestAllTypesProto2>, pb::IBufferMessage {
     private static readonly pb::MessageParser<TestAllTypesProto2> _parser = new pb::MessageParser<TestAllTypesProto2>(() => new TestAllTypesProto2());
     private pb::UnknownFieldSet _unknownFields;
     private pb::ExtensionSet<TestAllTypesProto2> _extensions;
@@ -3311,12 +3311,17 @@ namespace ProtobufTestMessages.Proto2 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
-              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             }
             break;
           case 8: {
@@ -3418,317 +3423,317 @@ namespace ProtobufTestMessages.Proto2 {
           }
           case 250:
           case 248: {
-            repeatedInt32_.AddEntriesFrom(input, _repeated_repeatedInt32_codec);
+            repeatedInt32_.AddEntriesFrom(ref input, _repeated_repeatedInt32_codec);
             break;
           }
           case 258:
           case 256: {
-            repeatedInt64_.AddEntriesFrom(input, _repeated_repeatedInt64_codec);
+            repeatedInt64_.AddEntriesFrom(ref input, _repeated_repeatedInt64_codec);
             break;
           }
           case 266:
           case 264: {
-            repeatedUint32_.AddEntriesFrom(input, _repeated_repeatedUint32_codec);
+            repeatedUint32_.AddEntriesFrom(ref input, _repeated_repeatedUint32_codec);
             break;
           }
           case 274:
           case 272: {
-            repeatedUint64_.AddEntriesFrom(input, _repeated_repeatedUint64_codec);
+            repeatedUint64_.AddEntriesFrom(ref input, _repeated_repeatedUint64_codec);
             break;
           }
           case 282:
           case 280: {
-            repeatedSint32_.AddEntriesFrom(input, _repeated_repeatedSint32_codec);
+            repeatedSint32_.AddEntriesFrom(ref input, _repeated_repeatedSint32_codec);
             break;
           }
           case 290:
           case 288: {
-            repeatedSint64_.AddEntriesFrom(input, _repeated_repeatedSint64_codec);
+            repeatedSint64_.AddEntriesFrom(ref input, _repeated_repeatedSint64_codec);
             break;
           }
           case 298:
           case 301: {
-            repeatedFixed32_.AddEntriesFrom(input, _repeated_repeatedFixed32_codec);
+            repeatedFixed32_.AddEntriesFrom(ref input, _repeated_repeatedFixed32_codec);
             break;
           }
           case 306:
           case 305: {
-            repeatedFixed64_.AddEntriesFrom(input, _repeated_repeatedFixed64_codec);
+            repeatedFixed64_.AddEntriesFrom(ref input, _repeated_repeatedFixed64_codec);
             break;
           }
           case 314:
           case 317: {
-            repeatedSfixed32_.AddEntriesFrom(input, _repeated_repeatedSfixed32_codec);
+            repeatedSfixed32_.AddEntriesFrom(ref input, _repeated_repeatedSfixed32_codec);
             break;
           }
           case 322:
           case 321: {
-            repeatedSfixed64_.AddEntriesFrom(input, _repeated_repeatedSfixed64_codec);
+            repeatedSfixed64_.AddEntriesFrom(ref input, _repeated_repeatedSfixed64_codec);
             break;
           }
           case 330:
           case 333: {
-            repeatedFloat_.AddEntriesFrom(input, _repeated_repeatedFloat_codec);
+            repeatedFloat_.AddEntriesFrom(ref input, _repeated_repeatedFloat_codec);
             break;
           }
           case 338:
           case 337: {
-            repeatedDouble_.AddEntriesFrom(input, _repeated_repeatedDouble_codec);
+            repeatedDouble_.AddEntriesFrom(ref input, _repeated_repeatedDouble_codec);
             break;
           }
           case 346:
           case 344: {
-            repeatedBool_.AddEntriesFrom(input, _repeated_repeatedBool_codec);
+            repeatedBool_.AddEntriesFrom(ref input, _repeated_repeatedBool_codec);
             break;
           }
           case 354: {
-            repeatedString_.AddEntriesFrom(input, _repeated_repeatedString_codec);
+            repeatedString_.AddEntriesFrom(ref input, _repeated_repeatedString_codec);
             break;
           }
           case 362: {
-            repeatedBytes_.AddEntriesFrom(input, _repeated_repeatedBytes_codec);
+            repeatedBytes_.AddEntriesFrom(ref input, _repeated_repeatedBytes_codec);
             break;
           }
           case 386: {
-            repeatedNestedMessage_.AddEntriesFrom(input, _repeated_repeatedNestedMessage_codec);
+            repeatedNestedMessage_.AddEntriesFrom(ref input, _repeated_repeatedNestedMessage_codec);
             break;
           }
           case 394: {
-            repeatedForeignMessage_.AddEntriesFrom(input, _repeated_repeatedForeignMessage_codec);
+            repeatedForeignMessage_.AddEntriesFrom(ref input, _repeated_repeatedForeignMessage_codec);
             break;
           }
           case 410:
           case 408: {
-            repeatedNestedEnum_.AddEntriesFrom(input, _repeated_repeatedNestedEnum_codec);
+            repeatedNestedEnum_.AddEntriesFrom(ref input, _repeated_repeatedNestedEnum_codec);
             break;
           }
           case 418:
           case 416: {
-            repeatedForeignEnum_.AddEntriesFrom(input, _repeated_repeatedForeignEnum_codec);
+            repeatedForeignEnum_.AddEntriesFrom(ref input, _repeated_repeatedForeignEnum_codec);
             break;
           }
           case 434: {
-            repeatedStringPiece_.AddEntriesFrom(input, _repeated_repeatedStringPiece_codec);
+            repeatedStringPiece_.AddEntriesFrom(ref input, _repeated_repeatedStringPiece_codec);
             break;
           }
           case 442: {
-            repeatedCord_.AddEntriesFrom(input, _repeated_repeatedCord_codec);
+            repeatedCord_.AddEntriesFrom(ref input, _repeated_repeatedCord_codec);
             break;
           }
           case 450: {
-            mapInt32Int32_.AddEntriesFrom(input, _map_mapInt32Int32_codec);
+            mapInt32Int32_.AddEntriesFrom(ref input, _map_mapInt32Int32_codec);
             break;
           }
           case 458: {
-            mapInt64Int64_.AddEntriesFrom(input, _map_mapInt64Int64_codec);
+            mapInt64Int64_.AddEntriesFrom(ref input, _map_mapInt64Int64_codec);
             break;
           }
           case 466: {
-            mapUint32Uint32_.AddEntriesFrom(input, _map_mapUint32Uint32_codec);
+            mapUint32Uint32_.AddEntriesFrom(ref input, _map_mapUint32Uint32_codec);
             break;
           }
           case 474: {
-            mapUint64Uint64_.AddEntriesFrom(input, _map_mapUint64Uint64_codec);
+            mapUint64Uint64_.AddEntriesFrom(ref input, _map_mapUint64Uint64_codec);
             break;
           }
           case 482: {
-            mapSint32Sint32_.AddEntriesFrom(input, _map_mapSint32Sint32_codec);
+            mapSint32Sint32_.AddEntriesFrom(ref input, _map_mapSint32Sint32_codec);
             break;
           }
           case 490: {
-            mapSint64Sint64_.AddEntriesFrom(input, _map_mapSint64Sint64_codec);
+            mapSint64Sint64_.AddEntriesFrom(ref input, _map_mapSint64Sint64_codec);
             break;
           }
           case 498: {
-            mapFixed32Fixed32_.AddEntriesFrom(input, _map_mapFixed32Fixed32_codec);
+            mapFixed32Fixed32_.AddEntriesFrom(ref input, _map_mapFixed32Fixed32_codec);
             break;
           }
           case 506: {
-            mapFixed64Fixed64_.AddEntriesFrom(input, _map_mapFixed64Fixed64_codec);
+            mapFixed64Fixed64_.AddEntriesFrom(ref input, _map_mapFixed64Fixed64_codec);
             break;
           }
           case 514: {
-            mapSfixed32Sfixed32_.AddEntriesFrom(input, _map_mapSfixed32Sfixed32_codec);
+            mapSfixed32Sfixed32_.AddEntriesFrom(ref input, _map_mapSfixed32Sfixed32_codec);
             break;
           }
           case 522: {
-            mapSfixed64Sfixed64_.AddEntriesFrom(input, _map_mapSfixed64Sfixed64_codec);
+            mapSfixed64Sfixed64_.AddEntriesFrom(ref input, _map_mapSfixed64Sfixed64_codec);
             break;
           }
           case 530: {
-            mapInt32Float_.AddEntriesFrom(input, _map_mapInt32Float_codec);
+            mapInt32Float_.AddEntriesFrom(ref input, _map_mapInt32Float_codec);
             break;
           }
           case 538: {
-            mapInt32Double_.AddEntriesFrom(input, _map_mapInt32Double_codec);
+            mapInt32Double_.AddEntriesFrom(ref input, _map_mapInt32Double_codec);
             break;
           }
           case 546: {
-            mapBoolBool_.AddEntriesFrom(input, _map_mapBoolBool_codec);
+            mapBoolBool_.AddEntriesFrom(ref input, _map_mapBoolBool_codec);
             break;
           }
           case 554: {
-            mapStringString_.AddEntriesFrom(input, _map_mapStringString_codec);
+            mapStringString_.AddEntriesFrom(ref input, _map_mapStringString_codec);
             break;
           }
           case 562: {
-            mapStringBytes_.AddEntriesFrom(input, _map_mapStringBytes_codec);
+            mapStringBytes_.AddEntriesFrom(ref input, _map_mapStringBytes_codec);
             break;
           }
           case 570: {
-            mapStringNestedMessage_.AddEntriesFrom(input, _map_mapStringNestedMessage_codec);
+            mapStringNestedMessage_.AddEntriesFrom(ref input, _map_mapStringNestedMessage_codec);
             break;
           }
           case 578: {
-            mapStringForeignMessage_.AddEntriesFrom(input, _map_mapStringForeignMessage_codec);
+            mapStringForeignMessage_.AddEntriesFrom(ref input, _map_mapStringForeignMessage_codec);
             break;
           }
           case 586: {
-            mapStringNestedEnum_.AddEntriesFrom(input, _map_mapStringNestedEnum_codec);
+            mapStringNestedEnum_.AddEntriesFrom(ref input, _map_mapStringNestedEnum_codec);
             break;
           }
           case 594: {
-            mapStringForeignEnum_.AddEntriesFrom(input, _map_mapStringForeignEnum_codec);
+            mapStringForeignEnum_.AddEntriesFrom(ref input, _map_mapStringForeignEnum_codec);
             break;
           }
           case 602:
           case 600: {
-            packedInt32_.AddEntriesFrom(input, _repeated_packedInt32_codec);
+            packedInt32_.AddEntriesFrom(ref input, _repeated_packedInt32_codec);
             break;
           }
           case 610:
           case 608: {
-            packedInt64_.AddEntriesFrom(input, _repeated_packedInt64_codec);
+            packedInt64_.AddEntriesFrom(ref input, _repeated_packedInt64_codec);
             break;
           }
           case 618:
           case 616: {
-            packedUint32_.AddEntriesFrom(input, _repeated_packedUint32_codec);
+            packedUint32_.AddEntriesFrom(ref input, _repeated_packedUint32_codec);
             break;
           }
           case 626:
           case 624: {
-            packedUint64_.AddEntriesFrom(input, _repeated_packedUint64_codec);
+            packedUint64_.AddEntriesFrom(ref input, _repeated_packedUint64_codec);
             break;
           }
           case 634:
           case 632: {
-            packedSint32_.AddEntriesFrom(input, _repeated_packedSint32_codec);
+            packedSint32_.AddEntriesFrom(ref input, _repeated_packedSint32_codec);
             break;
           }
           case 642:
           case 640: {
-            packedSint64_.AddEntriesFrom(input, _repeated_packedSint64_codec);
+            packedSint64_.AddEntriesFrom(ref input, _repeated_packedSint64_codec);
             break;
           }
           case 650:
           case 653: {
-            packedFixed32_.AddEntriesFrom(input, _repeated_packedFixed32_codec);
+            packedFixed32_.AddEntriesFrom(ref input, _repeated_packedFixed32_codec);
             break;
           }
           case 658:
           case 657: {
-            packedFixed64_.AddEntriesFrom(input, _repeated_packedFixed64_codec);
+            packedFixed64_.AddEntriesFrom(ref input, _repeated_packedFixed64_codec);
             break;
           }
           case 666:
           case 669: {
-            packedSfixed32_.AddEntriesFrom(input, _repeated_packedSfixed32_codec);
+            packedSfixed32_.AddEntriesFrom(ref input, _repeated_packedSfixed32_codec);
             break;
           }
           case 674:
           case 673: {
-            packedSfixed64_.AddEntriesFrom(input, _repeated_packedSfixed64_codec);
+            packedSfixed64_.AddEntriesFrom(ref input, _repeated_packedSfixed64_codec);
             break;
           }
           case 682:
           case 685: {
-            packedFloat_.AddEntriesFrom(input, _repeated_packedFloat_codec);
+            packedFloat_.AddEntriesFrom(ref input, _repeated_packedFloat_codec);
             break;
           }
           case 690:
           case 689: {
-            packedDouble_.AddEntriesFrom(input, _repeated_packedDouble_codec);
+            packedDouble_.AddEntriesFrom(ref input, _repeated_packedDouble_codec);
             break;
           }
           case 698:
           case 696: {
-            packedBool_.AddEntriesFrom(input, _repeated_packedBool_codec);
+            packedBool_.AddEntriesFrom(ref input, _repeated_packedBool_codec);
             break;
           }
           case 706:
           case 704: {
-            packedNestedEnum_.AddEntriesFrom(input, _repeated_packedNestedEnum_codec);
+            packedNestedEnum_.AddEntriesFrom(ref input, _repeated_packedNestedEnum_codec);
             break;
           }
           case 714:
           case 712: {
-            unpackedInt32_.AddEntriesFrom(input, _repeated_unpackedInt32_codec);
+            unpackedInt32_.AddEntriesFrom(ref input, _repeated_unpackedInt32_codec);
             break;
           }
           case 722:
           case 720: {
-            unpackedInt64_.AddEntriesFrom(input, _repeated_unpackedInt64_codec);
+            unpackedInt64_.AddEntriesFrom(ref input, _repeated_unpackedInt64_codec);
             break;
           }
           case 730:
           case 728: {
-            unpackedUint32_.AddEntriesFrom(input, _repeated_unpackedUint32_codec);
+            unpackedUint32_.AddEntriesFrom(ref input, _repeated_unpackedUint32_codec);
             break;
           }
           case 738:
           case 736: {
-            unpackedUint64_.AddEntriesFrom(input, _repeated_unpackedUint64_codec);
+            unpackedUint64_.AddEntriesFrom(ref input, _repeated_unpackedUint64_codec);
             break;
           }
           case 746:
           case 744: {
-            unpackedSint32_.AddEntriesFrom(input, _repeated_unpackedSint32_codec);
+            unpackedSint32_.AddEntriesFrom(ref input, _repeated_unpackedSint32_codec);
             break;
           }
           case 754:
           case 752: {
-            unpackedSint64_.AddEntriesFrom(input, _repeated_unpackedSint64_codec);
+            unpackedSint64_.AddEntriesFrom(ref input, _repeated_unpackedSint64_codec);
             break;
           }
           case 762:
           case 765: {
-            unpackedFixed32_.AddEntriesFrom(input, _repeated_unpackedFixed32_codec);
+            unpackedFixed32_.AddEntriesFrom(ref input, _repeated_unpackedFixed32_codec);
             break;
           }
           case 770:
           case 769: {
-            unpackedFixed64_.AddEntriesFrom(input, _repeated_unpackedFixed64_codec);
+            unpackedFixed64_.AddEntriesFrom(ref input, _repeated_unpackedFixed64_codec);
             break;
           }
           case 778:
           case 781: {
-            unpackedSfixed32_.AddEntriesFrom(input, _repeated_unpackedSfixed32_codec);
+            unpackedSfixed32_.AddEntriesFrom(ref input, _repeated_unpackedSfixed32_codec);
             break;
           }
           case 786:
           case 785: {
-            unpackedSfixed64_.AddEntriesFrom(input, _repeated_unpackedSfixed64_codec);
+            unpackedSfixed64_.AddEntriesFrom(ref input, _repeated_unpackedSfixed64_codec);
             break;
           }
           case 794:
           case 797: {
-            unpackedFloat_.AddEntriesFrom(input, _repeated_unpackedFloat_codec);
+            unpackedFloat_.AddEntriesFrom(ref input, _repeated_unpackedFloat_codec);
             break;
           }
           case 802:
           case 801: {
-            unpackedDouble_.AddEntriesFrom(input, _repeated_unpackedDouble_codec);
+            unpackedDouble_.AddEntriesFrom(ref input, _repeated_unpackedDouble_codec);
             break;
           }
           case 810:
           case 808: {
-            unpackedBool_.AddEntriesFrom(input, _repeated_unpackedBool_codec);
+            unpackedBool_.AddEntriesFrom(ref input, _repeated_unpackedBool_codec);
             break;
           }
           case 818:
           case 816: {
-            unpackedNestedEnum_.AddEntriesFrom(input, _repeated_unpackedNestedEnum_codec);
+            unpackedNestedEnum_.AddEntriesFrom(ref input, _repeated_unpackedNestedEnum_codec);
             break;
           }
           case 888: {
@@ -3892,7 +3897,7 @@ namespace ProtobufTestMessages.Proto2 {
         [pbr::OriginalName("NEG")] Neg = -1,
       }
 
-      public sealed partial class NestedMessage : pb::IMessage<NestedMessage> {
+      public sealed partial class NestedMessage : pb::IMessage<NestedMessage>, pb::IBufferMessage {
         private static readonly pb::MessageParser<NestedMessage> _parser = new pb::MessageParser<NestedMessage>(() => new NestedMessage());
         private pb::UnknownFieldSet _unknownFields;
         private int _hasBits0;
@@ -4047,11 +4052,16 @@ namespace ProtobufTestMessages.Proto2 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 8: {
                 A = input.ReadInt32();
@@ -4073,7 +4083,7 @@ namespace ProtobufTestMessages.Proto2 {
       /// <summary>
       /// groups
       /// </summary>
-      public sealed partial class Data : pb::IMessage<Data> {
+      public sealed partial class Data : pb::IMessage<Data>, pb::IBufferMessage {
         private static readonly pb::MessageParser<Data> _parser = new pb::MessageParser<Data>(() => new Data());
         private pb::UnknownFieldSet _unknownFields;
         private int _hasBits0;
@@ -4238,13 +4248,18 @@ namespace ProtobufTestMessages.Proto2 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               case 1612:
                 return;
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 1616: {
                 GroupInt32 = input.ReadInt32();
@@ -4263,7 +4278,7 @@ namespace ProtobufTestMessages.Proto2 {
       /// <summary>
       /// message_set test case.
       /// </summary>
-      public sealed partial class MessageSetCorrect : pb::IExtendableMessage<MessageSetCorrect> {
+      public sealed partial class MessageSetCorrect : pb::IExtendableMessage<MessageSetCorrect>, pb::IBufferMessage {
         private static readonly pb::MessageParser<MessageSetCorrect> _parser = new pb::MessageParser<MessageSetCorrect>(() => new MessageSetCorrect());
         private pb::UnknownFieldSet _unknownFields;
         private pb::ExtensionSet<MessageSetCorrect> _extensions;
@@ -4368,12 +4383,17 @@ namespace ProtobufTestMessages.Proto2 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
-                  _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+                  _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 }
                 break;
             }
@@ -4404,7 +4424,7 @@ namespace ProtobufTestMessages.Proto2 {
 
       }
 
-      public sealed partial class MessageSetCorrectExtension1 : pb::IMessage<MessageSetCorrectExtension1> {
+      public sealed partial class MessageSetCorrectExtension1 : pb::IMessage<MessageSetCorrectExtension1>, pb::IBufferMessage {
         private static readonly pb::MessageParser<MessageSetCorrectExtension1> _parser = new pb::MessageParser<MessageSetCorrectExtension1>(() => new MessageSetCorrectExtension1());
         private pb::UnknownFieldSet _unknownFields;
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -4529,11 +4549,16 @@ namespace ProtobufTestMessages.Proto2 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 202: {
                 Str = input.ReadString();
@@ -4554,7 +4579,7 @@ namespace ProtobufTestMessages.Proto2 {
 
       }
 
-      public sealed partial class MessageSetCorrectExtension2 : pb::IMessage<MessageSetCorrectExtension2> {
+      public sealed partial class MessageSetCorrectExtension2 : pb::IMessage<MessageSetCorrectExtension2>, pb::IBufferMessage {
         private static readonly pb::MessageParser<MessageSetCorrectExtension2> _parser = new pb::MessageParser<MessageSetCorrectExtension2>(() => new MessageSetCorrectExtension2());
         private pb::UnknownFieldSet _unknownFields;
         private int _hasBits0;
@@ -4682,11 +4707,16 @@ namespace ProtobufTestMessages.Proto2 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 72: {
                 I = input.ReadInt32();
@@ -4712,7 +4742,7 @@ namespace ProtobufTestMessages.Proto2 {
 
   }
 
-  public sealed partial class ForeignMessageProto2 : pb::IMessage<ForeignMessageProto2> {
+  public sealed partial class ForeignMessageProto2 : pb::IMessage<ForeignMessageProto2>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ForeignMessageProto2> _parser = new pb::MessageParser<ForeignMessageProto2>(() => new ForeignMessageProto2());
     private pb::UnknownFieldSet _unknownFields;
     private int _hasBits0;
@@ -4840,11 +4870,16 @@ namespace ProtobufTestMessages.Proto2 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             C = input.ReadInt32();
@@ -4856,7 +4891,7 @@ namespace ProtobufTestMessages.Proto2 {
 
   }
 
-  public sealed partial class UnknownToTestAllTypes : pb::IMessage<UnknownToTestAllTypes> {
+  public sealed partial class UnknownToTestAllTypes : pb::IMessage<UnknownToTestAllTypes>, pb::IBufferMessage {
     private static readonly pb::MessageParser<UnknownToTestAllTypes> _parser = new pb::MessageParser<UnknownToTestAllTypes>(() => new UnknownToTestAllTypes());
     private pb::UnknownFieldSet _unknownFields;
     private int _hasBits0;
@@ -5138,11 +5173,16 @@ namespace ProtobufTestMessages.Proto2 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8008: {
             OptionalInt32 = input.ReadInt32();
@@ -5172,7 +5212,7 @@ namespace ProtobufTestMessages.Proto2 {
           }
           case 8090:
           case 8088: {
-            repeatedInt32_.AddEntriesFrom(input, _repeated_repeatedInt32_codec);
+            repeatedInt32_.AddEntriesFrom(ref input, _repeated_repeatedInt32_codec);
             break;
           }
         }
@@ -5183,7 +5223,7 @@ namespace ProtobufTestMessages.Proto2 {
     /// <summary>Container for nested types declared in the UnknownToTestAllTypes message type.</summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static partial class Types {
-      public sealed partial class OptionalGroup : pb::IMessage<OptionalGroup> {
+      public sealed partial class OptionalGroup : pb::IMessage<OptionalGroup>, pb::IBufferMessage {
         private static readonly pb::MessageParser<OptionalGroup> _parser = new pb::MessageParser<OptionalGroup>(() => new OptionalGroup());
         private pb::UnknownFieldSet _unknownFields;
         private int _hasBits0;
@@ -5311,13 +5351,18 @@ namespace ProtobufTestMessages.Proto2 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               case 8036:
                 return;
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 8: {
                 A = input.ReadInt32();

+ 114 - 99
csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs

@@ -259,7 +259,7 @@ namespace ProtobufTestMessages.Proto3 {
   /// could trigger bugs that occur in any message type in this file.  We verify
   /// this stays true in a unit test.
   /// </summary>
-  public sealed partial class TestAllTypesProto3 : pb::IMessage<TestAllTypesProto3> {
+  public sealed partial class TestAllTypesProto3 : pb::IMessage<TestAllTypesProto3>, pb::IBufferMessage {
     private static readonly pb::MessageParser<TestAllTypesProto3> _parser = new pb::MessageParser<TestAllTypesProto3>(() => new TestAllTypesProto3());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -3383,11 +3383,16 @@ namespace ProtobufTestMessages.Proto3 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             OptionalInt32 = input.ReadInt32();
@@ -3492,317 +3497,317 @@ namespace ProtobufTestMessages.Proto3 {
           }
           case 250:
           case 248: {
-            repeatedInt32_.AddEntriesFrom(input, _repeated_repeatedInt32_codec);
+            repeatedInt32_.AddEntriesFrom(ref input, _repeated_repeatedInt32_codec);
             break;
           }
           case 258:
           case 256: {
-            repeatedInt64_.AddEntriesFrom(input, _repeated_repeatedInt64_codec);
+            repeatedInt64_.AddEntriesFrom(ref input, _repeated_repeatedInt64_codec);
             break;
           }
           case 266:
           case 264: {
-            repeatedUint32_.AddEntriesFrom(input, _repeated_repeatedUint32_codec);
+            repeatedUint32_.AddEntriesFrom(ref input, _repeated_repeatedUint32_codec);
             break;
           }
           case 274:
           case 272: {
-            repeatedUint64_.AddEntriesFrom(input, _repeated_repeatedUint64_codec);
+            repeatedUint64_.AddEntriesFrom(ref input, _repeated_repeatedUint64_codec);
             break;
           }
           case 282:
           case 280: {
-            repeatedSint32_.AddEntriesFrom(input, _repeated_repeatedSint32_codec);
+            repeatedSint32_.AddEntriesFrom(ref input, _repeated_repeatedSint32_codec);
             break;
           }
           case 290:
           case 288: {
-            repeatedSint64_.AddEntriesFrom(input, _repeated_repeatedSint64_codec);
+            repeatedSint64_.AddEntriesFrom(ref input, _repeated_repeatedSint64_codec);
             break;
           }
           case 298:
           case 301: {
-            repeatedFixed32_.AddEntriesFrom(input, _repeated_repeatedFixed32_codec);
+            repeatedFixed32_.AddEntriesFrom(ref input, _repeated_repeatedFixed32_codec);
             break;
           }
           case 306:
           case 305: {
-            repeatedFixed64_.AddEntriesFrom(input, _repeated_repeatedFixed64_codec);
+            repeatedFixed64_.AddEntriesFrom(ref input, _repeated_repeatedFixed64_codec);
             break;
           }
           case 314:
           case 317: {
-            repeatedSfixed32_.AddEntriesFrom(input, _repeated_repeatedSfixed32_codec);
+            repeatedSfixed32_.AddEntriesFrom(ref input, _repeated_repeatedSfixed32_codec);
             break;
           }
           case 322:
           case 321: {
-            repeatedSfixed64_.AddEntriesFrom(input, _repeated_repeatedSfixed64_codec);
+            repeatedSfixed64_.AddEntriesFrom(ref input, _repeated_repeatedSfixed64_codec);
             break;
           }
           case 330:
           case 333: {
-            repeatedFloat_.AddEntriesFrom(input, _repeated_repeatedFloat_codec);
+            repeatedFloat_.AddEntriesFrom(ref input, _repeated_repeatedFloat_codec);
             break;
           }
           case 338:
           case 337: {
-            repeatedDouble_.AddEntriesFrom(input, _repeated_repeatedDouble_codec);
+            repeatedDouble_.AddEntriesFrom(ref input, _repeated_repeatedDouble_codec);
             break;
           }
           case 346:
           case 344: {
-            repeatedBool_.AddEntriesFrom(input, _repeated_repeatedBool_codec);
+            repeatedBool_.AddEntriesFrom(ref input, _repeated_repeatedBool_codec);
             break;
           }
           case 354: {
-            repeatedString_.AddEntriesFrom(input, _repeated_repeatedString_codec);
+            repeatedString_.AddEntriesFrom(ref input, _repeated_repeatedString_codec);
             break;
           }
           case 362: {
-            repeatedBytes_.AddEntriesFrom(input, _repeated_repeatedBytes_codec);
+            repeatedBytes_.AddEntriesFrom(ref input, _repeated_repeatedBytes_codec);
             break;
           }
           case 386: {
-            repeatedNestedMessage_.AddEntriesFrom(input, _repeated_repeatedNestedMessage_codec);
+            repeatedNestedMessage_.AddEntriesFrom(ref input, _repeated_repeatedNestedMessage_codec);
             break;
           }
           case 394: {
-            repeatedForeignMessage_.AddEntriesFrom(input, _repeated_repeatedForeignMessage_codec);
+            repeatedForeignMessage_.AddEntriesFrom(ref input, _repeated_repeatedForeignMessage_codec);
             break;
           }
           case 410:
           case 408: {
-            repeatedNestedEnum_.AddEntriesFrom(input, _repeated_repeatedNestedEnum_codec);
+            repeatedNestedEnum_.AddEntriesFrom(ref input, _repeated_repeatedNestedEnum_codec);
             break;
           }
           case 418:
           case 416: {
-            repeatedForeignEnum_.AddEntriesFrom(input, _repeated_repeatedForeignEnum_codec);
+            repeatedForeignEnum_.AddEntriesFrom(ref input, _repeated_repeatedForeignEnum_codec);
             break;
           }
           case 434: {
-            repeatedStringPiece_.AddEntriesFrom(input, _repeated_repeatedStringPiece_codec);
+            repeatedStringPiece_.AddEntriesFrom(ref input, _repeated_repeatedStringPiece_codec);
             break;
           }
           case 442: {
-            repeatedCord_.AddEntriesFrom(input, _repeated_repeatedCord_codec);
+            repeatedCord_.AddEntriesFrom(ref input, _repeated_repeatedCord_codec);
             break;
           }
           case 450: {
-            mapInt32Int32_.AddEntriesFrom(input, _map_mapInt32Int32_codec);
+            mapInt32Int32_.AddEntriesFrom(ref input, _map_mapInt32Int32_codec);
             break;
           }
           case 458: {
-            mapInt64Int64_.AddEntriesFrom(input, _map_mapInt64Int64_codec);
+            mapInt64Int64_.AddEntriesFrom(ref input, _map_mapInt64Int64_codec);
             break;
           }
           case 466: {
-            mapUint32Uint32_.AddEntriesFrom(input, _map_mapUint32Uint32_codec);
+            mapUint32Uint32_.AddEntriesFrom(ref input, _map_mapUint32Uint32_codec);
             break;
           }
           case 474: {
-            mapUint64Uint64_.AddEntriesFrom(input, _map_mapUint64Uint64_codec);
+            mapUint64Uint64_.AddEntriesFrom(ref input, _map_mapUint64Uint64_codec);
             break;
           }
           case 482: {
-            mapSint32Sint32_.AddEntriesFrom(input, _map_mapSint32Sint32_codec);
+            mapSint32Sint32_.AddEntriesFrom(ref input, _map_mapSint32Sint32_codec);
             break;
           }
           case 490: {
-            mapSint64Sint64_.AddEntriesFrom(input, _map_mapSint64Sint64_codec);
+            mapSint64Sint64_.AddEntriesFrom(ref input, _map_mapSint64Sint64_codec);
             break;
           }
           case 498: {
-            mapFixed32Fixed32_.AddEntriesFrom(input, _map_mapFixed32Fixed32_codec);
+            mapFixed32Fixed32_.AddEntriesFrom(ref input, _map_mapFixed32Fixed32_codec);
             break;
           }
           case 506: {
-            mapFixed64Fixed64_.AddEntriesFrom(input, _map_mapFixed64Fixed64_codec);
+            mapFixed64Fixed64_.AddEntriesFrom(ref input, _map_mapFixed64Fixed64_codec);
             break;
           }
           case 514: {
-            mapSfixed32Sfixed32_.AddEntriesFrom(input, _map_mapSfixed32Sfixed32_codec);
+            mapSfixed32Sfixed32_.AddEntriesFrom(ref input, _map_mapSfixed32Sfixed32_codec);
             break;
           }
           case 522: {
-            mapSfixed64Sfixed64_.AddEntriesFrom(input, _map_mapSfixed64Sfixed64_codec);
+            mapSfixed64Sfixed64_.AddEntriesFrom(ref input, _map_mapSfixed64Sfixed64_codec);
             break;
           }
           case 530: {
-            mapInt32Float_.AddEntriesFrom(input, _map_mapInt32Float_codec);
+            mapInt32Float_.AddEntriesFrom(ref input, _map_mapInt32Float_codec);
             break;
           }
           case 538: {
-            mapInt32Double_.AddEntriesFrom(input, _map_mapInt32Double_codec);
+            mapInt32Double_.AddEntriesFrom(ref input, _map_mapInt32Double_codec);
             break;
           }
           case 546: {
-            mapBoolBool_.AddEntriesFrom(input, _map_mapBoolBool_codec);
+            mapBoolBool_.AddEntriesFrom(ref input, _map_mapBoolBool_codec);
             break;
           }
           case 554: {
-            mapStringString_.AddEntriesFrom(input, _map_mapStringString_codec);
+            mapStringString_.AddEntriesFrom(ref input, _map_mapStringString_codec);
             break;
           }
           case 562: {
-            mapStringBytes_.AddEntriesFrom(input, _map_mapStringBytes_codec);
+            mapStringBytes_.AddEntriesFrom(ref input, _map_mapStringBytes_codec);
             break;
           }
           case 570: {
-            mapStringNestedMessage_.AddEntriesFrom(input, _map_mapStringNestedMessage_codec);
+            mapStringNestedMessage_.AddEntriesFrom(ref input, _map_mapStringNestedMessage_codec);
             break;
           }
           case 578: {
-            mapStringForeignMessage_.AddEntriesFrom(input, _map_mapStringForeignMessage_codec);
+            mapStringForeignMessage_.AddEntriesFrom(ref input, _map_mapStringForeignMessage_codec);
             break;
           }
           case 586: {
-            mapStringNestedEnum_.AddEntriesFrom(input, _map_mapStringNestedEnum_codec);
+            mapStringNestedEnum_.AddEntriesFrom(ref input, _map_mapStringNestedEnum_codec);
             break;
           }
           case 594: {
-            mapStringForeignEnum_.AddEntriesFrom(input, _map_mapStringForeignEnum_codec);
+            mapStringForeignEnum_.AddEntriesFrom(ref input, _map_mapStringForeignEnum_codec);
             break;
           }
           case 602:
           case 600: {
-            packedInt32_.AddEntriesFrom(input, _repeated_packedInt32_codec);
+            packedInt32_.AddEntriesFrom(ref input, _repeated_packedInt32_codec);
             break;
           }
           case 610:
           case 608: {
-            packedInt64_.AddEntriesFrom(input, _repeated_packedInt64_codec);
+            packedInt64_.AddEntriesFrom(ref input, _repeated_packedInt64_codec);
             break;
           }
           case 618:
           case 616: {
-            packedUint32_.AddEntriesFrom(input, _repeated_packedUint32_codec);
+            packedUint32_.AddEntriesFrom(ref input, _repeated_packedUint32_codec);
             break;
           }
           case 626:
           case 624: {
-            packedUint64_.AddEntriesFrom(input, _repeated_packedUint64_codec);
+            packedUint64_.AddEntriesFrom(ref input, _repeated_packedUint64_codec);
             break;
           }
           case 634:
           case 632: {
-            packedSint32_.AddEntriesFrom(input, _repeated_packedSint32_codec);
+            packedSint32_.AddEntriesFrom(ref input, _repeated_packedSint32_codec);
             break;
           }
           case 642:
           case 640: {
-            packedSint64_.AddEntriesFrom(input, _repeated_packedSint64_codec);
+            packedSint64_.AddEntriesFrom(ref input, _repeated_packedSint64_codec);
             break;
           }
           case 650:
           case 653: {
-            packedFixed32_.AddEntriesFrom(input, _repeated_packedFixed32_codec);
+            packedFixed32_.AddEntriesFrom(ref input, _repeated_packedFixed32_codec);
             break;
           }
           case 658:
           case 657: {
-            packedFixed64_.AddEntriesFrom(input, _repeated_packedFixed64_codec);
+            packedFixed64_.AddEntriesFrom(ref input, _repeated_packedFixed64_codec);
             break;
           }
           case 666:
           case 669: {
-            packedSfixed32_.AddEntriesFrom(input, _repeated_packedSfixed32_codec);
+            packedSfixed32_.AddEntriesFrom(ref input, _repeated_packedSfixed32_codec);
             break;
           }
           case 674:
           case 673: {
-            packedSfixed64_.AddEntriesFrom(input, _repeated_packedSfixed64_codec);
+            packedSfixed64_.AddEntriesFrom(ref input, _repeated_packedSfixed64_codec);
             break;
           }
           case 682:
           case 685: {
-            packedFloat_.AddEntriesFrom(input, _repeated_packedFloat_codec);
+            packedFloat_.AddEntriesFrom(ref input, _repeated_packedFloat_codec);
             break;
           }
           case 690:
           case 689: {
-            packedDouble_.AddEntriesFrom(input, _repeated_packedDouble_codec);
+            packedDouble_.AddEntriesFrom(ref input, _repeated_packedDouble_codec);
             break;
           }
           case 698:
           case 696: {
-            packedBool_.AddEntriesFrom(input, _repeated_packedBool_codec);
+            packedBool_.AddEntriesFrom(ref input, _repeated_packedBool_codec);
             break;
           }
           case 706:
           case 704: {
-            packedNestedEnum_.AddEntriesFrom(input, _repeated_packedNestedEnum_codec);
+            packedNestedEnum_.AddEntriesFrom(ref input, _repeated_packedNestedEnum_codec);
             break;
           }
           case 714:
           case 712: {
-            unpackedInt32_.AddEntriesFrom(input, _repeated_unpackedInt32_codec);
+            unpackedInt32_.AddEntriesFrom(ref input, _repeated_unpackedInt32_codec);
             break;
           }
           case 722:
           case 720: {
-            unpackedInt64_.AddEntriesFrom(input, _repeated_unpackedInt64_codec);
+            unpackedInt64_.AddEntriesFrom(ref input, _repeated_unpackedInt64_codec);
             break;
           }
           case 730:
           case 728: {
-            unpackedUint32_.AddEntriesFrom(input, _repeated_unpackedUint32_codec);
+            unpackedUint32_.AddEntriesFrom(ref input, _repeated_unpackedUint32_codec);
             break;
           }
           case 738:
           case 736: {
-            unpackedUint64_.AddEntriesFrom(input, _repeated_unpackedUint64_codec);
+            unpackedUint64_.AddEntriesFrom(ref input, _repeated_unpackedUint64_codec);
             break;
           }
           case 746:
           case 744: {
-            unpackedSint32_.AddEntriesFrom(input, _repeated_unpackedSint32_codec);
+            unpackedSint32_.AddEntriesFrom(ref input, _repeated_unpackedSint32_codec);
             break;
           }
           case 754:
           case 752: {
-            unpackedSint64_.AddEntriesFrom(input, _repeated_unpackedSint64_codec);
+            unpackedSint64_.AddEntriesFrom(ref input, _repeated_unpackedSint64_codec);
             break;
           }
           case 762:
           case 765: {
-            unpackedFixed32_.AddEntriesFrom(input, _repeated_unpackedFixed32_codec);
+            unpackedFixed32_.AddEntriesFrom(ref input, _repeated_unpackedFixed32_codec);
             break;
           }
           case 770:
           case 769: {
-            unpackedFixed64_.AddEntriesFrom(input, _repeated_unpackedFixed64_codec);
+            unpackedFixed64_.AddEntriesFrom(ref input, _repeated_unpackedFixed64_codec);
             break;
           }
           case 778:
           case 781: {
-            unpackedSfixed32_.AddEntriesFrom(input, _repeated_unpackedSfixed32_codec);
+            unpackedSfixed32_.AddEntriesFrom(ref input, _repeated_unpackedSfixed32_codec);
             break;
           }
           case 786:
           case 785: {
-            unpackedSfixed64_.AddEntriesFrom(input, _repeated_unpackedSfixed64_codec);
+            unpackedSfixed64_.AddEntriesFrom(ref input, _repeated_unpackedSfixed64_codec);
             break;
           }
           case 794:
           case 797: {
-            unpackedFloat_.AddEntriesFrom(input, _repeated_unpackedFloat_codec);
+            unpackedFloat_.AddEntriesFrom(ref input, _repeated_unpackedFloat_codec);
             break;
           }
           case 802:
           case 801: {
-            unpackedDouble_.AddEntriesFrom(input, _repeated_unpackedDouble_codec);
+            unpackedDouble_.AddEntriesFrom(ref input, _repeated_unpackedDouble_codec);
             break;
           }
           case 810:
           case 808: {
-            unpackedBool_.AddEntriesFrom(input, _repeated_unpackedBool_codec);
+            unpackedBool_.AddEntriesFrom(ref input, _repeated_unpackedBool_codec);
             break;
           }
           case 818:
           case 816: {
-            unpackedNestedEnum_.AddEntriesFrom(input, _repeated_unpackedNestedEnum_codec);
+            unpackedNestedEnum_.AddEntriesFrom(ref input, _repeated_unpackedNestedEnum_codec);
             break;
           }
           case 888: {
@@ -3848,102 +3853,102 @@ namespace ProtobufTestMessages.Proto3 {
             break;
           }
           case 1610: {
-            bool? value = _single_optionalBoolWrapper_codec.Read(input);
+            bool? value = _single_optionalBoolWrapper_codec.Read(ref input);
             if (optionalBoolWrapper_ == null || value != false) {
               OptionalBoolWrapper = value;
             }
             break;
           }
           case 1618: {
-            int? value = _single_optionalInt32Wrapper_codec.Read(input);
+            int? value = _single_optionalInt32Wrapper_codec.Read(ref input);
             if (optionalInt32Wrapper_ == null || value != 0) {
               OptionalInt32Wrapper = value;
             }
             break;
           }
           case 1626: {
-            long? value = _single_optionalInt64Wrapper_codec.Read(input);
+            long? value = _single_optionalInt64Wrapper_codec.Read(ref input);
             if (optionalInt64Wrapper_ == null || value != 0L) {
               OptionalInt64Wrapper = value;
             }
             break;
           }
           case 1634: {
-            uint? value = _single_optionalUint32Wrapper_codec.Read(input);
+            uint? value = _single_optionalUint32Wrapper_codec.Read(ref input);
             if (optionalUint32Wrapper_ == null || value != 0) {
               OptionalUint32Wrapper = value;
             }
             break;
           }
           case 1642: {
-            ulong? value = _single_optionalUint64Wrapper_codec.Read(input);
+            ulong? value = _single_optionalUint64Wrapper_codec.Read(ref input);
             if (optionalUint64Wrapper_ == null || value != 0UL) {
               OptionalUint64Wrapper = value;
             }
             break;
           }
           case 1650: {
-            float? value = _single_optionalFloatWrapper_codec.Read(input);
+            float? value = _single_optionalFloatWrapper_codec.Read(ref input);
             if (optionalFloatWrapper_ == null || value != 0F) {
               OptionalFloatWrapper = value;
             }
             break;
           }
           case 1658: {
-            double? value = _single_optionalDoubleWrapper_codec.Read(input);
+            double? value = _single_optionalDoubleWrapper_codec.Read(ref input);
             if (optionalDoubleWrapper_ == null || value != 0D) {
               OptionalDoubleWrapper = value;
             }
             break;
           }
           case 1666: {
-            string value = _single_optionalStringWrapper_codec.Read(input);
+            string value = _single_optionalStringWrapper_codec.Read(ref input);
             if (optionalStringWrapper_ == null || value != "") {
               OptionalStringWrapper = value;
             }
             break;
           }
           case 1674: {
-            pb::ByteString value = _single_optionalBytesWrapper_codec.Read(input);
+            pb::ByteString value = _single_optionalBytesWrapper_codec.Read(ref input);
             if (optionalBytesWrapper_ == null || value != pb::ByteString.Empty) {
               OptionalBytesWrapper = value;
             }
             break;
           }
           case 1690: {
-            repeatedBoolWrapper_.AddEntriesFrom(input, _repeated_repeatedBoolWrapper_codec);
+            repeatedBoolWrapper_.AddEntriesFrom(ref input, _repeated_repeatedBoolWrapper_codec);
             break;
           }
           case 1698: {
-            repeatedInt32Wrapper_.AddEntriesFrom(input, _repeated_repeatedInt32Wrapper_codec);
+            repeatedInt32Wrapper_.AddEntriesFrom(ref input, _repeated_repeatedInt32Wrapper_codec);
             break;
           }
           case 1706: {
-            repeatedInt64Wrapper_.AddEntriesFrom(input, _repeated_repeatedInt64Wrapper_codec);
+            repeatedInt64Wrapper_.AddEntriesFrom(ref input, _repeated_repeatedInt64Wrapper_codec);
             break;
           }
           case 1714: {
-            repeatedUint32Wrapper_.AddEntriesFrom(input, _repeated_repeatedUint32Wrapper_codec);
+            repeatedUint32Wrapper_.AddEntriesFrom(ref input, _repeated_repeatedUint32Wrapper_codec);
             break;
           }
           case 1722: {
-            repeatedUint64Wrapper_.AddEntriesFrom(input, _repeated_repeatedUint64Wrapper_codec);
+            repeatedUint64Wrapper_.AddEntriesFrom(ref input, _repeated_repeatedUint64Wrapper_codec);
             break;
           }
           case 1730: {
-            repeatedFloatWrapper_.AddEntriesFrom(input, _repeated_repeatedFloatWrapper_codec);
+            repeatedFloatWrapper_.AddEntriesFrom(ref input, _repeated_repeatedFloatWrapper_codec);
             break;
           }
           case 1738: {
-            repeatedDoubleWrapper_.AddEntriesFrom(input, _repeated_repeatedDoubleWrapper_codec);
+            repeatedDoubleWrapper_.AddEntriesFrom(ref input, _repeated_repeatedDoubleWrapper_codec);
             break;
           }
           case 1746: {
-            repeatedStringWrapper_.AddEntriesFrom(input, _repeated_repeatedStringWrapper_codec);
+            repeatedStringWrapper_.AddEntriesFrom(ref input, _repeated_repeatedStringWrapper_codec);
             break;
           }
           case 1754: {
-            repeatedBytesWrapper_.AddEntriesFrom(input, _repeated_repeatedBytesWrapper_codec);
+            repeatedBytesWrapper_.AddEntriesFrom(ref input, _repeated_repeatedBytesWrapper_codec);
             break;
           }
           case 2410: {
@@ -3989,31 +3994,31 @@ namespace ProtobufTestMessages.Proto3 {
             break;
           }
           case 2490: {
-            repeatedDuration_.AddEntriesFrom(input, _repeated_repeatedDuration_codec);
+            repeatedDuration_.AddEntriesFrom(ref input, _repeated_repeatedDuration_codec);
             break;
           }
           case 2498: {
-            repeatedTimestamp_.AddEntriesFrom(input, _repeated_repeatedTimestamp_codec);
+            repeatedTimestamp_.AddEntriesFrom(ref input, _repeated_repeatedTimestamp_codec);
             break;
           }
           case 2506: {
-            repeatedFieldmask_.AddEntriesFrom(input, _repeated_repeatedFieldmask_codec);
+            repeatedFieldmask_.AddEntriesFrom(ref input, _repeated_repeatedFieldmask_codec);
             break;
           }
           case 2522: {
-            repeatedAny_.AddEntriesFrom(input, _repeated_repeatedAny_codec);
+            repeatedAny_.AddEntriesFrom(ref input, _repeated_repeatedAny_codec);
             break;
           }
           case 2530: {
-            repeatedValue_.AddEntriesFrom(input, _repeated_repeatedValue_codec);
+            repeatedValue_.AddEntriesFrom(ref input, _repeated_repeatedValue_codec);
             break;
           }
           case 2538: {
-            repeatedListValue_.AddEntriesFrom(input, _repeated_repeatedListValue_codec);
+            repeatedListValue_.AddEntriesFrom(ref input, _repeated_repeatedListValue_codec);
             break;
           }
           case 2594: {
-            repeatedStruct_.AddEntriesFrom(input, _repeated_repeatedStruct_codec);
+            repeatedStruct_.AddEntriesFrom(ref input, _repeated_repeatedStruct_codec);
             break;
           }
           case 3208: {
@@ -4115,7 +4120,7 @@ namespace ProtobufTestMessages.Proto3 {
         [pbr::OriginalName("bAz", PreferredAlias = false)] BAz = 2,
       }
 
-      public sealed partial class NestedMessage : pb::IMessage<NestedMessage> {
+      public sealed partial class NestedMessage : pb::IMessage<NestedMessage>, pb::IBufferMessage {
         private static readonly pb::MessageParser<NestedMessage> _parser = new pb::MessageParser<NestedMessage>(() => new NestedMessage());
         private pb::UnknownFieldSet _unknownFields;
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -4255,11 +4260,16 @@ namespace ProtobufTestMessages.Proto3 {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 8: {
                 A = input.ReadInt32();
@@ -4283,7 +4293,7 @@ namespace ProtobufTestMessages.Proto3 {
 
   }
 
-  public sealed partial class ForeignMessage : pb::IMessage<ForeignMessage> {
+  public sealed partial class ForeignMessage : pb::IMessage<ForeignMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ForeignMessage> _parser = new pb::MessageParser<ForeignMessage>(() => new ForeignMessage());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -4396,11 +4406,16 @@ namespace ProtobufTestMessages.Proto3 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             C = input.ReadInt32();

文件差异内容过多而无法显示
+ 242 - 92
csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs


+ 149 - 44
csharp/src/Google.Protobuf.Test.TestProtos/UnittestCustomOptionsProto3.cs

@@ -257,7 +257,7 @@ namespace UnitTest.Issues.TestProtos {
   /// A test message with custom options at all possible locations (and also some
   /// regular options, to make sure they interact nicely).
   /// </summary>
-  public sealed partial class TestMessageWithCustomOptions : pb::IMessage<TestMessageWithCustomOptions> {
+  public sealed partial class TestMessageWithCustomOptions : pb::IMessage<TestMessageWithCustomOptions>, pb::IBufferMessage {
     private static readonly pb::MessageParser<TestMessageWithCustomOptions> _parser = new pb::MessageParser<TestMessageWithCustomOptions>(() => new TestMessageWithCustomOptions());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -422,11 +422,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Field1 = input.ReadString();
@@ -459,7 +464,7 @@ namespace UnitTest.Issues.TestProtos {
   /// A test RPC service with custom options at all possible locations (and also
   /// some regular options, to make sure they interact nicely).
   /// </summary>
-  public sealed partial class CustomOptionFooRequest : pb::IMessage<CustomOptionFooRequest> {
+  public sealed partial class CustomOptionFooRequest : pb::IMessage<CustomOptionFooRequest>, pb::IBufferMessage {
     private static readonly pb::MessageParser<CustomOptionFooRequest> _parser = new pb::MessageParser<CustomOptionFooRequest>(() => new CustomOptionFooRequest());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -548,11 +553,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -560,7 +570,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class CustomOptionFooResponse : pb::IMessage<CustomOptionFooResponse> {
+  public sealed partial class CustomOptionFooResponse : pb::IMessage<CustomOptionFooResponse>, pb::IBufferMessage {
     private static readonly pb::MessageParser<CustomOptionFooResponse> _parser = new pb::MessageParser<CustomOptionFooResponse>(() => new CustomOptionFooResponse());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -649,11 +659,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -661,7 +676,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class CustomOptionFooClientMessage : pb::IMessage<CustomOptionFooClientMessage> {
+  public sealed partial class CustomOptionFooClientMessage : pb::IMessage<CustomOptionFooClientMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<CustomOptionFooClientMessage> _parser = new pb::MessageParser<CustomOptionFooClientMessage>(() => new CustomOptionFooClientMessage());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -750,11 +765,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -762,7 +782,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class CustomOptionFooServerMessage : pb::IMessage<CustomOptionFooServerMessage> {
+  public sealed partial class CustomOptionFooServerMessage : pb::IMessage<CustomOptionFooServerMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<CustomOptionFooServerMessage> _parser = new pb::MessageParser<CustomOptionFooServerMessage>(() => new CustomOptionFooServerMessage());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -851,11 +871,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -863,7 +888,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class DummyMessageContainingEnum : pb::IMessage<DummyMessageContainingEnum> {
+  public sealed partial class DummyMessageContainingEnum : pb::IMessage<DummyMessageContainingEnum>, pb::IBufferMessage {
     private static readonly pb::MessageParser<DummyMessageContainingEnum> _parser = new pb::MessageParser<DummyMessageContainingEnum>(() => new DummyMessageContainingEnum());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -952,11 +977,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -977,7 +1007,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class DummyMessageInvalidAsOptionType : pb::IMessage<DummyMessageInvalidAsOptionType> {
+  public sealed partial class DummyMessageInvalidAsOptionType : pb::IMessage<DummyMessageInvalidAsOptionType>, pb::IBufferMessage {
     private static readonly pb::MessageParser<DummyMessageInvalidAsOptionType> _parser = new pb::MessageParser<DummyMessageInvalidAsOptionType>(() => new DummyMessageInvalidAsOptionType());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1066,11 +1096,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -1078,7 +1113,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class CustomOptionMinIntegerValues : pb::IMessage<CustomOptionMinIntegerValues> {
+  public sealed partial class CustomOptionMinIntegerValues : pb::IMessage<CustomOptionMinIntegerValues>, pb::IBufferMessage {
     private static readonly pb::MessageParser<CustomOptionMinIntegerValues> _parser = new pb::MessageParser<CustomOptionMinIntegerValues>(() => new CustomOptionMinIntegerValues());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1167,11 +1202,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -1179,7 +1219,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class CustomOptionMaxIntegerValues : pb::IMessage<CustomOptionMaxIntegerValues> {
+  public sealed partial class CustomOptionMaxIntegerValues : pb::IMessage<CustomOptionMaxIntegerValues>, pb::IBufferMessage {
     private static readonly pb::MessageParser<CustomOptionMaxIntegerValues> _parser = new pb::MessageParser<CustomOptionMaxIntegerValues>(() => new CustomOptionMaxIntegerValues());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1268,11 +1308,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -1280,7 +1325,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class CustomOptionOtherValues : pb::IMessage<CustomOptionOtherValues> {
+  public sealed partial class CustomOptionOtherValues : pb::IMessage<CustomOptionOtherValues>, pb::IBufferMessage {
     private static readonly pb::MessageParser<CustomOptionOtherValues> _parser = new pb::MessageParser<CustomOptionOtherValues>(() => new CustomOptionOtherValues());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1369,11 +1414,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -1381,7 +1431,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class SettingRealsFromPositiveInts : pb::IMessage<SettingRealsFromPositiveInts> {
+  public sealed partial class SettingRealsFromPositiveInts : pb::IMessage<SettingRealsFromPositiveInts>, pb::IBufferMessage {
     private static readonly pb::MessageParser<SettingRealsFromPositiveInts> _parser = new pb::MessageParser<SettingRealsFromPositiveInts>(() => new SettingRealsFromPositiveInts());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1470,11 +1520,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -1482,7 +1537,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class SettingRealsFromNegativeInts : pb::IMessage<SettingRealsFromNegativeInts> {
+  public sealed partial class SettingRealsFromNegativeInts : pb::IMessage<SettingRealsFromNegativeInts>, pb::IBufferMessage {
     private static readonly pb::MessageParser<SettingRealsFromNegativeInts> _parser = new pb::MessageParser<SettingRealsFromNegativeInts>(() => new SettingRealsFromNegativeInts());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1571,11 +1626,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -1583,7 +1643,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class ComplexOptionType1 : pb::IMessage<ComplexOptionType1> {
+  public sealed partial class ComplexOptionType1 : pb::IMessage<ComplexOptionType1>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ComplexOptionType1> _parser = new pb::MessageParser<ComplexOptionType1>(() => new ComplexOptionType1());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1760,11 +1820,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Foo = input.ReadInt32();
@@ -1780,7 +1845,7 @@ namespace UnitTest.Issues.TestProtos {
           }
           case 34:
           case 32: {
-            foo4_.AddEntriesFrom(input, _repeated_foo4_codec);
+            foo4_.AddEntriesFrom(ref input, _repeated_foo4_codec);
             break;
           }
         }
@@ -1789,7 +1854,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class ComplexOptionType2 : pb::IMessage<ComplexOptionType2> {
+  public sealed partial class ComplexOptionType2 : pb::IMessage<ComplexOptionType2>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ComplexOptionType2> _parser = new pb::MessageParser<ComplexOptionType2>(() => new ComplexOptionType2());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1972,11 +2037,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             if (bar_ == null) {
@@ -1997,7 +2067,7 @@ namespace UnitTest.Issues.TestProtos {
             break;
           }
           case 34: {
-            barney_.AddEntriesFrom(input, _repeated_barney_codec);
+            barney_.AddEntriesFrom(ref input, _repeated_barney_codec);
             break;
           }
         }
@@ -2008,7 +2078,7 @@ namespace UnitTest.Issues.TestProtos {
     /// <summary>Container for nested types declared in the ComplexOptionType2 message type.</summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static partial class Types {
-      public sealed partial class ComplexOptionType4 : pb::IMessage<ComplexOptionType4> {
+      public sealed partial class ComplexOptionType4 : pb::IMessage<ComplexOptionType4>, pb::IBufferMessage {
         private static readonly pb::MessageParser<ComplexOptionType4> _parser = new pb::MessageParser<ComplexOptionType4>(() => new ComplexOptionType4());
         private pb::UnknownFieldSet _unknownFields;
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2121,11 +2191,16 @@ namespace UnitTest.Issues.TestProtos {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 8: {
                 Waldo = input.ReadInt32();
@@ -2151,7 +2226,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class ComplexOptionType3 : pb::IMessage<ComplexOptionType3> {
+  public sealed partial class ComplexOptionType3 : pb::IMessage<ComplexOptionType3>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ComplexOptionType3> _parser = new pb::MessageParser<ComplexOptionType3>(() => new ComplexOptionType3());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2264,11 +2339,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Qux = input.ReadInt32();
@@ -2283,7 +2363,7 @@ namespace UnitTest.Issues.TestProtos {
   /// <summary>
   /// Note that we try various different ways of naming the same extension.
   /// </summary>
-  public sealed partial class VariousComplexOptions : pb::IMessage<VariousComplexOptions> {
+  public sealed partial class VariousComplexOptions : pb::IMessage<VariousComplexOptions>, pb::IBufferMessage {
     private static readonly pb::MessageParser<VariousComplexOptions> _parser = new pb::MessageParser<VariousComplexOptions>(() => new VariousComplexOptions());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2372,11 +2452,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -2387,7 +2472,7 @@ namespace UnitTest.Issues.TestProtos {
   /// <summary>
   /// A helper type used to test aggregate option parsing
   /// </summary>
-  public sealed partial class Aggregate : pb::IMessage<Aggregate> {
+  public sealed partial class Aggregate : pb::IMessage<Aggregate>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Aggregate> _parser = new pb::MessageParser<Aggregate>(() => new Aggregate());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2554,11 +2639,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             I = input.ReadInt32();
@@ -2581,7 +2671,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class AggregateMessage : pb::IMessage<AggregateMessage> {
+  public sealed partial class AggregateMessage : pb::IMessage<AggregateMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<AggregateMessage> _parser = new pb::MessageParser<AggregateMessage>(() => new AggregateMessage());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2694,11 +2784,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Fieldname = input.ReadInt32();
@@ -2713,7 +2808,7 @@ namespace UnitTest.Issues.TestProtos {
   /// <summary>
   /// Test custom options for nested type.
   /// </summary>
-  public sealed partial class NestedOptionType : pb::IMessage<NestedOptionType> {
+  public sealed partial class NestedOptionType : pb::IMessage<NestedOptionType>, pb::IBufferMessage {
     private static readonly pb::MessageParser<NestedOptionType> _parser = new pb::MessageParser<NestedOptionType>(() => new NestedOptionType());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2802,11 +2897,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -2821,7 +2921,7 @@ namespace UnitTest.Issues.TestProtos {
         [pbr::OriginalName("NESTED_ENUM_VALUE")] Value = 1,
       }
 
-      public sealed partial class NestedMessage : pb::IMessage<NestedMessage> {
+      public sealed partial class NestedMessage : pb::IMessage<NestedMessage>, pb::IBufferMessage {
         private static readonly pb::MessageParser<NestedMessage> _parser = new pb::MessageParser<NestedMessage>(() => new NestedMessage());
         private pb::UnknownFieldSet _unknownFields;
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2934,11 +3034,16 @@ namespace UnitTest.Issues.TestProtos {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 8: {
                 NestedField = input.ReadInt32();

+ 7 - 2
csharp/src/Google.Protobuf.Test.TestProtos/UnittestImport.cs

@@ -59,7 +59,7 @@ namespace Google.Protobuf.TestProtos.Proto2 {
   #endregion
 
   #region Messages
-  public sealed partial class ImportMessage : pb::IMessage<ImportMessage> {
+  public sealed partial class ImportMessage : pb::IMessage<ImportMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ImportMessage> _parser = new pb::MessageParser<ImportMessage>(() => new ImportMessage());
     private pb::UnknownFieldSet _unknownFields;
     private int _hasBits0;
@@ -187,11 +187,16 @@ namespace Google.Protobuf.TestProtos.Proto2 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             D = input.ReadInt32();

+ 7 - 2
csharp/src/Google.Protobuf.Test.TestProtos/UnittestImportProto3.cs

@@ -50,7 +50,7 @@ namespace Google.Protobuf.TestProtos {
   #endregion
 
   #region Messages
-  public sealed partial class ImportMessage : pb::IMessage<ImportMessage> {
+  public sealed partial class ImportMessage : pb::IMessage<ImportMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ImportMessage> _parser = new pb::MessageParser<ImportMessage>(() => new ImportMessage());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -163,11 +163,16 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             D = input.ReadInt32();

+ 7 - 2
csharp/src/Google.Protobuf.Test.TestProtos/UnittestImportPublic.cs

@@ -37,7 +37,7 @@ namespace Google.Protobuf.TestProtos.Proto2 {
 
   }
   #region Messages
-  public sealed partial class PublicImportMessage : pb::IMessage<PublicImportMessage> {
+  public sealed partial class PublicImportMessage : pb::IMessage<PublicImportMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<PublicImportMessage> _parser = new pb::MessageParser<PublicImportMessage>(() => new PublicImportMessage());
     private pb::UnknownFieldSet _unknownFields;
     private int _hasBits0;
@@ -165,11 +165,16 @@ namespace Google.Protobuf.TestProtos.Proto2 {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             E = input.ReadInt32();

+ 7 - 2
csharp/src/Google.Protobuf.Test.TestProtos/UnittestImportPublicProto3.cs

@@ -38,7 +38,7 @@ namespace Google.Protobuf.TestProtos {
 
   }
   #region Messages
-  public sealed partial class PublicImportMessage : pb::IMessage<PublicImportMessage> {
+  public sealed partial class PublicImportMessage : pb::IMessage<PublicImportMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<PublicImportMessage> _parser = new pb::MessageParser<PublicImportMessage>(() => new PublicImportMessage());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -151,11 +151,16 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             E = input.ReadInt32();

+ 7 - 2
csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssue6936B.cs

@@ -37,7 +37,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
   #region Messages
-  public sealed partial class Foo : pb::IMessage<Foo> {
+  public sealed partial class Foo : pb::IMessage<Foo>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Foo> _parser = new pb::MessageParser<Foo>(() => new Foo());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -126,11 +126,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }

+ 7 - 2
csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssue6936C.cs

@@ -39,7 +39,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
   #region Messages
-  public sealed partial class Bar : pb::IMessage<Bar> {
+  public sealed partial class Bar : pb::IMessage<Bar>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Bar> _parser = new pb::MessageParser<Bar>(() => new Bar());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -155,11 +155,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             if (foo_ == null) {

+ 96 - 31
csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs

@@ -88,7 +88,7 @@ namespace UnitTest.Issues.TestProtos {
   /// Issue 307: when generating doubly-nested types, any references
   /// should be of the form A.Types.B.Types.C.
   /// </summary>
-  public sealed partial class Issue307 : pb::IMessage<Issue307> {
+  public sealed partial class Issue307 : pb::IMessage<Issue307>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Issue307> _parser = new pb::MessageParser<Issue307>(() => new Issue307());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -177,11 +177,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -191,7 +196,7 @@ namespace UnitTest.Issues.TestProtos {
     /// <summary>Container for nested types declared in the Issue307 message type.</summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static partial class Types {
-      public sealed partial class NestedOnce : pb::IMessage<NestedOnce> {
+      public sealed partial class NestedOnce : pb::IMessage<NestedOnce>, pb::IBufferMessage {
         private static readonly pb::MessageParser<NestedOnce> _parser = new pb::MessageParser<NestedOnce>(() => new NestedOnce());
         private pb::UnknownFieldSet _unknownFields;
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -280,11 +285,16 @@ namespace UnitTest.Issues.TestProtos {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
             }
           }
@@ -294,7 +304,7 @@ namespace UnitTest.Issues.TestProtos {
         /// <summary>Container for nested types declared in the NestedOnce message type.</summary>
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public static partial class Types {
-          public sealed partial class NestedTwice : pb::IMessage<NestedTwice> {
+          public sealed partial class NestedTwice : pb::IMessage<NestedTwice>, pb::IBufferMessage {
             private static readonly pb::MessageParser<NestedTwice> _parser = new pb::MessageParser<NestedTwice>(() => new NestedTwice());
             private pb::UnknownFieldSet _unknownFields;
             [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -383,11 +393,16 @@ namespace UnitTest.Issues.TestProtos {
 
             [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
             public void MergeFrom(pb::CodedInputStream input) {
+              input.ReadRawMessage(this);
+            }
+
+            [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+            void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
               uint tag;
               while ((tag = input.ReadTag()) != 0) {
                 switch(tag) {
                   default:
-                    _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                    _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                     break;
                 }
               }
@@ -405,7 +420,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class NegativeEnumMessage : pb::IMessage<NegativeEnumMessage> {
+  public sealed partial class NegativeEnumMessage : pb::IMessage<NegativeEnumMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<NegativeEnumMessage> _parser = new pb::MessageParser<NegativeEnumMessage>(() => new NegativeEnumMessage());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -550,11 +565,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Value = (global::UnitTest.Issues.TestProtos.NegativeEnum) input.ReadEnum();
@@ -562,12 +582,12 @@ namespace UnitTest.Issues.TestProtos {
           }
           case 18:
           case 16: {
-            values_.AddEntriesFrom(input, _repeated_values_codec);
+            values_.AddEntriesFrom(ref input, _repeated_values_codec);
             break;
           }
           case 26:
           case 24: {
-            packedValues_.AddEntriesFrom(input, _repeated_packedValues_codec);
+            packedValues_.AddEntriesFrom(ref input, _repeated_packedValues_codec);
             break;
           }
         }
@@ -576,7 +596,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class DeprecatedChild : pb::IMessage<DeprecatedChild> {
+  public sealed partial class DeprecatedChild : pb::IMessage<DeprecatedChild>, pb::IBufferMessage {
     private static readonly pb::MessageParser<DeprecatedChild> _parser = new pb::MessageParser<DeprecatedChild>(() => new DeprecatedChild());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -665,11 +685,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }
@@ -677,7 +702,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class DeprecatedFieldsMessage : pb::IMessage<DeprecatedFieldsMessage> {
+  public sealed partial class DeprecatedFieldsMessage : pb::IMessage<DeprecatedFieldsMessage>, pb::IBufferMessage {
     private static readonly pb::MessageParser<DeprecatedFieldsMessage> _parser = new pb::MessageParser<DeprecatedFieldsMessage>(() => new DeprecatedFieldsMessage());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -895,11 +920,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             PrimitiveValue = input.ReadInt32();
@@ -907,7 +937,7 @@ namespace UnitTest.Issues.TestProtos {
           }
           case 18:
           case 16: {
-            primitiveArray_.AddEntriesFrom(input, _repeated_primitiveArray_codec);
+            primitiveArray_.AddEntriesFrom(ref input, _repeated_primitiveArray_codec);
             break;
           }
           case 26: {
@@ -918,7 +948,7 @@ namespace UnitTest.Issues.TestProtos {
             break;
           }
           case 34: {
-            messageArray_.AddEntriesFrom(input, _repeated_messageArray_codec);
+            messageArray_.AddEntriesFrom(ref input, _repeated_messageArray_codec);
             break;
           }
           case 40: {
@@ -927,7 +957,7 @@ namespace UnitTest.Issues.TestProtos {
           }
           case 50:
           case 48: {
-            enumArray_.AddEntriesFrom(input, _repeated_enumArray_codec);
+            enumArray_.AddEntriesFrom(ref input, _repeated_enumArray_codec);
             break;
           }
         }
@@ -939,7 +969,7 @@ namespace UnitTest.Issues.TestProtos {
   /// <summary>
   /// Issue 45: http://code.google.com/p/protobuf-csharp-port/issues/detail?id=45
   /// </summary>
-  public sealed partial class ItemField : pb::IMessage<ItemField> {
+  public sealed partial class ItemField : pb::IMessage<ItemField>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ItemField> _parser = new pb::MessageParser<ItemField>(() => new ItemField());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1052,11 +1082,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Item = input.ReadInt32();
@@ -1068,7 +1103,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class ReservedNames : pb::IMessage<ReservedNames> {
+  public sealed partial class ReservedNames : pb::IMessage<ReservedNames>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ReservedNames> _parser = new pb::MessageParser<ReservedNames>(() => new ReservedNames());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1205,11 +1240,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Types_ = input.ReadInt32();
@@ -1230,7 +1270,7 @@ namespace UnitTest.Issues.TestProtos {
       /// <summary>
       /// Force a nested type called Types
       /// </summary>
-      public sealed partial class SomeNestedType : pb::IMessage<SomeNestedType> {
+      public sealed partial class SomeNestedType : pb::IMessage<SomeNestedType>, pb::IBufferMessage {
         private static readonly pb::MessageParser<SomeNestedType> _parser = new pb::MessageParser<SomeNestedType>(() => new SomeNestedType());
         private pb::UnknownFieldSet _unknownFields;
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1319,11 +1359,16 @@ namespace UnitTest.Issues.TestProtos {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
             }
           }
@@ -1348,7 +1393,7 @@ namespace UnitTest.Issues.TestProtos {
   /// Alternatively, consider just adding this to
   /// unittest_proto3.proto if multiple platforms want it.
   /// </summary>
-  public sealed partial class TestJsonFieldOrdering : pb::IMessage<TestJsonFieldOrdering> {
+  public sealed partial class TestJsonFieldOrdering : pb::IMessage<TestJsonFieldOrdering>, pb::IBufferMessage {
     private static readonly pb::MessageParser<TestJsonFieldOrdering> _parser = new pb::MessageParser<TestJsonFieldOrdering>(() => new TestJsonFieldOrdering());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1643,11 +1688,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             PlainString = input.ReadString();
@@ -1679,7 +1729,7 @@ namespace UnitTest.Issues.TestProtos {
 
   }
 
-  public sealed partial class TestJsonName : pb::IMessage<TestJsonName> {
+  public sealed partial class TestJsonName : pb::IMessage<TestJsonName>, pb::IBufferMessage {
     private static readonly pb::MessageParser<TestJsonName> _parser = new pb::MessageParser<TestJsonName>(() => new TestJsonName());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1843,11 +1893,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
@@ -1872,7 +1927,7 @@ namespace UnitTest.Issues.TestProtos {
   /// oneof case, which is itself a message type, the submessages should
   /// be merged.
   /// </summary>
-  public sealed partial class OneofMerging : pb::IMessage<OneofMerging> {
+  public sealed partial class OneofMerging : pb::IMessage<OneofMerging>, pb::IBufferMessage {
     private static readonly pb::MessageParser<OneofMerging> _parser = new pb::MessageParser<OneofMerging>(() => new OneofMerging());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2043,11 +2098,16 @@ namespace UnitTest.Issues.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Text = input.ReadString();
@@ -2070,7 +2130,7 @@ namespace UnitTest.Issues.TestProtos {
     /// <summary>Container for nested types declared in the OneofMerging message type.</summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static partial class Types {
-      public sealed partial class Nested : pb::IMessage<Nested> {
+      public sealed partial class Nested : pb::IMessage<Nested>, pb::IBufferMessage {
         private static readonly pb::MessageParser<Nested> _parser = new pb::MessageParser<Nested>(() => new Nested());
         private pb::UnknownFieldSet _unknownFields;
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2207,11 +2267,16 @@ namespace UnitTest.Issues.TestProtos {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 8: {
                 X = input.ReadInt32();

文件差异内容过多而无法显示
+ 252 - 112
csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3.cs


+ 14 - 4
csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.cs

@@ -66,7 +66,7 @@ namespace ProtobufUnittest {
 
   }
   #region Messages
-  public sealed partial class TestProto3Optional : pb::IMessage<TestProto3Optional> {
+  public sealed partial class TestProto3Optional : pb::IMessage<TestProto3Optional>, pb::IBufferMessage {
     private static readonly pb::MessageParser<TestProto3Optional> _parser = new pb::MessageParser<TestProto3Optional>(() => new TestProto3Optional());
     private pb::UnknownFieldSet _unknownFields;
     private int _hasBits0;
@@ -806,11 +806,16 @@ namespace ProtobufUnittest {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             OptionalInt32 = input.ReadInt32();
@@ -913,7 +918,7 @@ namespace ProtobufUnittest {
         [pbr::OriginalName("NEG")] Neg = -1,
       }
 
-      public sealed partial class NestedMessage : pb::IMessage<NestedMessage> {
+      public sealed partial class NestedMessage : pb::IMessage<NestedMessage>, pb::IBufferMessage {
         private static readonly pb::MessageParser<NestedMessage> _parser = new pb::MessageParser<NestedMessage>(() => new NestedMessage());
         private pb::UnknownFieldSet _unknownFields;
         private int _hasBits0;
@@ -1044,11 +1049,16 @@ namespace ProtobufUnittest {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 8: {
                 Bb = input.ReadInt32();

+ 82 - 62
csharp/src/Google.Protobuf.Test.TestProtos/UnittestWellKnownTypes.cs

@@ -179,7 +179,7 @@ namespace Google.Protobuf.TestProtos {
   /// Each wrapper type is included separately, as languages
   /// map handle different wrappers in different ways.
   /// </summary>
-  public sealed partial class TestWellKnownTypes : pb::IMessage<TestWellKnownTypes> {
+  public sealed partial class TestWellKnownTypes : pb::IMessage<TestWellKnownTypes>, pb::IBufferMessage {
     private static readonly pb::MessageParser<TestWellKnownTypes> _parser = new pb::MessageParser<TestWellKnownTypes>(() => new TestWellKnownTypes());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -784,11 +784,16 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             if (anyField_ == null) {
@@ -854,63 +859,63 @@ namespace Google.Protobuf.TestProtos {
             break;
           }
           case 82: {
-            double? value = _single_doubleField_codec.Read(input);
+            double? value = _single_doubleField_codec.Read(ref input);
             if (doubleField_ == null || value != 0D) {
               DoubleField = value;
             }
             break;
           }
           case 90: {
-            float? value = _single_floatField_codec.Read(input);
+            float? value = _single_floatField_codec.Read(ref input);
             if (floatField_ == null || value != 0F) {
               FloatField = value;
             }
             break;
           }
           case 98: {
-            long? value = _single_int64Field_codec.Read(input);
+            long? value = _single_int64Field_codec.Read(ref input);
             if (int64Field_ == null || value != 0L) {
               Int64Field = value;
             }
             break;
           }
           case 106: {
-            ulong? value = _single_uint64Field_codec.Read(input);
+            ulong? value = _single_uint64Field_codec.Read(ref input);
             if (uint64Field_ == null || value != 0UL) {
               Uint64Field = value;
             }
             break;
           }
           case 114: {
-            int? value = _single_int32Field_codec.Read(input);
+            int? value = _single_int32Field_codec.Read(ref input);
             if (int32Field_ == null || value != 0) {
               Int32Field = value;
             }
             break;
           }
           case 122: {
-            uint? value = _single_uint32Field_codec.Read(input);
+            uint? value = _single_uint32Field_codec.Read(ref input);
             if (uint32Field_ == null || value != 0) {
               Uint32Field = value;
             }
             break;
           }
           case 130: {
-            bool? value = _single_boolField_codec.Read(input);
+            bool? value = _single_boolField_codec.Read(ref input);
             if (boolField_ == null || value != false) {
               BoolField = value;
             }
             break;
           }
           case 138: {
-            string value = _single_stringField_codec.Read(input);
+            string value = _single_stringField_codec.Read(ref input);
             if (stringField_ == null || value != "") {
               StringField = value;
             }
             break;
           }
           case 146: {
-            pb::ByteString value = _single_bytesField_codec.Read(input);
+            pb::ByteString value = _single_bytesField_codec.Read(ref input);
             if (bytesField_ == null || value != pb::ByteString.Empty) {
               BytesField = value;
             }
@@ -932,7 +937,7 @@ namespace Google.Protobuf.TestProtos {
   /// <summary>
   /// A repeated field for each well-known type.
   /// </summary>
-  public sealed partial class RepeatedWellKnownTypes : pb::IMessage<RepeatedWellKnownTypes> {
+  public sealed partial class RepeatedWellKnownTypes : pb::IMessage<RepeatedWellKnownTypes>, pb::IBufferMessage {
     private static readonly pb::MessageParser<RepeatedWellKnownTypes> _parser = new pb::MessageParser<RepeatedWellKnownTypes>(() => new RepeatedWellKnownTypes());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1312,82 +1317,87 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            anyField_.AddEntriesFrom(input, _repeated_anyField_codec);
+            anyField_.AddEntriesFrom(ref input, _repeated_anyField_codec);
             break;
           }
           case 18: {
-            apiField_.AddEntriesFrom(input, _repeated_apiField_codec);
+            apiField_.AddEntriesFrom(ref input, _repeated_apiField_codec);
             break;
           }
           case 26: {
-            durationField_.AddEntriesFrom(input, _repeated_durationField_codec);
+            durationField_.AddEntriesFrom(ref input, _repeated_durationField_codec);
             break;
           }
           case 34: {
-            emptyField_.AddEntriesFrom(input, _repeated_emptyField_codec);
+            emptyField_.AddEntriesFrom(ref input, _repeated_emptyField_codec);
             break;
           }
           case 42: {
-            fieldMaskField_.AddEntriesFrom(input, _repeated_fieldMaskField_codec);
+            fieldMaskField_.AddEntriesFrom(ref input, _repeated_fieldMaskField_codec);
             break;
           }
           case 50: {
-            sourceContextField_.AddEntriesFrom(input, _repeated_sourceContextField_codec);
+            sourceContextField_.AddEntriesFrom(ref input, _repeated_sourceContextField_codec);
             break;
           }
           case 58: {
-            structField_.AddEntriesFrom(input, _repeated_structField_codec);
+            structField_.AddEntriesFrom(ref input, _repeated_structField_codec);
             break;
           }
           case 66: {
-            timestampField_.AddEntriesFrom(input, _repeated_timestampField_codec);
+            timestampField_.AddEntriesFrom(ref input, _repeated_timestampField_codec);
             break;
           }
           case 74: {
-            typeField_.AddEntriesFrom(input, _repeated_typeField_codec);
+            typeField_.AddEntriesFrom(ref input, _repeated_typeField_codec);
             break;
           }
           case 82: {
-            doubleField_.AddEntriesFrom(input, _repeated_doubleField_codec);
+            doubleField_.AddEntriesFrom(ref input, _repeated_doubleField_codec);
             break;
           }
           case 90: {
-            floatField_.AddEntriesFrom(input, _repeated_floatField_codec);
+            floatField_.AddEntriesFrom(ref input, _repeated_floatField_codec);
             break;
           }
           case 98: {
-            int64Field_.AddEntriesFrom(input, _repeated_int64Field_codec);
+            int64Field_.AddEntriesFrom(ref input, _repeated_int64Field_codec);
             break;
           }
           case 106: {
-            uint64Field_.AddEntriesFrom(input, _repeated_uint64Field_codec);
+            uint64Field_.AddEntriesFrom(ref input, _repeated_uint64Field_codec);
             break;
           }
           case 114: {
-            int32Field_.AddEntriesFrom(input, _repeated_int32Field_codec);
+            int32Field_.AddEntriesFrom(ref input, _repeated_int32Field_codec);
             break;
           }
           case 122: {
-            uint32Field_.AddEntriesFrom(input, _repeated_uint32Field_codec);
+            uint32Field_.AddEntriesFrom(ref input, _repeated_uint32Field_codec);
             break;
           }
           case 130: {
-            boolField_.AddEntriesFrom(input, _repeated_boolField_codec);
+            boolField_.AddEntriesFrom(ref input, _repeated_boolField_codec);
             break;
           }
           case 138: {
-            stringField_.AddEntriesFrom(input, _repeated_stringField_codec);
+            stringField_.AddEntriesFrom(ref input, _repeated_stringField_codec);
             break;
           }
           case 146: {
-            bytesField_.AddEntriesFrom(input, _repeated_bytesField_codec);
+            bytesField_.AddEntriesFrom(ref input, _repeated_bytesField_codec);
             break;
           }
         }
@@ -1396,7 +1406,7 @@ namespace Google.Protobuf.TestProtos {
 
   }
 
-  public sealed partial class OneofWellKnownTypes : pb::IMessage<OneofWellKnownTypes> {
+  public sealed partial class OneofWellKnownTypes : pb::IMessage<OneofWellKnownTypes>, pb::IBufferMessage {
     private static readonly pb::MessageParser<OneofWellKnownTypes> _parser = new pb::MessageParser<OneofWellKnownTypes>(() => new OneofWellKnownTypes());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2023,11 +2033,16 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             global::Google.Protobuf.WellKnownTypes.Any subBuilder = new global::Google.Protobuf.WellKnownTypes.Any();
@@ -2111,39 +2126,39 @@ namespace Google.Protobuf.TestProtos {
             break;
           }
           case 82: {
-            DoubleField = _oneof_doubleField_codec.Read(input);
+            DoubleField = _oneof_doubleField_codec.Read(ref input);
             break;
           }
           case 90: {
-            FloatField = _oneof_floatField_codec.Read(input);
+            FloatField = _oneof_floatField_codec.Read(ref input);
             break;
           }
           case 98: {
-            Int64Field = _oneof_int64Field_codec.Read(input);
+            Int64Field = _oneof_int64Field_codec.Read(ref input);
             break;
           }
           case 106: {
-            Uint64Field = _oneof_uint64Field_codec.Read(input);
+            Uint64Field = _oneof_uint64Field_codec.Read(ref input);
             break;
           }
           case 114: {
-            Int32Field = _oneof_int32Field_codec.Read(input);
+            Int32Field = _oneof_int32Field_codec.Read(ref input);
             break;
           }
           case 122: {
-            Uint32Field = _oneof_uint32Field_codec.Read(input);
+            Uint32Field = _oneof_uint32Field_codec.Read(ref input);
             break;
           }
           case 130: {
-            BoolField = _oneof_boolField_codec.Read(input);
+            BoolField = _oneof_boolField_codec.Read(ref input);
             break;
           }
           case 138: {
-            StringField = _oneof_stringField_codec.Read(input);
+            StringField = _oneof_stringField_codec.Read(ref input);
             break;
           }
           case 146: {
-            BytesField = _oneof_bytesField_codec.Read(input);
+            BytesField = _oneof_bytesField_codec.Read(ref input);
             break;
           }
         }
@@ -2157,7 +2172,7 @@ namespace Google.Protobuf.TestProtos {
   /// need to worry about the value part of the map being the
   /// well-known types, as messages can't be map keys.
   /// </summary>
-  public sealed partial class MapWellKnownTypes : pb::IMessage<MapWellKnownTypes> {
+  public sealed partial class MapWellKnownTypes : pb::IMessage<MapWellKnownTypes>, pb::IBufferMessage {
     private static readonly pb::MessageParser<MapWellKnownTypes> _parser = new pb::MessageParser<MapWellKnownTypes>(() => new MapWellKnownTypes());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2534,82 +2549,87 @@ namespace Google.Protobuf.TestProtos {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            anyField_.AddEntriesFrom(input, _map_anyField_codec);
+            anyField_.AddEntriesFrom(ref input, _map_anyField_codec);
             break;
           }
           case 18: {
-            apiField_.AddEntriesFrom(input, _map_apiField_codec);
+            apiField_.AddEntriesFrom(ref input, _map_apiField_codec);
             break;
           }
           case 26: {
-            durationField_.AddEntriesFrom(input, _map_durationField_codec);
+            durationField_.AddEntriesFrom(ref input, _map_durationField_codec);
             break;
           }
           case 34: {
-            emptyField_.AddEntriesFrom(input, _map_emptyField_codec);
+            emptyField_.AddEntriesFrom(ref input, _map_emptyField_codec);
             break;
           }
           case 42: {
-            fieldMaskField_.AddEntriesFrom(input, _map_fieldMaskField_codec);
+            fieldMaskField_.AddEntriesFrom(ref input, _map_fieldMaskField_codec);
             break;
           }
           case 50: {
-            sourceContextField_.AddEntriesFrom(input, _map_sourceContextField_codec);
+            sourceContextField_.AddEntriesFrom(ref input, _map_sourceContextField_codec);
             break;
           }
           case 58: {
-            structField_.AddEntriesFrom(input, _map_structField_codec);
+            structField_.AddEntriesFrom(ref input, _map_structField_codec);
             break;
           }
           case 66: {
-            timestampField_.AddEntriesFrom(input, _map_timestampField_codec);
+            timestampField_.AddEntriesFrom(ref input, _map_timestampField_codec);
             break;
           }
           case 74: {
-            typeField_.AddEntriesFrom(input, _map_typeField_codec);
+            typeField_.AddEntriesFrom(ref input, _map_typeField_codec);
             break;
           }
           case 82: {
-            doubleField_.AddEntriesFrom(input, _map_doubleField_codec);
+            doubleField_.AddEntriesFrom(ref input, _map_doubleField_codec);
             break;
           }
           case 90: {
-            floatField_.AddEntriesFrom(input, _map_floatField_codec);
+            floatField_.AddEntriesFrom(ref input, _map_floatField_codec);
             break;
           }
           case 98: {
-            int64Field_.AddEntriesFrom(input, _map_int64Field_codec);
+            int64Field_.AddEntriesFrom(ref input, _map_int64Field_codec);
             break;
           }
           case 106: {
-            uint64Field_.AddEntriesFrom(input, _map_uint64Field_codec);
+            uint64Field_.AddEntriesFrom(ref input, _map_uint64Field_codec);
             break;
           }
           case 114: {
-            int32Field_.AddEntriesFrom(input, _map_int32Field_codec);
+            int32Field_.AddEntriesFrom(ref input, _map_int32Field_codec);
             break;
           }
           case 122: {
-            uint32Field_.AddEntriesFrom(input, _map_uint32Field_codec);
+            uint32Field_.AddEntriesFrom(ref input, _map_uint32Field_codec);
             break;
           }
           case 130: {
-            boolField_.AddEntriesFrom(input, _map_boolField_codec);
+            boolField_.AddEntriesFrom(ref input, _map_boolField_codec);
             break;
           }
           case 138: {
-            stringField_.AddEntriesFrom(input, _map_stringField_codec);
+            stringField_.AddEntriesFrom(ref input, _map_stringField_codec);
             break;
           }
           case 146: {
-            bytesField_.AddEntriesFrom(input, _map_bytesField_codec);
+            bytesField_.AddEntriesFrom(ref input, _map_bytesField_codec);
             break;
           }
         }

+ 142 - 62
csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs

@@ -31,6 +31,7 @@
 #endregion
 
 using System;
+using System.Buffers;
 using System.IO;
 using Google.Protobuf.TestProtos;
 using Proto2 = Google.Protobuf.TestProtos.Proto2;
@@ -62,11 +63,22 @@ namespace Google.Protobuf
         {
             CodedInputStream input = new CodedInputStream(data);
             Assert.AreEqual((uint) value, input.ReadRawVarint32());
+            Assert.IsTrue(input.IsAtEnd);
 
             input = new CodedInputStream(data);
             Assert.AreEqual(value, input.ReadRawVarint64());
             Assert.IsTrue(input.IsAtEnd);
 
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>
+            {
+                Assert.AreEqual((uint) value, ctx.ReadUInt32());
+            }, true);
+
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>
+            {
+                Assert.AreEqual(value, ctx.ReadUInt64());
+            }, true);
+
             // Try different block sizes.
             for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
             {
@@ -76,6 +88,16 @@ namespace Google.Protobuf
                 input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
                 Assert.AreEqual(value, input.ReadRawVarint64());
                 Assert.IsTrue(input.IsAtEnd);
+
+                AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, bufferSize), (ref ParseContext ctx) =>
+                {
+                    Assert.AreEqual((uint) value, ctx.ReadUInt32());
+                }, true);
+
+                AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, bufferSize), (ref ParseContext ctx) =>
+                {
+                    Assert.AreEqual(value, ctx.ReadUInt64());
+                }, true);
             }
 
             // Try reading directly from a MemoryStream. We want to verify that it
@@ -104,11 +126,49 @@ namespace Google.Protobuf
             exception = Assert.Throws<InvalidProtocolBufferException>(() => input.ReadRawVarint64());
             Assert.AreEqual(expected.Message, exception.Message);
 
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>
+            {
+                try
+                {
+                    ctx.ReadUInt32();
+                    Assert.Fail();
+                }
+                catch (InvalidProtocolBufferException ex)
+                {
+                    Assert.AreEqual(expected.Message, ex.Message);
+                }
+            }, false);
+
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>
+            {
+                try
+                {
+                    ctx.ReadUInt64();
+                    Assert.Fail();
+                }
+                catch (InvalidProtocolBufferException ex)
+                {
+                    Assert.AreEqual(expected.Message, ex.Message);
+                }
+            }, false);
+
             // Make sure we get the same error when reading directly from a Stream.
             exception = Assert.Throws<InvalidProtocolBufferException>(() => CodedInputStream.ReadRawVarint32(new MemoryStream(data)));
             Assert.AreEqual(expected.Message, exception.Message);
         }
 
+        private delegate void ParseContextAssertAction(ref ParseContext ctx);
+
+        private static void AssertReadFromParseContext(ReadOnlySequence<byte> input, ParseContextAssertAction assertAction, bool assertIsAtEnd)
+        {
+            ParseContext.Initialize(input, out ParseContext parseCtx);
+            assertAction(ref parseCtx);
+            if (assertIsAtEnd)
+            {
+                Assert.IsTrue(SegmentedBufferHelper.IsAtEnd(ref parseCtx.buffer, ref parseCtx.state));
+            }
+        }
+
         [Test]
         public void ReadVarint()
         {
@@ -157,6 +217,11 @@ namespace Google.Protobuf
             Assert.AreEqual(value, input.ReadRawLittleEndian32());
             Assert.IsTrue(input.IsAtEnd);
 
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>
+            {
+                Assert.AreEqual(value, ctx.ReadFixed32());
+            }, true);
+
             // Try different block sizes.
             for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
             {
@@ -164,6 +229,11 @@ namespace Google.Protobuf
                     new SmallBlockInputStream(data, blockSize));
                 Assert.AreEqual(value, input.ReadRawLittleEndian32());
                 Assert.IsTrue(input.IsAtEnd);
+
+                AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, blockSize), (ref ParseContext ctx) =>
+                {
+                    Assert.AreEqual(value, ctx.ReadFixed32());
+                }, true);
             }
         }
 
@@ -177,6 +247,11 @@ namespace Google.Protobuf
             Assert.AreEqual(value, input.ReadRawLittleEndian64());
             Assert.IsTrue(input.IsAtEnd);
 
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>
+            {
+                Assert.AreEqual(value, ctx.ReadFixed64());
+            }, true);
+
             // Try different block sizes.
             for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
             {
@@ -184,6 +259,11 @@ namespace Google.Protobuf
                     new SmallBlockInputStream(data, blockSize));
                 Assert.AreEqual(value, input.ReadRawLittleEndian64());
                 Assert.IsTrue(input.IsAtEnd);
+
+                AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, blockSize), (ref ParseContext ctx) =>
+                {
+                    Assert.AreEqual(value, ctx.ReadFixed64());
+                }, true);
             }
         }
 
@@ -202,29 +282,29 @@ namespace Google.Protobuf
         [Test]
         public void DecodeZigZag32()
         {
-            Assert.AreEqual(0, CodedInputStream.DecodeZigZag32(0));
-            Assert.AreEqual(-1, CodedInputStream.DecodeZigZag32(1));
-            Assert.AreEqual(1, CodedInputStream.DecodeZigZag32(2));
-            Assert.AreEqual(-2, CodedInputStream.DecodeZigZag32(3));
-            Assert.AreEqual(0x3FFFFFFF, CodedInputStream.DecodeZigZag32(0x7FFFFFFE));
-            Assert.AreEqual(unchecked((int) 0xC0000000), CodedInputStream.DecodeZigZag32(0x7FFFFFFF));
-            Assert.AreEqual(0x7FFFFFFF, CodedInputStream.DecodeZigZag32(0xFFFFFFFE));
-            Assert.AreEqual(unchecked((int) 0x80000000), CodedInputStream.DecodeZigZag32(0xFFFFFFFF));
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(0));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(1));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(2));
+            Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag32(3));
+            Assert.AreEqual(0x3FFFFFFF, ParsingPrimitives.DecodeZigZag32(0x7FFFFFFE));
+            Assert.AreEqual(unchecked((int) 0xC0000000), ParsingPrimitives.DecodeZigZag32(0x7FFFFFFF));
+            Assert.AreEqual(0x7FFFFFFF, ParsingPrimitives.DecodeZigZag32(0xFFFFFFFE));
+            Assert.AreEqual(unchecked((int) 0x80000000), ParsingPrimitives.DecodeZigZag32(0xFFFFFFFF));
         }
 
         [Test]
         public void DecodeZigZag64()
         {
-            Assert.AreEqual(0, CodedInputStream.DecodeZigZag64(0));
-            Assert.AreEqual(-1, CodedInputStream.DecodeZigZag64(1));
-            Assert.AreEqual(1, CodedInputStream.DecodeZigZag64(2));
-            Assert.AreEqual(-2, CodedInputStream.DecodeZigZag64(3));
-            Assert.AreEqual(0x000000003FFFFFFFL, CodedInputStream.DecodeZigZag64(0x000000007FFFFFFEL));
-            Assert.AreEqual(unchecked((long) 0xFFFFFFFFC0000000L), CodedInputStream.DecodeZigZag64(0x000000007FFFFFFFL));
-            Assert.AreEqual(0x000000007FFFFFFFL, CodedInputStream.DecodeZigZag64(0x00000000FFFFFFFEL));
-            Assert.AreEqual(unchecked((long) 0xFFFFFFFF80000000L), CodedInputStream.DecodeZigZag64(0x00000000FFFFFFFFL));
-            Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
-            Assert.AreEqual(unchecked((long) 0x8000000000000000L), CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(0));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(1));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(2));
+            Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag64(3));
+            Assert.AreEqual(0x000000003FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFEL));
+            Assert.AreEqual(unchecked((long) 0xFFFFFFFFC0000000L), ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFFL));
+            Assert.AreEqual(0x000000007FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFEL));
+            Assert.AreEqual(unchecked((long) 0xFFFFFFFF80000000L), ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFFL));
+            Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
+            Assert.AreEqual(unchecked((long) 0x8000000000000000L), ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
         }
         
         [Test]
@@ -339,64 +419,64 @@ namespace Google.Protobuf
             Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(input));
         }
         
-        private static byte[] MakeMaliciousRecursionUnknownFieldsPayload(int recursionDepth)
-        {
-            // generate recursively nested groups that will be parsed as unknown fields
-            int unknownFieldNumber = 14;  // an unused field number
-            MemoryStream ms = new MemoryStream();
-            CodedOutputStream output = new CodedOutputStream(ms);
-            for (int i = 0; i < recursionDepth; i++)
-            {
-                output.WriteTag(WireFormat.MakeTag(unknownFieldNumber, WireFormat.WireType.StartGroup));
-            }
-            for (int i = 0; i < recursionDepth; i++)
-            {
-                output.WriteTag(WireFormat.MakeTag(unknownFieldNumber, WireFormat.WireType.EndGroup));
-            }
-            output.Flush();
-            return ms.ToArray();
+        private static byte[] MakeMaliciousRecursionUnknownFieldsPayload(int recursionDepth)
+        {
+            // generate recursively nested groups that will be parsed as unknown fields
+            int unknownFieldNumber = 14;  // an unused field number
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+            for (int i = 0; i < recursionDepth; i++)
+            {
+                output.WriteTag(WireFormat.MakeTag(unknownFieldNumber, WireFormat.WireType.StartGroup));
+            }
+            for (int i = 0; i < recursionDepth; i++)
+            {
+                output.WriteTag(WireFormat.MakeTag(unknownFieldNumber, WireFormat.WireType.EndGroup));
+            }
+            output.Flush();
+            return ms.ToArray();
         }
 
         [Test]
         public void MaliciousRecursion_UnknownFields()
         {
-            byte[] payloadAtRecursiveLimit = MakeMaliciousRecursionUnknownFieldsPayload(CodedInputStream.DefaultRecursionLimit);
-            byte[] payloadBeyondRecursiveLimit = MakeMaliciousRecursionUnknownFieldsPayload(CodedInputStream.DefaultRecursionLimit + 1);
-            
-            Assert.DoesNotThrow(() => TestRecursiveMessage.Parser.ParseFrom(payloadAtRecursiveLimit));
+            byte[] payloadAtRecursiveLimit = MakeMaliciousRecursionUnknownFieldsPayload(CodedInputStream.DefaultRecursionLimit);
+            byte[] payloadBeyondRecursiveLimit = MakeMaliciousRecursionUnknownFieldsPayload(CodedInputStream.DefaultRecursionLimit + 1);
+            
+            Assert.DoesNotThrow(() => TestRecursiveMessage.Parser.ParseFrom(payloadAtRecursiveLimit));
             Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(payloadBeyondRecursiveLimit));
         }
 
         [Test]
         public void ReadGroup_WrongEndGroupTag()
-        {
-            int groupFieldNumber = Proto2.TestAllTypes.OptionalGroupFieldNumber;
-
-            // write Proto2.TestAllTypes with "optional_group" set, but use wrong EndGroup closing tag
-            MemoryStream ms = new MemoryStream();
-            CodedOutputStream output = new CodedOutputStream(ms);
-            output.WriteTag(WireFormat.MakeTag(groupFieldNumber, WireFormat.WireType.StartGroup));
-            output.WriteGroup(new Proto2.TestAllTypes.Types.OptionalGroup { A = 12345 });
-            // end group with different field number
-            output.WriteTag(WireFormat.MakeTag(groupFieldNumber + 1, WireFormat.WireType.EndGroup));
-            output.Flush();
-            var payload = ms.ToArray();
-
-            Assert.Throws<InvalidProtocolBufferException>(() => Proto2.TestAllTypes.Parser.ParseFrom(payload));
+        {
+            int groupFieldNumber = Proto2.TestAllTypes.OptionalGroupFieldNumber;
+
+            // write Proto2.TestAllTypes with "optional_group" set, but use wrong EndGroup closing tag
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+            output.WriteTag(WireFormat.MakeTag(groupFieldNumber, WireFormat.WireType.StartGroup));
+            output.WriteGroup(new Proto2.TestAllTypes.Types.OptionalGroup { A = 12345 });
+            // end group with different field number
+            output.WriteTag(WireFormat.MakeTag(groupFieldNumber + 1, WireFormat.WireType.EndGroup));
+            output.Flush();
+            var payload = ms.ToArray();
+
+            Assert.Throws<InvalidProtocolBufferException>(() => Proto2.TestAllTypes.Parser.ParseFrom(payload));
         }
 
         [Test]
         public void ReadGroup_UnknownFields_WrongEndGroupTag()
-        {
-            MemoryStream ms = new MemoryStream();
-            CodedOutputStream output = new CodedOutputStream(ms);
-            output.WriteTag(WireFormat.MakeTag(14, WireFormat.WireType.StartGroup));
-            // end group with different field number
-            output.WriteTag(WireFormat.MakeTag(15, WireFormat.WireType.EndGroup));
-            output.Flush();
-            var payload = ms.ToArray();
-
-            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(payload));
+        {
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+            output.WriteTag(WireFormat.MakeTag(14, WireFormat.WireType.StartGroup));
+            // end group with different field number
+            output.WriteTag(WireFormat.MakeTag(15, WireFormat.WireType.EndGroup));
+            output.Flush();
+            var payload = ms.ToArray();
+
+            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(payload));
         }
 
         [Test]

+ 12 - 12
csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs

@@ -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, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(0)));
-            Assert.AreEqual(1, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(1)));
-            Assert.AreEqual(-1, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(-1)));
-            Assert.AreEqual(14927, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(14927)));
-            Assert.AreEqual(-3612, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(-3612)));
+            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)));
         }
 
         [Test]
         public void RoundTripZigZag64()
         {
-            Assert.AreEqual(0, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(0)));
-            Assert.AreEqual(1, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(1)));
-            Assert.AreEqual(-1, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-1)));
-            Assert.AreEqual(14927, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(14927)));
-            Assert.AreEqual(-3612, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-3612)));
+            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(856912304801416L,
-                            CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(856912304801416L)));
+                            ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(856912304801416L)));
             Assert.AreEqual(-75123905439571256L,
-                            CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-75123905439571256L)));
+                            ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-75123905439571256L)));
         }
 
         [Test]

+ 28 - 0
csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs

@@ -31,6 +31,7 @@
 #endregion
 
 using System;
+using System.IO;
 using System.Collections.Generic;
 using Google.Protobuf.TestProtos;
 using NUnit.Framework;
@@ -583,6 +584,33 @@ namespace Google.Protobuf.Collections
             Assert.False(map.TryGetValue(SampleNaNs.PayloadFlipped, out ignored));
         }
 
+        [Test]
+        public void AddEntriesFrom_CodedInputStream()
+        {
+            // map will have string key and string value
+            var keyTag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+            var valueTag = WireFormat.MakeTag(2, WireFormat.WireType.LengthDelimited);
+
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+            output.WriteLength(20);  // total of keyTag + key + valueTag + value
+            output.WriteTag(keyTag);
+            output.WriteString("the_key");
+            output.WriteTag(valueTag);
+            output.WriteString("the_value");
+            output.Flush();
+
+            var field = new MapField<string,string>();
+            var mapCodec = new MapField<string,string>.Codec(FieldCodec.ForString(keyTag, ""), FieldCodec.ForString(valueTag, ""), 10);
+            var input = new CodedInputStream(memoryStream.ToArray());
+
+            // test the legacy overload of AddEntriesFrom that takes a CodedInputStream
+            field.AddEntriesFrom(input, mapCodec);
+            CollectionAssert.AreEquivalent(new[] { "the_key" }, field.Keys);
+            CollectionAssert.AreEquivalent(new[] { "the_value" }, field.Values);
+            Assert.IsTrue(input.IsAtEnd);
+        }
+
 #if !NET35
         [Test]
         public void IDictionaryKeys_Equals_IReadOnlyDictionaryKeys()

+ 25 - 7
csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs

@@ -1,4 +1,4 @@
-using Google.Protobuf.TestProtos.Proto2;
+using Google.Protobuf.TestProtos.Proto2;
 using NUnit.Framework;
 
 using static Google.Protobuf.TestProtos.Proto2.UnittestExtensions;
@@ -34,12 +34,14 @@ namespace Google.Protobuf
             message.SetExtension(OptionalBoolExtension, true);
             var serialized = message.ToByteArray();
 
-            var other = TestAllExtensions.Parser
-                .WithExtensionRegistry(new ExtensionRegistry() { OptionalBoolExtension })
-                .ParseFrom(serialized);
-
-            Assert.AreEqual(message, other);
-            Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
+            MessageParsingHelpers.AssertReadingMessage(
+                TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { OptionalBoolExtension }),
+                serialized,
+                other =>
+                {
+                    Assert.AreEqual(message, other);
+                    Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
+                });
         }
 
         [Test]
@@ -59,6 +61,22 @@ namespace Google.Protobuf
             Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
         }
 
+        [Test]
+        public void TryMergeFieldFrom_CodedInputStream()
+        {
+            var message = new TestAllExtensions();
+            message.SetExtension(OptionalStringExtension, "abcd");
+
+            var input = new CodedInputStream(message.ToByteArray());
+            input.ExtensionRegistry = new ExtensionRegistry() { OptionalStringExtension };
+            input.ReadTag(); // TryMergeFieldFrom expects that a tag was just read and will inspect the LastTag value
+
+            ExtensionSet<TestAllExtensions> extensionSet = null;
+            // test the legacy overload of TryMergeFieldFrom that takes a CodedInputStream
+            Assert.IsTrue(ExtensionSet.TryMergeFieldFrom(ref extensionSet, input));
+            Assert.AreEqual("abcd", ExtensionSet.Get(ref extensionSet, OptionalStringExtension));
+        }
+
         [Test]
         public void TestEquals()
         {

+ 3 - 3
csharp/src/Google.Protobuf.Test/FieldCodecTest.cs

@@ -128,7 +128,7 @@ namespace Google.Protobuf
                 codedOutput.Flush();
                 stream.Position = 0;
                 var codedInput = new CodedInputStream(stream);
-                Assert.AreEqual(sampleValue, codec.ValueReader(codedInput));
+                Assert.AreEqual(sampleValue, codec.Read(codedInput));
                 Assert.IsTrue(codedInput.IsAtEnd);
             }
 
@@ -158,7 +158,7 @@ namespace Google.Protobuf
             {
                 // WriteTagAndValue ignores default values
                 var stream = new MemoryStream();
-                CodedOutputStream codedOutput;
+                CodedOutputStream codedOutput;
 #if !NET35
                 codedOutput = new CodedOutputStream(stream);
                 codec.WriteTagAndValue(codedOutput, codec.DefaultValue);
@@ -181,7 +181,7 @@ namespace Google.Protobuf
                     Assert.AreEqual(stream.Position, codec.ValueSizeCalculator(codec.DefaultValue));
                     stream.Position = 0;
                     var codedInput = new CodedInputStream(stream);
-                    Assert.AreEqual(codec.DefaultValue, codec.ValueReader(codedInput));
+                    Assert.AreEqual(codec.DefaultValue, codec.Read(codedInput));
                 }
             }
 

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

@@ -269,8 +269,8 @@ namespace Google.Protobuf
         [Test]
         public void RequiredFieldsNoThrow()
         {
-            Assert.DoesNotThrow(() => TestRequired.Parser.ParseFrom(new byte[0]));
-            Assert.DoesNotThrow(() => (TestRequired.Parser as MessageParser).ParseFrom(new byte[0]));
+            Assert.DoesNotThrow(() => MessageParsingHelpers.AssertReadingMessage(TestRequired.Parser, new byte[0], m => { }));
+            Assert.DoesNotThrow(() => MessageParsingHelpers.AssertReadingMessage(TestRequired.Parser as MessageParser, new byte[0], m => { }));
         }
 
         [Test]
@@ -344,9 +344,7 @@ namespace Google.Protobuf
                 }
             };
 
-            byte[] bytes = message.ToByteArray();
-            TestAllTypes parsed = Proto2.TestAllTypes.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, parsed);
+            MessageParsingHelpers.AssertRoundtrip(Proto2.TestAllTypes.Parser, message);
         }
 
         [Test]
@@ -361,9 +359,9 @@ namespace Google.Protobuf
                 new RepeatedGroup_extension { A = 30 }
             });
 
-            byte[] bytes = message.ToByteArray();
-            TestAllExtensions extendable_parsed = TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { UnittestExtensions.OptionalGroupExtension, UnittestExtensions.RepeatedGroupExtension }).ParseFrom(bytes);
-            Assert.AreEqual(message, extendable_parsed);
+            MessageParsingHelpers.AssertRoundtrip(
+                TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { UnittestExtensions.OptionalGroupExtension, UnittestExtensions.RepeatedGroupExtension }),
+                message);
         }
 
         [Test]
@@ -372,9 +370,9 @@ namespace Google.Protobuf
             var message = new TestGroupExtension();
             message.SetExtension(TestNestedExtension.Extensions.OptionalGroupExtension, new TestNestedExtension.Types.OptionalGroup_extension { A = 10 });
 
-            byte[] bytes = message.ToByteArray();
-            TestGroupExtension extendable_parsed = TestGroupExtension.Parser.WithExtensionRegistry(new ExtensionRegistry() { TestNestedExtension.Extensions.OptionalGroupExtension }).ParseFrom(bytes);
-            Assert.AreEqual(message, extendable_parsed);
+            MessageParsingHelpers.AssertRoundtrip(
+                TestGroupExtension.Parser.WithExtensionRegistry(new ExtensionRegistry() { TestNestedExtension.Extensions.OptionalGroupExtension }),
+                message);
         }
     }
 }

+ 98 - 44
csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs

@@ -131,8 +131,8 @@ namespace Google.Protobuf
             // Without setting any values, there's nothing to write.
             byte[] bytes = message.ToByteArray();
             Assert.AreEqual(0, bytes.Length);
-            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, parsed);
+
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
         }
 
         [Test]
@@ -165,8 +165,8 @@ namespace Google.Protobuf
             };
 
             byte[] bytes = message.ToByteArray();
-            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, parsed);
+
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
         }
 
         [Test]
@@ -199,8 +199,8 @@ namespace Google.Protobuf
             };
 
             byte[] bytes = message.ToByteArray();
-            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, parsed);
+
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
         }
 
         // Note that not every map within map_unittest_proto3 is used. They all go through very
@@ -231,8 +231,8 @@ namespace Google.Protobuf
             };
 
             byte[] bytes = message.ToByteArray();
-            TestMap parsed = TestMap.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, parsed);
+
+            MessageParsingHelpers.AssertRoundtrip(TestMap.Parser, message);
         }
 
         [Test]
@@ -246,9 +246,14 @@ namespace Google.Protobuf
             byte[] bytes = message.ToByteArray();
             Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)
 
-            var parsed = TestMap.Parser.ParseFrom(bytes);
-            Assert.AreEqual(1, parsed.MapInt32Bytes.Count);
-            Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                bytes,
+                parsed=>
+                {
+                    Assert.AreEqual(1, parsed.MapInt32Bytes.Count);
+                    Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);
+                });
         }
 
         [Test]
@@ -265,8 +270,13 @@ namespace Google.Protobuf
             output.WriteMessage(nestedMessage);
             output.Flush();
 
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
+                });
         }
 
         [Test]
@@ -282,8 +292,13 @@ namespace Google.Protobuf
             output.WriteInt32(key);
             output.Flush();
 
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
+                });
         }
 
         [Test]
@@ -299,8 +314,13 @@ namespace Google.Protobuf
             output.WriteInt32(key);
             output.Flush();
 
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
+                });
         }
 
         [Test]
@@ -327,8 +347,13 @@ namespace Google.Protobuf
             output.WriteInt32(extra);
             output.Flush();
 
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+                });
         }
 
         [Test]
@@ -351,8 +376,13 @@ namespace Google.Protobuf
             output.WriteInt32(key);
             output.Flush();
 
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+                });
         }
 
         [Test]
@@ -397,13 +427,19 @@ namespace Google.Protobuf
             output.WriteInt32(value3);
 
             output.Flush();
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            var expected = new TestMap
-            {
-                MapInt32Int32 = { { key1, value1 }, { key3, value3 } },
-                MapStringString = { { key2, value2 } }
-            };
-            Assert.AreEqual(expected, parsed);
+
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    var expected = new TestMap
+                    {
+                        MapInt32Int32 = { { key1, value1 }, { key3, value3 } },
+                        MapStringString = { { key2, value2 } }
+                    };
+                    Assert.AreEqual(expected, parsed);
+                });
         }
 
         [Test]
@@ -433,8 +469,13 @@ namespace Google.Protobuf
             output.WriteInt32(value2);
             output.Flush();
 
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            Assert.AreEqual(value2, parsed.MapInt32Int32[key]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(value2, parsed.MapInt32Int32[key]);
+                });
         }
 
         [Test]
@@ -619,9 +660,10 @@ namespace Google.Protobuf
             var bytes = message.ToByteArray();
             Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - no string!
 
-            var message2 = TestAllTypes.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, message2);
-            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
+            {
+                Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);
+            });
         }
 
         [Test]
@@ -633,9 +675,10 @@ 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
 
-            var message2 = TestAllTypes.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, message2);
-            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
+            {
+                Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);
+            });
         }
 
         [Test]
@@ -651,10 +694,14 @@ namespace Google.Protobuf
             message.WriteTo(output);
             output.Flush();
 
-            stream.Position = 0;
-            var parsed = TestAllTypes.Parser.ParseFrom(stream);
-            // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
-            // Assert.AreEqual(message, parsed);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestAllTypes.Parser,
+                stream.ToArray(),
+                parsed =>
+                {
+                    // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
+                    // Assert.AreEqual(message, parsed);
+                });
         }
 
         [Test]
@@ -663,8 +710,15 @@ namespace Google.Protobuf
             // Simple way of ensuring we can skip all kinds of fields.
             var data = SampleMessages.CreateFullTestAllTypes().ToByteArray();
             var empty = Empty.Parser.ParseFrom(data);
-            // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
-            // Assert.AreNotEqual(new Empty(), empty);
+
+            MessageParsingHelpers.AssertReadingMessage(
+                Empty.Parser,
+                data,
+                parsed =>
+                {
+                    // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
+                    // Assert.AreNotEqual(new Empty(), empty);
+                });
         }
 
         // This was originally seen as a conformance test failure.
@@ -674,7 +728,7 @@ namespace Google.Protobuf
             // 130, 3 is the message tag
             // 1 is the data length - but there's no data.
             var data = new byte[] { 130, 3, 1 };
-            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(data));
+            MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, data);
         }
 
         /// <summary>
@@ -695,7 +749,7 @@ namespace Google.Protobuf
             output.Flush();
 
             stream.Position = 0;
-            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(stream));
+            MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, stream.ToArray());
         }
 
         [Test]

+ 233 - 0
csharp/src/Google.Protobuf.Test/LegacyGeneratedCodeTest.cs

@@ -0,0 +1,233 @@
+#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 Google.Protobuf;
+using Google.Protobuf.Reflection;
+using System.Buffers;
+using pb = global::Google.Protobuf;
+using pbr = global::Google.Protobuf.Reflection;
+using NUnit.Framework;
+
+
+namespace Google.Protobuf
+{
+    public class LegacyGeneratedCodeTest
+    {
+        [Test]
+        public void IntermixingOfNewAndLegacyGeneratedCodeWorksWithCodedInputStream()
+        {
+            var message = new ParseContextEnabledMessageB
+            {
+              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.
+            var codedInput = new CodedInputStream(data);
+            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 LegacyGeneratedCodeThrowsWithReadOnlySequence()
+        {
+            var message = new ParseContextEnabledMessageB
+            {
+              A = new LegacyGeneratedCodeMessageA
+              {
+                Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
+              },
+              OptionalInt32 = 6789
+            };
+            var data = message.ToByteArray();
+
+            // if parsing started using ReadOnlySequence and we don't have a CodedInputStream
+            // instance at hand, we cannot fall back to the legacy MergeFrom(CodedInputStream)
+            // method and parsing will fail. As a consequence, one can only use parsing
+            // from ReadOnlySequence if all the messages in the parsing tree have their generated
+            // 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);
+            });
+            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);
+        }
+
+        // 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 {
+          private pb::UnknownFieldSet _unknownFields;
+
+          pbr::MessageDescriptor pb::IMessage.Descriptor => throw new System.NotImplementedException();
+
+          /// <summary>Field number for the "bb" field.</summary>
+          public const int BbFieldNumber = 1;
+          private ParseContextEnabledMessageB bb_;
+          public ParseContextEnabledMessageB Bb {
+            get { return bb_; }
+            set {
+              bb_ = value;
+            }
+          }
+
+          public void WriteTo(pb::CodedOutputStream output) {
+            if (bb_ != null) {
+              output.WriteRawTag(10);
+              output.WriteMessage(Bb);
+            }
+            if (_unknownFields != null) {
+              _unknownFields.WriteTo(output);
+            }
+          }
+
+          public int CalculateSize() {
+            int size = 0;
+            if (bb_ != null) {
+              size += 1 + pb::CodedOutputStream.ComputeMessageSize(Bb);
+            }
+            if (_unknownFields != null) {
+              size += _unknownFields.CalculateSize();
+            }
+            return size;
+          }
+
+          public void MergeFrom(pb::CodedInputStream input) {
+            uint tag;
+            while ((tag = input.ReadTag()) != 0) {
+              switch(tag) {
+                default:
+                  _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                  break;
+                case 10: {
+                  if (bb_ == null) {
+                    Bb = new ParseContextEnabledMessageB();
+                  }
+                  input.ReadMessage(Bb);
+                  break;
+                }
+              }
+            }
+          }
+        }
+
+        // hand-modified version of a generated message that does provide
+        // the new InternalMergeFrom(ref ParseContext) method.
+        private sealed partial class ParseContextEnabledMessageB : pb::IBufferMessage {
+          private pb::UnknownFieldSet _unknownFields;
+
+          pbr::MessageDescriptor pb::IMessage.Descriptor => throw new System.NotImplementedException();
+
+          /// <summary>Field number for the "a" field.</summary>
+          public const int AFieldNumber = 1;
+          private LegacyGeneratedCodeMessageA a_;
+          public LegacyGeneratedCodeMessageA A {
+            get { return a_; }
+            set {
+              a_ = value;
+            }
+          }
+
+          /// <summary>Field number for the "optional_int32" field.</summary>
+          public const int OptionalInt32FieldNumber = 2;
+          private int optionalInt32_;
+          public int OptionalInt32 {
+            get { return optionalInt32_; }
+            set {
+              optionalInt32_ = value;
+            }
+          }
+
+          public void WriteTo(pb::CodedOutputStream output) {
+            if (a_ != null) {
+              output.WriteRawTag(10);
+              output.WriteMessage(A);
+            }
+            if (OptionalInt32 != 0) {
+              output.WriteRawTag(16);
+              output.WriteInt32(OptionalInt32);
+            }
+            if (_unknownFields != null) {
+              _unknownFields.WriteTo(output);
+            }
+          }
+          public int CalculateSize() {
+            int size = 0;
+            if (a_ != null) {
+              size += 1 + pb::CodedOutputStream.ComputeMessageSize(A);
+            }
+            if (OptionalInt32 != 0) {
+              size += 1 + pb::CodedOutputStream.ComputeInt32Size(OptionalInt32);
+            }
+            if (_unknownFields != null) {
+              size += _unknownFields.CalculateSize();
+            }
+            return size;
+          }
+          public void MergeFrom(pb::CodedInputStream input) {
+            input.ReadRawMessage(this);
+          }
+
+          void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+            uint tag;
+            while ((tag = input.ReadTag()) != 0) {
+              switch(tag) {
+                default:
+                  _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+                  break;
+                case 10: {
+                  if (a_ == null) {
+                    A = new LegacyGeneratedCodeMessageA();
+                  }
+                  input.ReadMessage(A);
+                  break;
+                }
+                case 16: {
+                  OptionalInt32 = input.ReadInt32();
+                  break;
+                }
+              }
+            }
+          }
+        }
+    }
+}

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

@@ -0,0 +1,100 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 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 NUnit.Framework;
+using System;
+using System.Buffers;
+
+namespace Google.Protobuf
+{
+    public static class MessageParsingHelpers
+    {
+        public static void AssertReadingMessage<T>(MessageParser<T> parser, byte[] bytes, Action<T> assert) where T : IMessage<T>
+        {
+            var parsedStream = parser.ParseFrom(bytes);
+
+            // Load content as single segment
+            var parsedBuffer = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
+            assert(parsedBuffer);
+
+            // Load content as multiple segments
+            parsedBuffer = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
+            assert(parsedBuffer);
+
+            assert(parsedStream);
+        }
+
+        public static void AssertReadingMessage(MessageParser parser, byte[] bytes, Action<IMessage> assert)
+        {
+            var parsedStream = parser.ParseFrom(bytes);
+
+            // Load content as single segment
+            var parsedBuffer = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
+            assert(parsedBuffer);
+
+            // Load content as multiple segments
+            parsedBuffer = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
+            assert(parsedBuffer);
+
+            assert(parsedStream);
+        }
+
+        public static void AssertReadingMessageThrows<TMessage, TException>(MessageParser<TMessage> parser, byte[] bytes)
+            where TMessage : IMessage<TMessage>
+            where TException : Exception
+        {
+            Assert.Throws<TException>(() => parser.ParseFrom(bytes));
+
+            Assert.Throws<TException>(() => parser.ParseFrom(new ReadOnlySequence<byte>(bytes)));
+        }
+
+        public static void AssertRoundtrip<T>(MessageParser<T> parser, T message, Action<T> additionalAssert = null) where T : IMessage<T>
+        {
+            var bytes = message.ToByteArray();
+
+            // Load content as single segment
+            var parsedBuffer = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
+            Assert.AreEqual(message, parsedBuffer);
+            additionalAssert?.Invoke(parsedBuffer);
+
+            // Load content as multiple segments
+            parsedBuffer = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
+            Assert.AreEqual(message, parsedBuffer);
+            additionalAssert?.Invoke(parsedBuffer);
+
+            var parsedStream = parser.ParseFrom(bytes);
+
+            Assert.AreEqual(message, parsedStream);
+            additionalAssert?.Invoke(parsedStream);
+        }
+    }
+}

+ 128 - 0
csharp/src/Google.Protobuf.Test/ReadOnlySequenceFactory.cs

@@ -0,0 +1,128 @@
+#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.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Google.Protobuf
+{
+    internal static class ReadOnlySequenceFactory
+    {
+        public static ReadOnlySequence<byte> CreateWithContent(byte[] data, int segmentSize = 1)
+        {
+            var segments = new List<byte[]>();
+
+            segments.Add(new byte[0]);
+            var currentIndex = 0;
+            while (currentIndex < data.Length)
+            {
+                var segment = new List<byte>();
+                for (; currentIndex < Math.Min(currentIndex + segmentSize, data.Length); currentIndex++)
+                {
+                    segment.Add(data[currentIndex]);
+                }
+                segments.Add(segment.ToArray());
+                segments.Add(new byte[0]);
+            }
+
+            return CreateSegments(segments.ToArray());
+        }
+
+        /// <summary>
+        /// Originally from corefx, and has been contributed to Protobuf
+        /// https://github.com/dotnet/corefx/blob/e99ec129cfd594d53f4390bf97d1d736cff6f860/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.byte.cs
+        /// </summary>
+        private static ReadOnlySequence<byte> CreateSegments(params byte[][] inputs)
+        {
+            if (inputs == null || inputs.Length == 0)
+            {
+                throw new InvalidOperationException();
+            }
+
+            int i = 0;
+
+            BufferSegment last = null;
+            BufferSegment first = null;
+
+            do
+            {
+                byte[] s = inputs[i];
+                int length = s.Length;
+                int dataOffset = length;
+                var chars = new byte[length * 2];
+
+                for (int j = 0; j < length; j++)
+                {
+                    chars[dataOffset + j] = s[j];
+                }
+
+                // Create a segment that has offset relative to the OwnedMemory and OwnedMemory itself has offset relative to array
+                var memory = new Memory<byte>(chars).Slice(length, length);
+
+                if (first == null)
+                {
+                    first = new BufferSegment(memory);
+                    last = first;
+                }
+                else
+                {
+                    last = last.Append(memory);
+                }
+                i++;
+            } while (i < inputs.Length);
+
+            return new ReadOnlySequence<byte>(first, 0, last, last.Memory.Length);
+        }
+
+        private class BufferSegment : ReadOnlySequenceSegment<byte>
+        {
+            public BufferSegment(Memory<byte> memory)
+            {
+                Memory = memory;
+            }
+
+            public BufferSegment Append(Memory<byte> memory)
+            {
+                var segment = new BufferSegment(memory)
+                {
+                    RunningIndex = RunningIndex + Memory.Length
+                };
+                Next = segment;
+                return segment;
+            }
+        }
+    }
+}

+ 4 - 4
csharp/src/Google.Protobuf.Test/UnknownFieldSetTest.cs

@@ -161,10 +161,10 @@ namespace Google.Protobuf
             MessageParser discardingParser2 = retainingParser2.WithDiscardUnknownFields(true);
 
             // Test parse from byte[]
-            assertFull(retainingParser1.ParseFrom(data));
-            assertFull(retainingParser2.ParseFrom(data));
-            assertEmpty(discardingParser1.ParseFrom(data));
-            assertEmpty(discardingParser2.ParseFrom(data));
+            MessageParsingHelpers.AssertReadingMessage(retainingParser1, data, m => assertFull(m));
+            MessageParsingHelpers.AssertReadingMessage(retainingParser2, data, m => assertFull(m));
+            MessageParsingHelpers.AssertReadingMessage(discardingParser1, data, m => assertEmpty(m));
+            MessageParsingHelpers.AssertReadingMessage(discardingParser2, data, m => assertEmpty(m));
 
             // Test parse from byte[] with offset
             assertFull(retainingParser1.ParseFrom(data, 0, data.Length));

+ 48 - 43
csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs

@@ -71,18 +71,18 @@ namespace Google.Protobuf.WellKnownTypes
                 Uint64Field = 4
             };
 
-            var bytes = message.ToByteArray();
-            var parsed = TestWellKnownTypes.Parser.ParseFrom(bytes);
-
-            Assert.AreEqual("x", parsed.StringField);
-            Assert.AreEqual(ByteString.CopyFrom(1, 2, 3), parsed.BytesField);
-            Assert.AreEqual(true, parsed.BoolField);
-            Assert.AreEqual(12.5f, parsed.FloatField);
-            Assert.AreEqual(12.25d, parsed.DoubleField);
-            Assert.AreEqual(1, parsed.Int32Field);
-            Assert.AreEqual(2L, parsed.Int64Field);
-            Assert.AreEqual(3U, parsed.Uint32Field);
-            Assert.AreEqual(4UL, parsed.Uint64Field);
+            MessageParsingHelpers.AssertRoundtrip(TestWellKnownTypes.Parser, message, parsed =>
+            {
+                Assert.AreEqual("x", parsed.StringField);
+                Assert.AreEqual(ByteString.CopyFrom(1, 2, 3), parsed.BytesField);
+                Assert.AreEqual(true, parsed.BoolField);
+                Assert.AreEqual(12.5f, parsed.FloatField);
+                Assert.AreEqual(12.25d, parsed.DoubleField);
+                Assert.AreEqual(1, parsed.Int32Field);
+                Assert.AreEqual(2L, parsed.Int64Field);
+                Assert.AreEqual(3U, parsed.Uint32Field);
+                Assert.AreEqual(4UL, parsed.Uint64Field);
+            });
         }
 
         [Test]
@@ -101,18 +101,18 @@ namespace Google.Protobuf.WellKnownTypes
                 Uint64Field = 0
             };
 
-            var bytes = message.ToByteArray();
-            var parsed = TestWellKnownTypes.Parser.ParseFrom(bytes);
-
-            Assert.AreEqual("", parsed.StringField);
-            Assert.AreEqual(ByteString.Empty, parsed.BytesField);
-            Assert.AreEqual(false, parsed.BoolField);
-            Assert.AreEqual(0f, parsed.FloatField);
-            Assert.AreEqual(0d, parsed.DoubleField);
-            Assert.AreEqual(0, parsed.Int32Field);
-            Assert.AreEqual(0L, parsed.Int64Field);
-            Assert.AreEqual(0U, parsed.Uint32Field);
-            Assert.AreEqual(0UL, parsed.Uint64Field);
+            MessageParsingHelpers.AssertRoundtrip(TestWellKnownTypes.Parser, message, parsed =>
+            {
+                Assert.AreEqual("", parsed.StringField);
+                Assert.AreEqual(ByteString.Empty, parsed.BytesField);
+                Assert.AreEqual(false, parsed.BoolField);
+                Assert.AreEqual(0f, parsed.FloatField);
+                Assert.AreEqual(0d, parsed.DoubleField);
+                Assert.AreEqual(0, parsed.Int32Field);
+                Assert.AreEqual(0L, parsed.Int64Field);
+                Assert.AreEqual(0U, parsed.Uint32Field);
+                Assert.AreEqual(0UL, parsed.Uint64Field);
+            });
         }
 
         [Test]
@@ -140,12 +140,11 @@ namespace Google.Protobuf.WellKnownTypes
                 Uint32Field = { uint.MaxValue, uint.MinValue, 0U },
                 Uint64Field = { ulong.MaxValue, ulong.MinValue, 0UL },
             };
-            var bytes = message.ToByteArray();
-            var parsed = RepeatedWellKnownTypes.Parser.ParseFrom(bytes);
 
-            Assert.AreEqual(message, parsed);
             // Just to test a single value for sanity...
             Assert.AreEqual("Second", message.StringField[1]);
+
+            MessageParsingHelpers.AssertRoundtrip(RepeatedWellKnownTypes.Parser, message);
         }
 
         [Test]
@@ -194,12 +193,10 @@ namespace Google.Protobuf.WellKnownTypes
                 Uint64Field = { { 18, ulong.MaxValue }, { 19, ulong.MinValue }, { 20, 0UL } },
             };
 
-            var bytes = message.ToByteArray();
-            var parsed = MapWellKnownTypes.Parser.ParseFrom(bytes);
-
-            Assert.AreEqual(message, parsed);
             // Just to test a single value for sanity...
             Assert.AreEqual("Second", message.StringField[12]);
+
+            MessageParsingHelpers.AssertRoundtrip(MapWellKnownTypes.Parser, message);
         }
 
         [Test]
@@ -288,10 +285,10 @@ namespace Google.Protobuf.WellKnownTypes
         private void AssertOneofRoundTrip(OneofWellKnownTypes message)
         {
             // Normal roundtrip, but explicitly checking the case...
-            var bytes = message.ToByteArray();
-            var parsed = OneofWellKnownTypes.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, parsed);
-            Assert.AreEqual(message.OneofFieldCase, parsed.OneofFieldCase);
+            MessageParsingHelpers.AssertRoundtrip(OneofWellKnownTypes.Parser, message, parsed =>
+            {
+                Assert.AreEqual(message.OneofFieldCase, parsed.OneofFieldCase);
+            });
         }
 
         [Test]
@@ -406,8 +403,10 @@ namespace Google.Protobuf.WellKnownTypes
             Assert.AreEqual(8, stream.Length); // tag (1 byte) + length (1 byte) + message (6 bytes)
             stream.Position = 0;
 
-            var message = TestWellKnownTypes.Parser.ParseFrom(stream);
-            Assert.AreEqual(65536, message.Int32Field);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestWellKnownTypes.Parser,
+                stream.ToArray(),
+                message => Assert.AreEqual(65536, message.Int32Field));
         }
 
         [Test]
@@ -431,8 +430,10 @@ namespace Google.Protobuf.WellKnownTypes
             Assert.Less(stream.Length, 8); // tag (1 byte) + length (1 byte) + message
             stream.Position = 0;
 
-            var message = TestWellKnownTypes.Parser.ParseFrom(stream);
-            Assert.AreEqual(6, message.Int32Field);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestWellKnownTypes.Parser,
+                stream.ToArray(),
+                message => Assert.AreEqual(6, message.Int32Field));
         }
 
         [Test]
@@ -456,8 +457,10 @@ namespace Google.Protobuf.WellKnownTypes
             Assert.AreEqual(13, stream.Length); // tag (1 byte) + length (1 byte) + message (11 bytes)
             stream.Position = 0;
 
-            var message = TestWellKnownTypes.Parser.ParseFrom(stream);
-            Assert.AreEqual(0xfffffffffffffL, message.Int64Field);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestWellKnownTypes.Parser,
+                stream.ToArray(),
+                message => Assert.AreEqual(0xfffffffffffffL, message.Int64Field));
         }
 
         [Test]
@@ -481,8 +484,10 @@ namespace Google.Protobuf.WellKnownTypes
             Assert.Less(stream.Length, 12); // tag (1 byte) + length (1 byte) + message
             stream.Position = 0;
 
-            var message = TestWellKnownTypes.Parser.ParseFrom(stream);
-            Assert.AreEqual(6L, message.Int64Field);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestWellKnownTypes.Parser,
+                stream.ToArray(),
+                message => Assert.AreEqual(6L, message.Int64Field));
         }
 
         [Test]

+ 2 - 2
csharp/src/Google.Protobuf/ByteString.cs

@@ -160,7 +160,7 @@ namespace Google.Protobuf
             int capacity = stream.CanSeek ? checked((int) (stream.Length - stream.Position)) : 0;
             var memoryStream = new MemoryStream(capacity);
             stream.CopyTo(memoryStream);
-#if NETSTANDARD1_0 || NETSTANDARD2_0
+#if NETSTANDARD1_1 || NETSTANDARD2_0
             byte[] bytes = memoryStream.ToArray();
 #else
             // Avoid an extra copy if we can.
@@ -186,7 +186,7 @@ namespace Google.Protobuf
             // We have to specify the buffer size here, as there's no overload accepting the cancellation token
             // alone. But it's documented to use 81920 by default if not specified.
             await stream.CopyToAsync(memoryStream, 81920, cancellationToken);
-#if NETSTANDARD1_0 || NETSTANDARD2_0
+#if NETSTANDARD1_1 || NETSTANDARD2_0
             byte[] bytes = memoryStream.ToArray();
 #else
             // Avoid an extra copy if we can.

文件差异内容过多而无法显示
+ 82 - 716
csharp/src/Google.Protobuf/CodedInputStream.cs


+ 52 - 9
csharp/src/Google.Protobuf/Collections/MapField.cs

@@ -33,10 +33,12 @@
 using Google.Protobuf.Compatibility;
 using Google.Protobuf.Reflection;
 using System;
+using System.Buffers;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using System.Security;
 
 namespace Google.Protobuf.Collections
 {
@@ -421,14 +423,38 @@ namespace Google.Protobuf.Collections
         /// <param name="input">Stream to read from</param>
         /// <param name="codec">Codec describing how the key/value pairs are encoded</param>
         public void AddEntriesFrom(CodedInputStream input, Codec codec)
+        {
+            ParseContext.Initialize(input, out ParseContext ctx);
+            try
+            {
+                AddEntriesFrom(ref ctx, codec);
+            }
+            finally
+            {
+                ctx.CopyStateTo(input);
+            }
+        }
+
+        /// <summary>
+        /// Adds entries to the map from the given parse context.
+        /// </summary>
+        /// <remarks>
+        /// It is assumed that the input is initially positioned after the tag specified by the codec.
+        /// This method will continue reading entries from the input until the end is reached, or
+        /// a different tag is encountered.
+        /// </remarks>
+        /// <param name="ctx">Input to read from</param>
+        /// <param name="codec">Codec describing how the key/value pairs are encoded</param>
+        [SecuritySafeCritical]
+        public void AddEntriesFrom(ref ParseContext ctx, Codec codec)
         {
             var adapter = new Codec.MessageAdapter(codec);
             do
             {
                 adapter.Reset();
-                input.ReadMessage(adapter);
+                ctx.ReadMessage(adapter);
                 this[adapter.Key] = adapter.Value;
-            } while (input.MaybeConsumeTag(codec.MapTag));
+            } while (ParsingPrimitives.MaybeConsumeTag(ref ctx.buffer, ref ctx.state, codec.MapTag));
         }
 
         /// <summary>
@@ -620,7 +646,7 @@ namespace Google.Protobuf.Collections
             /// This is nested inside Codec as it's tightly coupled to the associated codec,
             /// and it's simpler if it has direct access to all its fields.
             /// </summary>
-            internal class MessageAdapter : IMessage
+            internal class MessageAdapter : IMessage, IBufferMessage
             {
                 private static readonly byte[] ZeroLengthMessageStreamData = new byte[] { 0 };
 
@@ -640,29 +666,46 @@ namespace Google.Protobuf.Collections
                 }
 
                 public void MergeFrom(CodedInputStream input)
+                {
+                    // Message adapter is an internal class and we know that all the parsing will happen via InternalMergeFrom.
+                    throw new NotImplementedException();
+                }
+
+                [SecuritySafeCritical]
+                public void InternalMergeFrom(ref ParseContext ctx)
                 {
                     uint tag;
-                    while ((tag = input.ReadTag()) != 0)
+                    while ((tag = ctx.ReadTag()) != 0)
                     {
                         if (tag == codec.keyCodec.Tag)
                         {
-                            Key = codec.keyCodec.Read(input);
+                            Key = codec.keyCodec.Read(ref ctx);
                         }
                         else if (tag == codec.valueCodec.Tag)
                         {
-                            Value = codec.valueCodec.Read(input);
+                            Value = codec.valueCodec.Read(ref ctx);
                         }
                         else 
                         {
-                            input.SkipLastField();
+                            ParsingPrimitivesMessages.SkipLastField(ref ctx.buffer, ref ctx.state);
                         }
                     }
 
                     // Corner case: a map entry with a key but no value, where the value type is a message.
-                    // Read it as if we'd seen an input stream with no data (i.e. create a "default" message).
+                    // Read it as if we'd seen input with no data (i.e. create a "default" message).
                     if (Value == null)
                     {
-                        Value = codec.valueCodec.Read(new CodedInputStream(ZeroLengthMessageStreamData));
+                        if (ctx.state.CodedInputStream != null)
+                        {
+                            // the decoded message might not support parsing from ParseContext, so
+                            // we need to allow fallback to the legacy MergeFrom(CodedInputStream) parsing.
+                            Value = codec.valueCodec.Read(new CodedInputStream(ZeroLengthMessageStreamData));
+                        }
+                        else
+                        {
+                            ParseContext.Initialize(new ReadOnlySequence<byte>(ZeroLengthMessageStreamData), out ParseContext zeroLengthCtx);
+                            Value = codec.valueCodec.Read(ref zeroLengthCtx);
+                        }
                     }
                 }
 

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

@@ -34,6 +34,7 @@ using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
+using System.Security;
 
 namespace Google.Protobuf.Collections
 {
@@ -94,23 +95,42 @@ namespace Google.Protobuf.Collections
         /// <param name="input">The input stream to read from.</param>
         /// <param name="codec">The codec to use in order to read each entry.</param>
         public void AddEntriesFrom(CodedInputStream input, FieldCodec<T> codec)
+        {
+            ParseContext.Initialize(input, out ParseContext ctx);
+            try
+            {
+                AddEntriesFrom(ref ctx, codec);
+            }
+            finally
+            {
+                ctx.CopyStateTo(input);
+            }
+        }
+
+        /// <summary>
+        /// Adds the entries from the given parse context, decoding them with the specified codec.
+        /// </summary>
+        /// <param name="ctx">The input to read from.</param>
+        /// <param name="codec">The codec to use in order to read each entry.</param>
+        [SecuritySafeCritical]
+        public void AddEntriesFrom(ref ParseContext ctx, FieldCodec<T> codec)
         {
             // TODO: Inline some of the Add code, so we can avoid checking the size on every
             // iteration.
-            uint tag = input.LastTag;
+            uint tag = ctx.state.lastTag;
             var reader = codec.ValueReader;
             // Non-nullable value types can be packed or not.
             if (FieldCodec<T>.IsPackedRepeatedField(tag))
             {
-                int length = input.ReadLength();
+                int length = ctx.ReadLength();
                 if (length > 0)
                 {
-                    int oldLimit = input.PushLimit(length);
-                    while (!input.ReachedLimit)
+                    int oldLimit = SegmentedBufferHelper.PushLimit(ref ctx.state, length);
+                    while (!SegmentedBufferHelper.IsReachedLimit(ref ctx.state))
                     {
-                        Add(reader(input));
+                        Add(reader(ref ctx));
                     }
-                    input.PopLimit(oldLimit);
+                    SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit);
                 }
                 // Empty packed field. Odd, but valid - just ignore.
             }
@@ -119,8 +139,8 @@ namespace Google.Protobuf.Collections
                 // Not packed... (possibly not packable)
                 do
                 {
-                    Add(reader(input));
-                } while (input.MaybeConsumeTag(tag));
+                    Add(reader(ref ctx));
+                } while (ParsingPrimitives.MaybeConsumeTag(ref ctx.buffer, ref ctx.state, tag));
             }
         }
 

+ 2 - 2
csharp/src/Google.Protobuf/ExtensionRegistry.cs

@@ -80,9 +80,9 @@ namespace Google.Protobuf
         /// </summary>
         bool ICollection<Extension>.IsReadOnly => false;
 
-        internal bool ContainsInputField(CodedInputStream stream, Type target, out Extension extension)
+        internal bool ContainsInputField(uint lastTag, Type target, out Extension extension)
         {
-            return extensions.TryGetValue(new ObjectIntPair<Type>(target, WireFormat.GetTagFieldNumber(stream.LastTag)), out extension);
+            return extensions.TryGetValue(new ObjectIntPair<Type>(target, WireFormat.GetTagFieldNumber(lastTag)), out extension);
         }
 
         /// <summary>

+ 22 - 5
csharp/src/Google.Protobuf/ExtensionSet.cs

@@ -182,20 +182,37 @@ namespace Google.Protobuf
         /// If the set is null or the field was not otherwise merged, this returns false.
         /// </summary>
         public static bool TryMergeFieldFrom<TTarget>(ref ExtensionSet<TTarget> set, CodedInputStream stream) where TTarget : IExtendableMessage<TTarget>
+        {
+            ParseContext.Initialize(stream, out ParseContext ctx);
+            try
+            {
+                return TryMergeFieldFrom<TTarget>(ref set, ref ctx);
+            }
+            finally
+            {
+                ctx.CopyStateTo(stream);
+            }
+        }
+
+        /// <summary>
+        /// Tries to merge a field from the coded input, returning true if the field was merged.
+        /// If the set is null or the field was not otherwise merged, this returns false.
+        /// </summary>
+        public static bool TryMergeFieldFrom<TTarget>(ref ExtensionSet<TTarget> set, ref ParseContext ctx) where TTarget : IExtendableMessage<TTarget>
         {
             Extension extension;
-            int lastFieldNumber = WireFormat.GetTagFieldNumber(stream.LastTag);
-            
+            int lastFieldNumber = WireFormat.GetTagFieldNumber(ctx.LastTag);
+
             IExtensionValue extensionValue;
             if (set != null && set.ValuesByNumber.TryGetValue(lastFieldNumber, out extensionValue))
             {
-                extensionValue.MergeFrom(stream);
+                extensionValue.MergeFrom(ref ctx);
                 return true;
             }
-            else if (stream.ExtensionRegistry != null && stream.ExtensionRegistry.ContainsInputField(stream, typeof(TTarget), out extension))
+            else if (ctx.ExtensionRegistry != null && ctx.ExtensionRegistry.ContainsInputField(ctx.LastTag, typeof(TTarget), out extension))
             {
                 IExtensionValue value = extension.CreateValue();
-                value.MergeFrom(stream);
+                value.MergeFrom(ref ctx);
                 set = (set ?? new ExtensionSet<TTarget>());
                 set.ValuesByNumber.Add(extension.FieldNumber, value);
                 return true;

+ 30 - 24
csharp/src/Google.Protobuf/ExtensionValue.cs

@@ -38,7 +38,8 @@ namespace Google.Protobuf
 {
     internal interface IExtensionValue : IEquatable<IExtensionValue>, IDeepCloneable<IExtensionValue>
     {
-        void MergeFrom(CodedInputStream input);
+        void MergeFrom(ref ParseContext ctx);
+
         void MergeFrom(IExtensionValue value);
         void WriteTo(CodedOutputStream output);
         int CalculateSize();
@@ -91,16 +92,16 @@ namespace Google.Protobuf
             }
         }
 
-        public void MergeFrom(CodedInputStream input)
+        public void MergeFrom(ref ParseContext ctx)
         {
-            codec.ValueMerger(input, ref field);
+            codec.ValueMerger(ref ctx, ref field);
         }
 
         public void MergeFrom(IExtensionValue value)
         {
             if (value is ExtensionValue<T>)
             {
-                var extensionValue = value as ExtensionValue<T>;
+                var extensionValue = value as ExtensionValue<T>;
                 codec.FieldMerger(ref field, extensionValue.field);
             }
         }
@@ -124,13 +125,13 @@ namespace Google.Protobuf
 
         public bool IsInitialized()
         {
-            if (field is IMessage)
-            {
-                return (field as IMessage).IsInitialized();
+            if (field is IMessage)
+            {
+                return (field as IMessage).IsInitialized();
             }
-            else
-            {
-                return true;
+            else
+            {
+                return true;
             }
         }
     }
@@ -185,6 +186,11 @@ namespace Google.Protobuf
             field.AddEntriesFrom(input, codec);
         }
 
+        public void MergeFrom(ref ParseContext ctx)
+        {
+            field.AddEntriesFrom(ref ctx, codec);
+        }
+
         public void MergeFrom(IExtensionValue value)
         {
             if (value is RepeatedExtensionValue<T>)
@@ -202,20 +208,20 @@ namespace Google.Protobuf
 
         public bool IsInitialized()
         {
-            for (int i = 0; i < field.Count; i++)
-            {
-                var element = field[i];
-                if (element is IMessage)
-                {
-                    if (!(element as IMessage).IsInitialized())
-                    {
-                        return false;
-                    }
-                }
-                else
-                {
-                    break;
-                }
+            for (int i = 0; i < field.Count; i++)
+            {
+                var element = field[i];
+                if (element is IMessage)
+                {
+                    if (!(element as IMessage).IsInitialized())
+                    {
+                        return false;
+                    }
+                }
+                else
+                {
+                    break;
+                }
             }
 
             return true;

+ 81 - 56
csharp/src/Google.Protobuf/FieldCodec.cs

@@ -35,6 +35,7 @@ using Google.Protobuf.Compatibility;
 using Google.Protobuf.WellKnownTypes;
 using System;
 using System.Collections.Generic;
+using System.Security;
 
 namespace Google.Protobuf
 {
@@ -218,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>(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag, defaultValue);
+            return new FieldCodec<string>((ref ParseContext ctx) => ctx.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag, defaultValue);
         }
 
         /// <summary>
@@ -229,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>(input => input.ReadBytes(), (output, value) => output.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag, defaultValue);
+            return new FieldCodec<ByteString>((ref ParseContext ctx) => ctx.ReadBytes(), (output, value) => output.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag, defaultValue);
         }
 
         /// <summary>
@@ -240,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>(input => input.ReadBool(), (output, value) => output.WriteBool(value), CodedOutputStream.BoolSize, tag, defaultValue);
+            return new FieldCodec<bool>((ref ParseContext ctx) => ctx.ReadBool(), (output, value) => output.WriteBool(value), CodedOutputStream.BoolSize, tag, defaultValue);
         }
 
         /// <summary>
@@ -251,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>(input => input.ReadInt32(), (output, value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag, defaultValue);
+            return new FieldCodec<int>((ref ParseContext ctx) => ctx.ReadInt32(), (output, value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag, defaultValue);
         }
 
         /// <summary>
@@ -262,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>(input => input.ReadSInt32(), (output, value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag, defaultValue);
+            return new FieldCodec<int>((ref ParseContext ctx) => ctx.ReadSInt32(), (output, value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag, defaultValue);
         }
 
         /// <summary>
@@ -273,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>(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), 4, tag, defaultValue);
+            return new FieldCodec<uint>((ref ParseContext ctx) => ctx.ReadFixed32(), (output, value) => output.WriteFixed32(value), 4, tag, defaultValue);
         }
 
         /// <summary>
@@ -284,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>(input => input.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), 4, tag, defaultValue);
+            return new FieldCodec<int>((ref ParseContext ctx) => ctx.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), 4, tag, defaultValue);
         }
 
         /// <summary>
@@ -295,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>(input => input.ReadUInt32(), (output, value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag, defaultValue);
+            return new FieldCodec<uint>((ref ParseContext ctx) => ctx.ReadUInt32(), (output, value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag, defaultValue);
         }
 
         /// <summary>
@@ -306,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>(input => input.ReadInt64(), (output, value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag, defaultValue);
+            return new FieldCodec<long>((ref ParseContext ctx) => ctx.ReadInt64(), (output, value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag, defaultValue);
         }
 
         /// <summary>
@@ -317,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>(input => input.ReadSInt64(), (output, value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag, defaultValue);
+            return new FieldCodec<long>((ref ParseContext ctx) => ctx.ReadSInt64(), (output, value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag, defaultValue);
         }
 
         /// <summary>
@@ -328,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>(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), 8, tag, defaultValue);
+            return new FieldCodec<ulong>((ref ParseContext ctx) => ctx.ReadFixed64(), (output, value) => output.WriteFixed64(value), 8, tag, defaultValue);
         }
 
         /// <summary>
@@ -339,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>(input => input.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), 8, tag, defaultValue);
+            return new FieldCodec<long>((ref ParseContext ctx) => ctx.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), 8, tag, defaultValue);
         }
 
         /// <summary>
@@ -350,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>(input => input.ReadUInt64(), (output, value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag, defaultValue);
+            return new FieldCodec<ulong>((ref ParseContext ctx) => ctx.ReadUInt64(), (output, value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag, defaultValue);
         }
 
         /// <summary>
@@ -361,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>(input => input.ReadFloat(), (output, value) => output.WriteFloat(value), CodedOutputStream.FloatSize, tag, defaultValue);
+            return new FieldCodec<float>((ref ParseContext ctx) => ctx.ReadFloat(), (output, value) => output.WriteFloat(value), CodedOutputStream.FloatSize, tag, defaultValue);
         }
 
         /// <summary>
@@ -372,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>(input => input.ReadDouble(), (output, value) => output.WriteDouble(value), CodedOutputStream.DoubleSize, tag, defaultValue);
+            return new FieldCodec<double>((ref ParseContext ctx) => ctx.ReadDouble(), (output, value) => output.WriteDouble(value), CodedOutputStream.DoubleSize, tag, defaultValue);
         }
 
         // Enums are tricky. We can probably use expression trees to build these delegates automatically,
@@ -388,8 +389,8 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32, T defaultValue)
         {
-            return new FieldCodec<T>(input => fromInt32(
-                input.ReadEnum()),
+            return new FieldCodec<T>((ref ParseContext ctx) => fromInt32(
+                ctx.ReadEnum()),
                 (output, value) => output.WriteEnum(toInt32(value)),
                 value => CodedOutputStream.ComputeEnumSize(toInt32(value)), tag, defaultValue);
         }
@@ -403,21 +404,21 @@ namespace Google.Protobuf
         public static FieldCodec<T> ForMessage<T>(uint tag, MessageParser<T> parser) where T : class, IMessage<T>
         {
             return new FieldCodec<T>(
-                input => 
+                (ref ParseContext ctx) => 
                 { 
                     T message = parser.CreateTemplate(); 
-                    input.ReadMessage(message); 
+                    ctx.ReadMessage(message); 
                     return message; 
                 },
                 (output, value) => output.WriteMessage(value),
-                (CodedInputStream i, ref T v) => 
+                (ref ParseContext ctx, ref T v) => 
                 {
                     if (v == null)
                     {
                         v = parser.CreateTemplate();
                     }
 
-                    i.ReadMessage(v);
+                    ctx.ReadMessage(v);
                 },
                 (ref T v, T v2) =>
                 {
@@ -448,21 +449,21 @@ namespace Google.Protobuf
         public static FieldCodec<T> ForGroup<T>(uint startTag, uint endTag, MessageParser<T> parser) where T : class, IMessage<T>
         {
             return new FieldCodec<T>(
-                input => 
+                (ref ParseContext ctx) => 
                 { 
                     T message = parser.CreateTemplate();
-                    input.ReadGroup(message);
+                    ctx.ReadGroup(message);
                     return message;
                 },
                 (output, value) => output.WriteGroup(value), 
-                (CodedInputStream i, ref T v) => 
+                (ref ParseContext ctx, ref T v) => 
                 {
                     if (v == null)
                     {
                         v = parser.CreateTemplate();
                     }
 
-                    i.ReadGroup(v);
+                    ctx.ReadGroup(v);
                 },
                 (ref T v, T v2) =>
                 {
@@ -490,9 +491,9 @@ namespace Google.Protobuf
         {
             var nestedCodec = WrapperCodecs.GetCodec<T>();
             return new FieldCodec<T>(
-                input => WrapperCodecs.Read<T>(input, nestedCodec),
+                (ref ParseContext ctx) => WrapperCodecs.Read<T>(ref ctx, nestedCodec),
                 (output, value) => WrapperCodecs.Write<T>(output, value, nestedCodec),
-                (CodedInputStream i, ref T v) => v = WrapperCodecs.Read<T>(i, 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),
                 tag, 0,
@@ -509,7 +510,7 @@ namespace Google.Protobuf
             return new FieldCodec<T?>(
                 WrapperCodecs.GetReader<T>(),
                 (output, value) => WrapperCodecs.Write<T>(output, value.Value, nestedCodec),
-                (CodedInputStream i, ref T? v) => v = WrapperCodecs.Read<T>(i, 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),
                 tag, 0,
@@ -542,17 +543,17 @@ namespace Google.Protobuf
             private static readonly Dictionary<System.Type, object> Readers = new Dictionary<System.Type, object>
             {
                 // TODO: Provide more optimized readers.
-                { typeof(bool), (Func<CodedInputStream, bool?>)CodedInputStream.ReadBoolWrapper },
-                { typeof(int), (Func<CodedInputStream, int?>)CodedInputStream.ReadInt32Wrapper },
-                { typeof(long), (Func<CodedInputStream, long?>)CodedInputStream.ReadInt64Wrapper },
-                { typeof(uint), (Func<CodedInputStream, uint?>)CodedInputStream.ReadUInt32Wrapper },
-                { typeof(ulong), (Func<CodedInputStream, ulong?>)CodedInputStream.ReadUInt64Wrapper },
+                { typeof(bool), (ValueReader<bool?>)ParsingPrimitivesWrappers.ReadBoolWrapper },
+                { typeof(int), (ValueReader<int?>)ParsingPrimitivesWrappers.ReadInt32Wrapper },
+                { typeof(long), (ValueReader<long?>)ParsingPrimitivesWrappers.ReadInt64Wrapper },
+                { typeof(uint), (ValueReader<uint?>)ParsingPrimitivesWrappers.ReadUInt32Wrapper },
+                { typeof(ulong), (ValueReader<ulong?>)ParsingPrimitivesWrappers.ReadUInt64Wrapper },
                 { typeof(float), BitConverter.IsLittleEndian ?
-                    (Func<CodedInputStream, float?>)CodedInputStream.ReadFloatWrapperLittleEndian :
-                    (Func<CodedInputStream, float?>)CodedInputStream.ReadFloatWrapperSlow },
+                    (ValueReader<float?>)ParsingPrimitivesWrappers.ReadFloatWrapperLittleEndian :
+                    (ValueReader<float?>)ParsingPrimitivesWrappers.ReadFloatWrapperSlow },
                 { typeof(double), BitConverter.IsLittleEndian ?
-                    (Func<CodedInputStream, double?>)CodedInputStream.ReadDoubleWrapperLittleEndian :
-                    (Func<CodedInputStream, double?>)CodedInputStream.ReadDoubleWrapperSlow },
+                    (ValueReader<double?>)ParsingPrimitivesWrappers.ReadDoubleWrapperLittleEndian :
+                    (ValueReader<double?>)ParsingPrimitivesWrappers.ReadDoubleWrapperSlow },
                 // `string` and `ByteString` less performance-sensitive. Do not implement for now.
                 { typeof(string), null },
                 { typeof(ByteString), null },
@@ -572,7 +573,7 @@ namespace Google.Protobuf
                 return (FieldCodec<T>) value;
             }
 
-            internal static Func<CodedInputStream, T?> GetReader<T>() where T : struct
+            internal static ValueReader<T?> GetReader<T>() where T : struct
             {
                 object value;
                 if (!Readers.TryGetValue(typeof(T), out value))
@@ -583,33 +584,34 @@ namespace Google.Protobuf
                 {
                     // Return default unoptimized reader for the wrapper type.
                     var nestedCoded = GetCodec<T>();
-                    return input => Read<T>(input, nestedCoded);
+                    return (ref ParseContext ctx) => Read<T>(ref ctx, nestedCoded);
                 }
                 // Return optimized read for the wrapper type.
-                return (Func<CodedInputStream, T?>)value;
+                return (ValueReader<T?>)value;
             }
 
-            internal static T Read<T>(CodedInputStream input, FieldCodec<T> codec)
+            [SecuritySafeCritical]
+            internal static T Read<T>(ref ParseContext ctx, FieldCodec<T> codec)
             {
-                int length = input.ReadLength();
-                int oldLimit = input.PushLimit(length);
+                int length = ctx.ReadLength();
+                int oldLimit = SegmentedBufferHelper.PushLimit(ref ctx.state, length);
 
                 uint tag;
                 T value = codec.DefaultValue;
-                while ((tag = input.ReadTag()) != 0)
+                while ((tag = ctx.ReadTag()) != 0)
                 {
                     if (tag == codec.Tag)
                     {
-                        value = codec.Read(input);
+                        value = codec.Read(ref ctx);
                     }
                     else
                     {
-                        input.SkipLastField();
+                        ParsingPrimitivesMessages.SkipLastField(ref ctx.buffer, ref ctx.state);
                     }
 
                 }
-                input.CheckReadEndOfStreamTag();
-                input.PopLimit(oldLimit);
+                ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref ctx.state);
+                SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit);
 
                 return value;
             }
@@ -628,6 +630,8 @@ namespace Google.Protobuf
         }
     }
 
+    internal delegate TValue ValueReader<out TValue>(ref ParseContext ctx);
+
     /// <summary>
     /// <para>
     /// An encode/decode pair for a single field. This effectively encapsulates
@@ -653,7 +657,7 @@ namespace Google.Protobuf
         /// <summary>
         /// Merges an input stream into a value
         /// </summary>
-        internal delegate void InputMerger(CodedInputStream input, ref T value);
+        internal delegate void InputMerger(ref ParseContext ctx, ref T value);
 
         /// <summary>
         /// Merges a value into a reference to another value, returning a boolean if the value was set
@@ -692,7 +696,7 @@ namespace Google.Protobuf
         /// Returns a delegate to read a value from a coded input stream. It is assumed that
         /// the stream is already positioned on the appropriate tag.
         /// </summary>
-        internal Func<CodedInputStream, T> ValueReader { get; }
+        internal ValueReader<T> ValueReader { get; }
 
         /// <summary>
         /// Returns a delegate to merge a value from a coded input stream.
@@ -739,7 +743,7 @@ namespace Google.Protobuf
         private readonly int tagSize;
 
         internal FieldCodec(
-                Func<CodedInputStream, T> reader,
+                ValueReader<T> reader,
                 Action<CodedOutputStream, T> writer,
                 int fixedSize,
                 uint tag,
@@ -749,16 +753,16 @@ namespace Google.Protobuf
         }
 
         internal FieldCodec(
-            Func<CodedInputStream, T> reader,
+            ValueReader<T> reader,
             Action<CodedOutputStream, T> writer,
             Func<T, int> sizeCalculator,
             uint tag,
-            T defaultValue) : this(reader, writer, (CodedInputStream i, ref T v) => v = reader(i), (ref T v, T v2) => { v = v2; return true; }, sizeCalculator, tag, 0, defaultValue)
+            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)
         {
         }
 
         internal FieldCodec(
-            Func<CodedInputStream, T> reader,
+            ValueReader<T> reader,
             Action<CodedOutputStream, T> writer,
             InputMerger inputMerger,
             ValuesMerger valuesMerger,
@@ -769,7 +773,7 @@ namespace Google.Protobuf
         }
 
         internal FieldCodec(
-            Func<CodedInputStream, T> reader,
+            ValueReader<T> reader,
             Action<CodedOutputStream, T> writer,
             InputMerger inputMerger,
             ValuesMerger valuesMerger,
@@ -815,7 +819,28 @@ namespace Google.Protobuf
         /// </summary>
         /// <param name="input">The input stream to read from.</param>
         /// <returns>The value read from the stream.</returns>
-        public T Read(CodedInputStream input) => ValueReader(input);
+        public T Read(CodedInputStream input)
+        {
+            ParseContext.Initialize(input, out ParseContext ctx);
+            try
+            {
+                return ValueReader(ref ctx);
+            }
+            finally
+            {
+                ctx.CopyStateTo(input);
+            }
+        }
+
+        /// <summary>
+        /// Reads a value of the codec type from the given <see cref="ParseContext"/>.
+        /// </summary>
+        /// <param name="ctx">The parse context to read from.</param>
+        /// <returns>The value read.</returns>
+        public T Read(ref ParseContext ctx)
+        {
+            return ValueReader(ref ctx);
+        }
 
         /// <summary>
         /// Calculates the size required to write the given value, with a tag,

+ 14 - 12
csharp/src/Google.Protobuf/Google.Protobuf.csproj

@@ -1,13 +1,14 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <Description>C# runtime library for Protocol Buffers - Google's data interchange format.</Description>
     <Copyright>Copyright 2015, Google Inc.</Copyright>
     <AssemblyTitle>Google Protocol Buffers</AssemblyTitle>
     <VersionPrefix>3.11.4</VersionPrefix>
-    <LangVersion>6</LangVersion>
+    <!-- C# 7.2 is required for Span/BufferWriter/ReadOnlySequence -->
+    <LangVersion>7.2</LangVersion>
     <Authors>Google Inc.</Authors>
-    <TargetFrameworks>netstandard1.0;netstandard2.0;net45</TargetFrameworks>
+    <TargetFrameworks>netstandard1.1;netstandard2.0;net45</TargetFrameworks>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
@@ -18,25 +19,26 @@
     <PackageLicenseUrl>https://github.com/protocolbuffers/protobuf/blob/master/LICENSE</PackageLicenseUrl>
     <RepositoryType>git</RepositoryType>
     <RepositoryUrl>https://github.com/protocolbuffers/protobuf.git</RepositoryUrl>
+    <DefineConstants>$(DefineConstants);GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY</DefineConstants>
+    <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
     <!-- Include PDB in the built .nupkg -->
     <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
   </PropertyGroup>
 
-  <PropertyGroup Condition=" '$(TargetFramework)' == 'net45' or '$(TargetFramework)' == 'netstandard2.0' ">
-    <DefineConstants>$(DefineConstants);GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY</DefineConstants>
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
+    <DefineConstants>$(DefineConstants);GOOGLE_PROTOBUF_SUPPORT_FAST_STRING</DefineConstants>
   </PropertyGroup>
 
-  <!-- Needed for the net45 build to work on Unix. See https://github.com/dotnet/designs/pull/33 -->
   <ItemGroup>
-    <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" Version="1.0.0-preview.2"/>
-  </ItemGroup>
-
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net45' or '$(TargetFramework)' == 'netstandard2.0' ">
     <PackageReference Include="System.Memory" Version="4.5.2"/>
+    <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="1.0.0-beta2-18618-05"/>
+    <!-- Needed for the net45 build to work on Unix. See https://github.com/dotnet/designs/pull/33 -->
+    <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" Version="1.0.0"/>
   </ItemGroup>
 
-  <ItemGroup>
-    <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="1.0.0-beta2-18618-05"/>
+  <!-- Needed for netcoreapp2.1 to work correctly. .NET is not able to load the assembly without this -->
+  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
+    <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.2"/>
   </ItemGroup>
 
 </Project>

+ 49 - 0
csharp/src/Google.Protobuf/IBufferMessage.cs

@@ -0,0 +1,49 @@
+#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
+
+namespace Google.Protobuf
+{
+#if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY
+    /// <summary>
+    /// Interface for a Protocol Buffers message, supporting
+    /// parsing from <see cref="ParseContext"/>.
+    /// </summary>
+    public interface IBufferMessage : IMessage
+    {
+        /// <summary>
+        /// Internal implementation of merging data from given parse context into this message.
+        /// Users should never invoke this method directly.
+        /// </summary>        
+        void InternalMergeFrom(ref ParseContext ctx);
+    }
+#endif
+}

+ 12 - 0
csharp/src/Google.Protobuf/MessageExtensions.cs

@@ -31,9 +31,11 @@
 #endregion
 
 using Google.Protobuf.Reflection;
+using System.Buffers;
 using System.Collections;
 using System.IO;
 using System.Linq;
+using System.Security;
 
 namespace Google.Protobuf
 {
@@ -248,6 +250,16 @@ namespace Google.Protobuf
             codedInput.CheckReadEndOfStreamTag();
         }
 
+        [SecuritySafeCritical]
+        internal static void MergeFrom(this IMessage message, ReadOnlySequence<byte> data, bool discardUnknownFields, ExtensionRegistry registry)
+        {
+            ParseContext.Initialize(data, out ParseContext ctx);
+            ctx.DiscardUnknownFields = discardUnknownFields;
+            ctx.ExtensionRegistry = registry;
+            ParsingPrimitivesMessages.ReadRawMessage(ref ctx, message);
+            ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref ctx.state);
+        }
+
         internal static void MergeDelimitedFrom(this IMessage message, Stream input, bool discardUnknownFields, ExtensionRegistry registry)
         {
             ProtoPreconditions.CheckNotNull(message, "message");

+ 28 - 0
csharp/src/Google.Protobuf/MessageParser.cs

@@ -31,7 +31,9 @@
 #endregion
 
 using System;
+using System.Buffers;
 using System.IO;
+using System.Security;
 
 namespace Google.Protobuf
 {
@@ -113,6 +115,19 @@ namespace Google.Protobuf
             return message;
         }
 
+        /// <summary>
+        /// Parses a message from the given sequence.
+        /// </summary>
+        /// <param name="data">The data to parse.</param>
+        /// <returns>The parsed message.</returns>
+        [SecuritySafeCritical]
+        public IMessage ParseFrom(ReadOnlySequence<byte> data)
+        {
+            IMessage message = factory();
+            message.MergeFrom(data, DiscardUnknownFields, Extensions);
+            return message;
+        }
+
         /// <summary>
         /// Parses a length-delimited message from the given stream.
         /// </summary>
@@ -287,6 +302,19 @@ namespace Google.Protobuf
             return message;
         }
 
+        /// <summary>
+        /// Parses a message from the given sequence.
+        /// </summary>
+        /// <param name="data">The data to parse.</param>
+        /// <returns>The parsed message.</returns>
+        [SecuritySafeCritical]
+        public new T ParseFrom(ReadOnlySequence<byte> data)
+        {
+            T message = factory();
+            message.MergeFrom(data, DiscardUnknownFields, Extensions);
+            return message;
+        }
+
         /// <summary>
         /// Parses a length-delimited message from the given stream.
         /// </summary>

+ 329 - 0
csharp/src/Google.Protobuf/ParseContext.cs

@@ -0,0 +1,329 @@
+#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 parsing state and is passed along
+    /// as the parsing 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 ParseContext
+    {
+        internal const int DefaultRecursionLimit = 100;
+        internal const int DefaultSizeLimit = Int32.MaxValue;
+
+        internal ReadOnlySpan<byte> buffer;
+        internal ParserInternalState state;
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static void Initialize(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, out ParseContext ctx)
+        {
+            ctx.buffer = buffer;
+            ctx.state = state;
+        }
+
+        /// <summary>
+        /// Creates a ParseContext instance from CodedInputStream.
+        /// WARNING: internally this copies the CodedInputStream's state, so after done with the ParseContext,
+        /// the CodedInputStream's state needs to be updated.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static void Initialize(CodedInputStream input, out ParseContext ctx)
+        {
+            ctx.buffer = new ReadOnlySpan<byte>(input.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 CodedInputStream if we want to keep it usable.
+            ctx.state = input.InternalState;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static void Initialize(ReadOnlySequence<byte> input, out ParseContext ctx)
+        {
+            Initialize(input, DefaultRecursionLimit, out ctx);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static void Initialize(ReadOnlySequence<byte> input, int recursionLimit, out ParseContext ctx)
+        {
+            ctx.buffer = default;
+            ctx.state = default;
+            ctx.state.lastTag = 0;
+            ctx.state.recursionDepth = 0;
+            ctx.state.sizeLimit = DefaultSizeLimit;
+            ctx.state.recursionLimit = recursionLimit;
+            ctx.state.currentLimit = int.MaxValue;
+            SegmentedBufferHelper.Initialize(input, out ctx.state.segmentedBufferHelper, out ctx.buffer);
+            ctx.state.bufferPos = 0;
+            ctx.state.bufferSize = ctx.buffer.Length;
+
+            ctx.state.DiscardUnknownFields = false;
+            ctx.state.ExtensionRegistry = null;
+        }
+
+        /// <summary>
+        /// Returns the last tag read, or 0 if no tags have been read or we've read beyond
+        /// the end of the input.
+        /// </summary>
+        internal uint LastTag { get { return state.lastTag; } }
+
+        /// <summary>
+        /// Internal-only property; when set to true, unknown fields will be discarded while parsing.
+        /// </summary>
+        internal bool DiscardUnknownFields {
+            get { return state.DiscardUnknownFields; }
+            set { state.DiscardUnknownFields = value; }
+        }
+
+        /// <summary>
+        /// Internal-only property; provides extension identifiers to compatible messages while parsing.
+        /// </summary>
+        internal ExtensionRegistry ExtensionRegistry
+        {
+            get { return state.ExtensionRegistry; }
+            set { state.ExtensionRegistry = value; }
+        }
+
+        /// <summary>
+        /// Reads a field tag, returning the tag of 0 for "end of input".
+        /// </summary>
+        /// <remarks>
+        /// If this method returns 0, it doesn't necessarily mean the end of all
+        /// the data in this CodedInputReader; it may be the end of the logical input
+        /// for an embedded message, for example.
+        /// </remarks>
+        /// <returns>The next field tag, or 0 for end of input. (0 is never a valid tag.)</returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public uint ReadTag()
+        {
+            return ParsingPrimitives.ParseTag(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads a double field from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public double ReadDouble()
+        {
+            return ParsingPrimitives.ParseDouble(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads a float field from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public float ReadFloat()
+        {
+            return ParsingPrimitives.ParseFloat(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads a uint64 field from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public ulong ReadUInt64()
+        {
+            return ParsingPrimitives.ParseRawVarint64(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads an int64 field from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public long ReadInt64()
+        {
+            return (long)ParsingPrimitives.ParseRawVarint64(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads an int32 field from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public int ReadInt32()
+        {
+            return (int)ParsingPrimitives.ParseRawVarint32(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads a fixed64 field from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public ulong ReadFixed64()
+        {
+            return ParsingPrimitives.ParseRawLittleEndian64(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads a fixed32 field from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public uint ReadFixed32()
+        {
+            return ParsingPrimitives.ParseRawLittleEndian32(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads a bool field from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool ReadBool()
+        {
+            return ParsingPrimitives.ParseRawVarint64(ref buffer, ref state) != 0;
+        }
+        /// <summary>
+        /// Reads a string field from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public string ReadString()
+        {
+            return ParsingPrimitives.ReadString(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads an embedded message field value from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void ReadMessage(IMessage message)
+        {
+            ParsingPrimitivesMessages.ReadMessage(ref this, message);
+        }
+
+        /// <summary>
+        /// Reads an embedded group field from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void ReadGroup(IMessage message)
+        {
+            ParsingPrimitivesMessages.ReadGroup(ref this, message);
+        }
+
+        /// <summary>
+        /// Reads a bytes field value from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public ByteString ReadBytes()
+        {
+            return ParsingPrimitives.ReadBytes(ref buffer, ref state);
+        }
+        /// <summary>
+        /// Reads a uint32 field value from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public uint ReadUInt32()
+        {
+            return ParsingPrimitives.ParseRawVarint32(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads an enum field value from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public int ReadEnum()
+        {
+            // Currently just a pass-through, but it's nice to separate it logically from WriteInt32.
+            return (int)ParsingPrimitives.ParseRawVarint32(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads an sfixed32 field value from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public int ReadSFixed32()
+        {
+            return (int)ParsingPrimitives.ParseRawLittleEndian32(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads an sfixed64 field value from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public long ReadSFixed64()
+        {
+            return (long)ParsingPrimitives.ParseRawLittleEndian64(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Reads an sint32 field value from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public int ReadSInt32()
+        {
+            return ParsingPrimitives.DecodeZigZag32(ParsingPrimitives.ParseRawVarint32(ref buffer, ref state));
+        }
+
+        /// <summary>
+        /// Reads an sint64 field value from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public long ReadSInt64()
+        {
+            return ParsingPrimitives.DecodeZigZag64(ParsingPrimitives.ParseRawVarint64(ref buffer, ref state));
+        }
+
+        /// <summary>
+        /// Reads a length for length-delimited data.
+        /// </summary>
+        /// <remarks>
+        /// This is internally just reading a varint, but this method exists
+        /// to make the calling code clearer.
+        /// </remarks>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public int ReadLength()
+        {
+            return (int)ParsingPrimitives.ParseRawVarint32(ref buffer, ref state);
+        }
+
+        internal void CopyStateTo(CodedInputStream input)
+        {
+            input.InternalState = state;
+        }
+
+        internal void LoadStateFrom(CodedInputStream input)
+        {
+            state = input.InternalState;
+        }
+    }
+}

+ 115 - 0
csharp/src/Google.Protobuf/ParserInternalState.cs

@@ -0,0 +1,115 @@
+#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 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
+        // be included in CodedInputStream's internal state
+
+        /// <summary>
+        /// The position within the current buffer (i.e. the next byte to read)
+        /// </summary>
+        internal int bufferPos;
+        
+        /// <summary>
+        /// Size of the current buffer
+        /// </summary>
+        internal int bufferSize;
+        
+        /// <summary>
+        /// If we are currently inside a length-delimited block, this is the number of
+        /// bytes in the buffer that are still available once we leave the delimited block.
+        /// </summary>
+        internal int bufferSizeAfterLimit;
+
+        /// <summary>
+        /// The absolute position of the end of the current length-delimited block (including totalBytesRetired)
+        /// </summary>
+        internal int currentLimit;
+
+        /// <summary>
+        /// The total number of consumed before the start of the current buffer. The
+        /// total bytes read up to the current position can be computed as
+        /// totalBytesRetired + bufferPos.
+        /// </summary>
+        internal int totalBytesRetired;
+
+        internal int recursionDepth;  // current recursion depth
+        
+        internal SegmentedBufferHelper segmentedBufferHelper;
+        
+        /// <summary>
+        /// The last tag we read. 0 indicates we've read to the end of the stream
+        /// (or haven't read anything yet).
+        /// </summary>
+        internal uint lastTag;
+
+        /// <summary>
+        /// The next tag, used to store the value read by PeekTag.
+        /// </summary>
+        internal uint nextTag;
+        internal bool hasNextTag;
+
+        // these fields are configuration, they should be readonly
+        internal int sizeLimit;
+        internal int recursionLimit;
+
+        // If non-null, the top level parse method was started with given coded input stream as an argument
+        // which also means we can potentially fallback to calling MergeFrom(CodedInputStream cis) if needed.
+        internal CodedInputStream CodedInputStream => segmentedBufferHelper.CodedInputStream;
+        
+        /// <summary>
+        /// Internal-only property; when set to true, unknown fields will be discarded while parsing.
+        /// </summary>
+        internal bool DiscardUnknownFields { get; set; }
+
+        /// <summary>
+        /// Internal-only property; provides extension identifiers to compatible messages while parsing.
+        /// </summary>
+        internal ExtensionRegistry ExtensionRegistry { get; set; }
+    }
+}

+ 735 - 0
csharp/src/Google.Protobuf/ParsingPrimitives.cs

@@ -0,0 +1,735 @@
+#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>
+    /// Primitives for parsing protobuf wire format.
+    /// </summary>
+    [SecuritySafeCritical]
+    internal static class ParsingPrimitives
+    {
+
+        /// <summary>
+        /// Reads a length for length-delimited data.
+        /// </summary>
+        /// <remarks>
+        /// This is internally just reading a varint, but this method exists
+        /// to make the calling code clearer.
+        /// </remarks>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+
+        public static int ParseLength(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            return (int)ParseRawVarint32(ref buffer, ref state);
+        }
+
+        /// <summary>
+        /// Parses the next tag.
+        /// If the end of logical stream was reached, an invalid tag of 0 is returned. 
+        /// </summary>
+        public static uint ParseTag(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            // The "nextTag" logic is there only as an optimization for reading non-packed repeated / map
+            // fields and is strictly speaking not necessary.
+            // TODO(jtattermusch): look into simplifying the ParseTag logic.
+            if (state.hasNextTag)
+            {
+                state.lastTag = state.nextTag;
+                state.hasNextTag = false;
+                return state.lastTag;
+            }
+
+            // Optimize for the incredibly common case of having at least two bytes left in the buffer,
+            // and those two bytes being enough to get the tag. This will be true for fields up to 4095.
+            if (state.bufferPos + 2 <= state.bufferSize)
+            {
+                int tmp = buffer[state.bufferPos++];
+                if (tmp < 128)
+                {
+                    state.lastTag = (uint)tmp;
+                }
+                else
+                {
+                    int result = tmp & 0x7f;
+                    if ((tmp = buffer[state.bufferPos++]) < 128)
+                    {
+                        result |= tmp << 7;
+                        state.lastTag = (uint) result;
+                    }
+                    else
+                    {
+                        // Nope, rewind and go the potentially slow route.
+                        state.bufferPos -= 2;
+                        state.lastTag = ParsingPrimitives.ParseRawVarint32(ref buffer, ref state);
+                    }
+                }
+            }
+            else
+            {
+                if (SegmentedBufferHelper.IsAtEnd(ref buffer, ref state))
+                {
+                    state.lastTag = 0;
+                    return 0;
+                }
+
+                state.lastTag = ParsingPrimitives.ParseRawVarint32(ref buffer, ref state);
+            }
+            if (WireFormat.GetTagFieldNumber(state.lastTag) == 0)
+            {
+                // If we actually read a tag with a field of 0, that's not a valid tag.
+                throw InvalidProtocolBufferException.InvalidTag();
+            }
+            return state.lastTag;
+        }
+
+        /// <summary>
+        /// Peeks at the next tag in the stream. If it matches <paramref name="tag"/>,
+        /// the tag is consumed and the method returns <c>true</c>; otherwise, the
+        /// stream is left in the original position and the method returns <c>false</c>.
+        /// </summary>
+        public static bool MaybeConsumeTag(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, uint tag)
+        {
+            if (PeekTag(ref buffer, ref state) == tag)
+            {
+                state.hasNextTag = false;
+                return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Peeks at the next field tag. This is like calling <see cref="ParseTag"/>, but the
+        /// tag is not consumed. (So a subsequent call to <see cref="ParseTag"/> will return the
+        /// same value.)
+        /// </summary>
+        public static uint PeekTag(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            if (state.hasNextTag)
+            {
+                return state.nextTag;
+            }
+
+            uint savedLast = state.lastTag;
+            state.nextTag = ParseTag(ref buffer, ref state);
+            state.hasNextTag = true;
+            state.lastTag = savedLast; // Undo the side effect of ReadTag
+            return state.nextTag;
+        }
+
+        /// <summary>
+        /// Parses a raw varint.
+        /// </summary>
+        public static ulong ParseRawVarint64(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            if (state.bufferPos + 10 > state.bufferSize)
+            {
+                return ParseRawVarint64SlowPath(ref buffer, ref state);
+            }
+
+            ulong result = buffer[state.bufferPos++];
+            if (result < 128)
+            {
+                return result;
+            }
+            result &= 0x7f;
+            int shift = 7;
+            do
+            {
+                byte b = buffer[state.bufferPos++];
+                result |= (ulong)(b & 0x7F) << shift;
+                if (b < 0x80)
+                {
+                    return result;
+                }
+                shift += 7;
+            }
+            while (shift < 64);
+
+            throw InvalidProtocolBufferException.MalformedVarint();
+        }
+
+        private static ulong ParseRawVarint64SlowPath(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            int shift = 0;
+            ulong result = 0;
+            do
+            {
+                byte b = ReadRawByte(ref buffer, ref state);
+                result |= (ulong)(b & 0x7F) << shift;
+                if (b < 0x80)
+                {
+                    return result;
+                }
+                shift += 7;
+            }
+            while (shift < 64);
+
+            throw InvalidProtocolBufferException.MalformedVarint();
+        }
+
+        /// <summary>
+        /// Parses a raw Varint.  If larger than 32 bits, discard the upper bits.
+        /// This method is optimised for the case where we've got lots of data in the buffer.
+        /// That means we can check the size just once, then just read directly from the buffer
+        /// without constant rechecking of the buffer length.
+        /// </summary>
+        public static uint ParseRawVarint32(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            if (state.bufferPos + 5 > state.bufferSize)
+            {
+                return ParseRawVarint32SlowPath(ref buffer, ref state);
+            }
+
+            int tmp = buffer[state.bufferPos++];
+            if (tmp < 128)
+            {
+                return (uint)tmp;
+            }
+            int result = tmp & 0x7f;
+            if ((tmp = buffer[state.bufferPos++]) < 128)
+            {
+                result |= tmp << 7;
+            }
+            else
+            {
+                result |= (tmp & 0x7f) << 7;
+                if ((tmp = buffer[state.bufferPos++]) < 128)
+                {
+                    result |= tmp << 14;
+                }
+                else
+                {
+                    result |= (tmp & 0x7f) << 14;
+                    if ((tmp = buffer[state.bufferPos++]) < 128)
+                    {
+                        result |= tmp << 21;
+                    }
+                    else
+                    {
+                        result |= (tmp & 0x7f) << 21;
+                        result |= (tmp = buffer[state.bufferPos++]) << 28;
+                        if (tmp >= 128)
+                        {
+                            // Discard upper 32 bits.
+                            // Note that this has to use ReadRawByte() as we only ensure we've
+                            // got at least 5 bytes at the start of the method. This lets us
+                            // use the fast path in more cases, and we rarely hit this section of code.
+                            for (int i = 0; i < 5; i++)
+                            {
+                                if (ReadRawByte(ref buffer, ref state) < 128)
+                                {
+                                    return (uint) result;
+                                }
+                            }
+                            throw InvalidProtocolBufferException.MalformedVarint();
+                        }
+                    }
+                }
+            }
+            return (uint)result;
+        }
+
+        private static uint ParseRawVarint32SlowPath(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            int tmp = ReadRawByte(ref buffer, ref state);
+            if (tmp < 128)
+            {
+                return (uint) tmp;
+            }
+            int result = tmp & 0x7f;
+            if ((tmp = ReadRawByte(ref buffer, ref state)) < 128)
+            {
+                result |= tmp << 7;
+            }
+            else
+            {
+                result |= (tmp & 0x7f) << 7;
+                if ((tmp = ReadRawByte(ref buffer, ref state)) < 128)
+                {
+                    result |= tmp << 14;
+                }
+                else
+                {
+                    result |= (tmp & 0x7f) << 14;
+                    if ((tmp = ReadRawByte(ref buffer, ref state)) < 128)
+                    {
+                        result |= tmp << 21;
+                    }
+                    else
+                    {
+                        result |= (tmp & 0x7f) << 21;
+                        result |= (tmp = ReadRawByte(ref buffer, ref state)) << 28;
+                        if (tmp >= 128)
+                        {
+                            // Discard upper 32 bits.
+                            for (int i = 0; i < 5; i++)
+                            {
+                                if (ReadRawByte(ref buffer, ref state) < 128)
+                                {
+                                    return (uint) result;
+                                }
+                            }
+                            throw InvalidProtocolBufferException.MalformedVarint();
+                        }
+                    }
+                }
+            }
+            return (uint) result;
+        }
+
+        /// <summary>
+        /// Parses a 32-bit little-endian integer.
+        /// </summary>
+        public static uint ParseRawLittleEndian32(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            const int uintLength = sizeof(uint);
+            const int ulongLength = sizeof(ulong);
+            if (state.bufferPos + ulongLength > state.bufferSize)
+            {
+                return ParseRawLittleEndian32SlowPath(ref buffer, ref state);
+            }
+            // ReadUInt32LittleEndian is many times slower than ReadUInt64LittleEndian (at least on some runtimes)
+            // so it's faster better to use ReadUInt64LittleEndian and truncate the result.
+            uint result = (uint) BinaryPrimitives.ReadUInt64LittleEndian(buffer.Slice(state.bufferPos, ulongLength));
+            state.bufferPos += uintLength;
+            return result;
+        }
+
+        private static uint ParseRawLittleEndian32SlowPath(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            uint b1 = ReadRawByte(ref buffer, ref state);
+            uint b2 = ReadRawByte(ref buffer, ref state);
+            uint b3 = ReadRawByte(ref buffer, ref state);
+            uint b4 = ReadRawByte(ref buffer, ref state);
+            return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
+        }
+
+        /// <summary>
+        /// Parses a 64-bit little-endian integer.
+        /// </summary>
+        public static ulong ParseRawLittleEndian64(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            const int length = sizeof(ulong);
+            if (state.bufferPos + length > state.bufferSize)
+            {
+                return ParseRawLittleEndian64SlowPath(ref buffer, ref state);
+            }
+            ulong result = BinaryPrimitives.ReadUInt64LittleEndian(buffer.Slice(state.bufferPos, length));
+            state.bufferPos += length;
+            return result;
+        }
+
+        private static ulong ParseRawLittleEndian64SlowPath(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            ulong b1 = ReadRawByte(ref buffer, ref state);
+            ulong b2 = ReadRawByte(ref buffer, ref state);
+            ulong b3 = ReadRawByte(ref buffer, ref state);
+            ulong b4 = ReadRawByte(ref buffer, ref state);
+            ulong b5 = ReadRawByte(ref buffer, ref state);
+            ulong b6 = ReadRawByte(ref buffer, ref state);
+            ulong b7 = ReadRawByte(ref buffer, ref state);
+            ulong b8 = ReadRawByte(ref buffer, ref state);
+            return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
+                    | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
+        }
+
+        /// <summary>
+        /// Parses a double value.
+        /// </summary>
+        public static double ParseDouble(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            const int length = sizeof(double);
+            if (!BitConverter.IsLittleEndian || state.bufferPos + length > state.bufferSize)
+            {
+                return BitConverter.Int64BitsToDouble((long)ParseRawLittleEndian64(ref buffer, ref state));
+            }
+            // ReadUnaligned uses processor architecture for endianness.
+            double result = Unsafe.ReadUnaligned<double>(ref MemoryMarshal.GetReference(buffer.Slice(state.bufferPos, length)));
+            state.bufferPos += length;
+            return result;
+        }
+
+        /// <summary>
+        /// Parses a float value.
+        /// </summary>
+        public static float ParseFloat(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            const int length = sizeof(float);
+            if (!BitConverter.IsLittleEndian || state.bufferPos + length > state.bufferSize)
+            {
+                return ParseFloatSlow(ref buffer, ref state);
+            }
+            // ReadUnaligned uses processor architecture for endianness.
+            float result = Unsafe.ReadUnaligned<float>(ref MemoryMarshal.GetReference(buffer.Slice(state.bufferPos, length)));
+            state.bufferPos += length;
+            return result;  
+        }
+
+        private static unsafe float ParseFloatSlow(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            const int length = sizeof(float);
+            byte* stackBuffer = stackalloc byte[length];
+            Span<byte> tempSpan = new Span<byte>(stackBuffer, length);
+            for (int i = 0; i < length; i++)
+            {
+                tempSpan[i] = ReadRawByte(ref buffer, ref state);
+            }
+
+            // Content is little endian. Reverse if needed to match endianness of architecture.
+            if (!BitConverter.IsLittleEndian)
+            {
+                tempSpan.Reverse();
+            }
+            return Unsafe.ReadUnaligned<float>(ref MemoryMarshal.GetReference(tempSpan));
+        }
+
+        /// <summary>
+        /// Reads a fixed size of bytes from the input.
+        /// </summary>
+        /// <exception cref="InvalidProtocolBufferException">
+        /// the end of the stream or the current limit was reached
+        /// </exception>
+        public static byte[] ReadRawBytes(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int size)
+        {
+            if (size < 0)
+            {
+                throw InvalidProtocolBufferException.NegativeSize();
+            }
+
+            if (state.totalBytesRetired + state.bufferPos + size > state.currentLimit)
+            {
+                // Read to the end of the stream (up to the current limit) anyway.
+                SkipRawBytes(ref buffer, ref state, state.currentLimit - state.totalBytesRetired - state.bufferPos);
+                // Then fail.
+                throw InvalidProtocolBufferException.TruncatedMessage();
+            }
+
+            if (size <= state.bufferSize - state.bufferPos)
+            {
+                // We have all the bytes we need already.
+                byte[] bytes = new byte[size];
+                buffer.Slice(state.bufferPos, size).CopyTo(bytes);
+                state.bufferPos += size;
+                return bytes;
+            }
+            else if (size < buffer.Length || size < state.segmentedBufferHelper.TotalLength)
+            {
+                // Reading more bytes than are in the buffer, but not an excessive number
+                // of bytes.  We can safely allocate the resulting array ahead of time.
+
+                // First copy what we have.
+                byte[] bytes = new byte[size];
+                var bytesSpan = new Span<byte>(bytes);
+                int pos = state.bufferSize - state.bufferPos;
+                buffer.Slice(state.bufferPos, pos).CopyTo(bytesSpan.Slice(0, pos));
+                state.bufferPos = state.bufferSize;
+
+                // We want to use RefillBuffer() and then copy from the buffer into our
+                // byte array rather than reading directly into our byte array because
+                // the input may be unbuffered.
+                state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, true);
+
+                while (size - pos > state.bufferSize)
+                {
+                    buffer.Slice(0, state.bufferSize)
+                        .CopyTo(bytesSpan.Slice(pos, state.bufferSize));
+                    pos += state.bufferSize;
+                    state.bufferPos = state.bufferSize;
+                    state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, true);
+                }
+
+                buffer.Slice(0, size - pos)
+                        .CopyTo(bytesSpan.Slice(pos, size - pos));
+                state.bufferPos = size - pos;
+
+                return bytes;
+            }
+            else
+            {
+                // The size is very large.  For security reasons, we can't allocate the
+                // entire byte array yet.  The size comes directly from the input, so a
+                // maliciously-crafted message could provide a bogus very large size in
+                // order to trick the app into allocating a lot of memory.  We avoid this
+                // by allocating and reading only a small chunk at a time, so that the
+                // malicious message must actually *be* extremely large to cause
+                // problems.  Meanwhile, we limit the allowed size of a message elsewhere.
+
+                List<byte[]> chunks = new List<byte[]>();
+
+                int pos = state.bufferSize - state.bufferPos;
+                byte[] firstChunk = new byte[pos];
+                buffer.Slice(state.bufferPos, pos).CopyTo(firstChunk);
+                chunks.Add(firstChunk);
+                state.bufferPos = state.bufferSize;
+
+                // Read all the rest of the bytes we need.
+                int sizeLeft = size - pos;
+                while (sizeLeft > 0)
+                {
+                    state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, true);
+                    byte[] chunk = new byte[Math.Min(sizeLeft, state.bufferSize)];
+
+                    buffer.Slice(0, chunk.Length)
+                        .CopyTo(chunk);
+                    state.bufferPos += chunk.Length;
+                    sizeLeft -= chunk.Length;
+                    chunks.Add(chunk);
+                }
+
+                // OK, got everything.  Now concatenate it all into one buffer.
+                byte[] bytes = new byte[size];          
+                int newPos = 0;
+                foreach (byte[] chunk in chunks)
+                {
+                    Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length);
+                    newPos += chunk.Length;
+                }
+
+                // Done.
+                return bytes;
+            }
+        }
+
+        /// <summary>
+        /// Reads and discards <paramref name="size"/> bytes.
+        /// </summary>
+        /// <exception cref="InvalidProtocolBufferException">the end of the stream
+        /// or the current limit was reached</exception>
+        public static void SkipRawBytes(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int size)
+        {
+            if (size < 0)
+            {
+                throw InvalidProtocolBufferException.NegativeSize();
+            }
+
+            if (state.totalBytesRetired + state.bufferPos + size > state.currentLimit)
+            {
+                // Read to the end of the stream anyway.
+                SkipRawBytes(ref buffer, ref state, state.currentLimit - state.totalBytesRetired - state.bufferPos);
+                // Then fail.
+                throw InvalidProtocolBufferException.TruncatedMessage();
+            }
+
+            if (size <= state.bufferSize - state.bufferPos)
+            {
+                // We have all the bytes we need already.
+                state.bufferPos += size;
+            }
+            else
+            {
+                // Skipping more bytes than are in the buffer.  First skip what we have.
+                int pos = state.bufferSize - state.bufferPos;
+                state.bufferPos = state.bufferSize;
+
+                // TODO: If our segmented buffer is backed by a Stream that is seekable, we could skip the bytes more efficiently
+                // by simply updating stream's Position property. This used to be supported in the past, but the support was dropped
+                // because it would make the segmentedBufferHelper more complex. Support can be reintroduced if needed.
+                state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, true);
+
+                while (size - pos > state.bufferSize)
+                {
+                    pos += state.bufferSize;
+                    state.bufferPos = state.bufferSize;
+                    state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, true);
+                }
+
+                state.bufferPos = size - pos;
+            }
+        }
+
+        /// <summary>
+        /// Reads a string field value from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static string ReadString(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            int length = ParsingPrimitives.ParseLength(ref buffer, ref state);
+            return ParsingPrimitives.ReadRawString(ref buffer, ref state, length);
+        }
+
+        /// <summary>
+        /// Reads a bytes field value from the input.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static ByteString ReadBytes(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            int length = ParsingPrimitives.ParseLength(ref buffer, ref state);
+            return ByteString.AttachBytes(ParsingPrimitives.ReadRawBytes(ref buffer, ref state, length));
+        }
+
+        /// <summary>
+        /// Reads a UTF-8 string from the next "length" bytes.
+        /// </summary>
+        /// <exception cref="InvalidProtocolBufferException">
+        /// the end of the stream or the current limit was reached
+        /// </exception>
+        [SecuritySafeCritical]
+        public static string ReadRawString(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, int length)
+        {
+            // No need to read any data for an empty string.
+            if (length == 0)
+            {
+                return string.Empty;
+            }
+
+            if (length < 0)
+            {
+                throw InvalidProtocolBufferException.NegativeSize();
+            }
+
+#if GOOGLE_PROTOBUF_SUPPORT_FAST_STRING
+            if (length <= state.bufferSize - state.bufferPos && length > 0)
+            {
+                // Fast path: all bytes to decode appear in the same span.
+                ReadOnlySpan<byte> data = buffer.Slice(state.bufferPos, length);
+
+                string value;
+                unsafe
+                {
+                    fixed (byte* sourceBytes = &MemoryMarshal.GetReference(data))
+                    {
+                        value = CodedOutputStream.Utf8Encoding.GetString(sourceBytes, length);
+                    }
+                }
+
+                state.bufferPos += length;
+                return value;
+            }
+#endif
+
+            var decoder = CodedOutputStream.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)
+            // whenever the buffer is backed by a byte array (and avoid creating a new byte array), but the problem is
+            // there is no way to get the underlying byte array from a span.
+
+            // TODO: in case the string spans multiple buffer segments, creating a char[] and decoding into it and then
+            // 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);
+        }
+
+        [SecuritySafeCritical]
+        private static byte ReadRawByte(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            if (state.bufferPos == state.bufferSize)
+            {
+                state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, true);
+            }
+            return buffer[state.bufferPos++];
+        }
+
+        /// <summary>
+        /// Reads a varint from the input one byte at a time, so that it does not
+        /// read any bytes after the end of the varint. If you simply wrapped the
+        /// stream in a CodedInputStream and used ReadRawVarint32(Stream)
+        /// then you would probably end up reading past the end of the varint since
+        /// CodedInputStream buffers its input.
+        /// </summary>
+        /// <param name="input"></param>
+        /// <returns></returns>
+        public static uint ReadRawVarint32(Stream input)
+        {
+            int result = 0;
+            int offset = 0;
+            for (; offset < 32; offset += 7)
+            {
+                int b = input.ReadByte();
+                if (b == -1)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+                result |= (b & 0x7f) << offset;
+                if ((b & 0x80) == 0)
+                {
+                    return (uint) result;
+                }
+            }
+            // Keep reading up to 64 bits.
+            for (; offset < 64; offset += 7)
+            {
+                int b = input.ReadByte();
+                if (b == -1)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+                if ((b & 0x80) == 0)
+                {
+                    return (uint) result;
+                }
+            }
+            throw InvalidProtocolBufferException.MalformedVarint();
+        }
+
+        /// <summary>
+        /// Decode 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 32 bits to be varint encoded, thus always taking
+        /// 5 bytes on the wire.)
+        /// </remarks>
+        public static int DecodeZigZag32(uint n)
+        {
+            return (int)(n >> 1) ^ -(int)(n & 1);
+        }
+
+        /// <summary>
+        /// Decode 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 long DecodeZigZag64(ulong n)
+        {
+            return (long)(n >> 1) ^ -(long)(n & 1);
+        }
+    }
+}

+ 229 - 0
csharp/src/Google.Protobuf/ParsingPrimitivesMessages.cs

@@ -0,0 +1,229 @@
+#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>
+    /// Reading and skipping messages / groups
+    /// </summary>
+    [SecuritySafeCritical]
+    internal static class ParsingPrimitivesMessages
+    {
+        public static void SkipLastField(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            if (state.lastTag == 0)
+            {
+                throw new InvalidOperationException("SkipLastField cannot be called at the end of a stream");
+            }
+            switch (WireFormat.GetTagWireType(state.lastTag))
+            {
+                case WireFormat.WireType.StartGroup:
+                    SkipGroup(ref buffer, ref state, state.lastTag);
+                    break;
+                case WireFormat.WireType.EndGroup:
+                    throw new InvalidProtocolBufferException(
+                        "SkipLastField called on an end-group tag, indicating that the corresponding start-group was missing");
+                case WireFormat.WireType.Fixed32:
+                    ParsingPrimitives.ParseRawLittleEndian32(ref buffer, ref state);
+                    break;
+                case WireFormat.WireType.Fixed64:
+                    ParsingPrimitives.ParseRawLittleEndian64(ref buffer, ref state);
+                    break;
+                case WireFormat.WireType.LengthDelimited:
+                    var length = ParsingPrimitives.ParseLength(ref buffer, ref state);
+                    ParsingPrimitives.SkipRawBytes(ref buffer, ref state, length);
+                    break;
+                case WireFormat.WireType.Varint:
+                    ParsingPrimitives.ParseRawVarint32(ref buffer, ref state);
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Skip a group.
+        /// </summary>
+        public static void SkipGroup(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, uint startGroupTag)
+        {
+            // Note: Currently we expect this to be the way that groups are read. We could put the recursion
+            // depth changes into the ReadTag method instead, potentially...
+            state.recursionDepth++;
+            if (state.recursionDepth >= state.recursionLimit)
+            {
+                throw InvalidProtocolBufferException.RecursionLimitExceeded();
+            }
+            uint tag;
+            while (true)
+            {
+                tag = ParsingPrimitives.ParseTag(ref buffer, ref state);
+                if (tag == 0)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+                // Can't call SkipLastField for this case- that would throw.
+                if (WireFormat.GetTagWireType(tag) == WireFormat.WireType.EndGroup)
+                {
+                    break;
+                }
+                // This recursion will allow us to handle nested groups.
+                SkipLastField(ref buffer, ref state);
+            }
+            int startField = WireFormat.GetTagFieldNumber(startGroupTag);
+            int endField = WireFormat.GetTagFieldNumber(tag);
+            if (startField != endField)
+            {
+                throw new InvalidProtocolBufferException(
+                    $"Mismatched end-group tag. Started with field {startField}; ended with field {endField}");
+            }
+            state.recursionDepth--;
+        }
+
+        public static void ReadMessage(ref ParseContext ctx, IMessage message)
+        {
+            int length = ParsingPrimitives.ParseLength(ref ctx.buffer, ref ctx.state);
+            if (ctx.state.recursionDepth >= ctx.state.recursionLimit)
+            {
+                throw InvalidProtocolBufferException.RecursionLimitExceeded();
+            }
+            int oldLimit = SegmentedBufferHelper.PushLimit(ref ctx.state, length);
+            ++ctx.state.recursionDepth;
+
+            ReadRawMessage(ref ctx, message);
+
+            CheckReadEndOfStreamTag(ref ctx.state);
+            // Check that we've read exactly as much data as expected.
+            if (!SegmentedBufferHelper.IsReachedLimit(ref ctx.state))
+            {
+                throw InvalidProtocolBufferException.TruncatedMessage();
+            }
+            --ctx.state.recursionDepth;
+            SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit);
+        }
+
+        public static void ReadGroup(ref ParseContext ctx, IMessage message)
+        {
+            if (ctx.state.recursionDepth >= ctx.state.recursionLimit)
+            {
+                throw InvalidProtocolBufferException.RecursionLimitExceeded();
+            }
+            ++ctx.state.recursionDepth;
+            
+            uint tag = ctx.state.lastTag;
+            int fieldNumber = WireFormat.GetTagFieldNumber(tag);
+            ReadRawMessage(ref ctx, message);
+            CheckLastTagWas(ref ctx.state, WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
+
+            --ctx.state.recursionDepth;
+        }
+
+        public static void ReadGroup(ref ParseContext ctx, int fieldNumber, UnknownFieldSet set)
+        {
+            if (ctx.state.recursionDepth >= ctx.state.recursionLimit)
+            {
+                throw InvalidProtocolBufferException.RecursionLimitExceeded();
+            }
+            ++ctx.state.recursionDepth;
+
+            set.MergeGroupFrom(ref ctx);
+            CheckLastTagWas(ref ctx.state, WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
+
+            --ctx.state.recursionDepth;
+        }
+
+        public static void ReadRawMessage(ref ParseContext ctx, IMessage message)
+        {
+            if (message is IBufferMessage bufferMessage)
+            {
+                bufferMessage.InternalMergeFrom(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 InternalMergeFrom method that takes a ParseContext.
+                // With a slight performance overhead, we can still parse this message just fine,
+                // but we need to find the original CodedInputStream instance that initiated this
+                // parsing 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 InternalMergeFrom method we need.
+
+                if (ctx.state.CodedInputStream == null)
+                {
+                    // This can only happen when the parsing started without providing a CodedInputStream instance
+                    // (e.g. ParseContext was created directly from a ReadOnlySequence).
+                    // 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 ParseContext support (and fail otherwise).
+                    throw new InvalidProtocolBufferException($"Message {message.GetType().Name} doesn't provide the generated method that enables ParseContext-based parsing. You might need to regenerate the generated protobuf code.");
+                }
+
+                ctx.CopyStateTo(ctx.state.CodedInputStream);
+                try
+                {
+                    // fallback parse using the CodedInputStream that started current parsing tree
+                    message.MergeFrom(ctx.state.CodedInputStream);
+                }
+                finally
+                {
+                    ctx.LoadStateFrom(ctx.state.CodedInputStream);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Verifies that the last call to ReadTag() returned tag 0 - in other words,
+        /// we've reached the end of the stream when we expected to.
+        /// </summary>
+        /// <exception cref="InvalidProtocolBufferException">The 
+        /// tag read was not the one specified</exception>
+        public static void CheckReadEndOfStreamTag(ref ParserInternalState state)
+        {
+            if (state.lastTag != 0)
+            {
+                throw InvalidProtocolBufferException.MoreDataAvailable();
+            }
+        }
+
+        private static void CheckLastTagWas(ref ParserInternalState state, uint expectedTag)
+        {
+            if (state.lastTag != expectedTag) {
+               throw InvalidProtocolBufferException.InvalidEndTag();
+            }
+        }
+    }
+}

+ 353 - 0
csharp/src/Google.Protobuf/ParsingPrimitivesWrappers.cs

@@ -0,0 +1,353 @@
+#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>
+    /// Fast parsing primitives for wrapper types
+    /// </summary>
+    [SecuritySafeCritical]
+    internal static class ParsingPrimitivesWrappers
+    {
+        internal static float? ReadFloatWrapperLittleEndian(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            // length:1 + tag:1 + value:4 = 6 bytes
+            if (state.bufferPos + 6 <= state.bufferSize)
+            {
+                // The entire wrapper message is already contained in `buffer`.
+                int length = buffer[state.bufferPos];
+                if (length == 0)
+                {
+                    state.bufferPos++;
+                    return 0F;
+                }
+                // tag:1 + value:4 = length of 5 bytes
+                // field=1, type=32-bit = tag of 13
+                if (length != 5 || buffer[state.bufferPos + 1] != 13)
+                {
+                    return ReadFloatWrapperSlow(ref buffer, ref state);
+                }
+                state.bufferPos += 2;
+                return ParsingPrimitives.ParseFloat(ref buffer, ref state);
+            }
+            else
+            {
+                return ReadFloatWrapperSlow(ref buffer, ref state);
+            }
+        }
+
+        internal static float? ReadFloatWrapperSlow(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            int length = ParsingPrimitives.ParseLength(ref buffer, ref state);
+            if (length == 0)
+            {
+                return 0F;
+            }
+            int finalBufferPos = state.totalBytesRetired + state.bufferPos + length;
+            float result = 0F;
+            do
+            {
+                // field=1, type=32-bit = tag of 13
+                if (ParsingPrimitives.ParseTag(ref buffer, ref state) == 13)
+                {
+                    result = ParsingPrimitives.ParseFloat(ref buffer, ref state);
+                }
+                else
+                {
+                    ParsingPrimitivesMessages.SkipLastField(ref buffer, ref state);
+                }
+            }
+            while (state.totalBytesRetired + state.bufferPos < finalBufferPos);
+            return result;
+        }
+
+        internal static double? ReadDoubleWrapperLittleEndian(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            // length:1 + tag:1 + value:8 = 10 bytes
+            if (state.bufferPos + 10 <= state.bufferSize)
+            {
+                // The entire wrapper message is already contained in `buffer`.
+                int length = buffer[state.bufferPos];
+                if (length == 0)
+                {
+                    state.bufferPos++;
+                    return 0D;
+                }
+                // tag:1 + value:8 = length of 9 bytes
+                // field=1, type=64-bit = tag of 9
+                if (length != 9 || buffer[state.bufferPos + 1] != 9)
+                {
+                    return ReadDoubleWrapperSlow(ref buffer, ref state);
+                }
+                state.bufferPos += 2;
+                return ParsingPrimitives.ParseDouble(ref buffer, ref state);
+            }
+            else
+            {
+                return ReadDoubleWrapperSlow(ref buffer, ref state);
+            }
+        }
+
+        internal static double? ReadDoubleWrapperSlow(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            int length = ParsingPrimitives.ParseLength(ref buffer, ref state);
+            if (length == 0)
+            {
+                return 0D;
+            }
+            int finalBufferPos = state.totalBytesRetired + state.bufferPos + length;
+            double result = 0D;
+            do
+            {
+                // field=1, type=64-bit = tag of 9
+                if (ParsingPrimitives.ParseTag(ref buffer, ref state) == 9)
+                {
+                    result = ParsingPrimitives.ParseDouble(ref buffer, ref state);
+                }
+                else
+                {
+                    ParsingPrimitivesMessages.SkipLastField(ref buffer, ref state);
+                }
+            }
+            while (state.totalBytesRetired + state.bufferPos < finalBufferPos);
+            return result;
+        }
+
+        internal static bool? ReadBoolWrapper(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            return ReadUInt64Wrapper(ref buffer, ref state) != 0;
+        }
+
+        internal static uint? ReadUInt32Wrapper(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            // length:1 + tag:1 + value:5(varint32-max) = 7 bytes
+            if (state.bufferPos + 7 <= state.bufferSize)
+            {
+                // The entire wrapper message is already contained in `buffer`.
+                int pos0 = state.bufferPos;
+                int length = buffer[state.bufferPos++];
+                if (length == 0)
+                {
+                    return 0;
+                }
+                // Length will always fit in a single byte.
+                if (length >= 128)
+                {
+                    state.bufferPos = pos0;
+                    return ReadUInt32WrapperSlow(ref buffer, ref state);
+                }
+                int finalBufferPos = state.bufferPos + length;
+                // field=1, type=varint = tag of 8
+                if (buffer[state.bufferPos++] != 8)
+                {
+                    state.bufferPos = pos0;
+                    return ReadUInt32WrapperSlow(ref buffer, ref state);
+                }
+                var result = ParsingPrimitives.ParseRawVarint32(ref buffer, ref state);
+                // Verify this message only contained a single field.
+                if (state.bufferPos != finalBufferPos)
+                {
+                    state.bufferPos = pos0;
+                    return ReadUInt32WrapperSlow(ref buffer, ref state);
+                }
+                return result;
+            }
+            else
+            {
+                return ReadUInt32WrapperSlow(ref buffer, ref state);
+            }
+        }
+
+        internal static uint? ReadUInt32WrapperSlow(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            int length = ParsingPrimitives.ParseLength(ref buffer, ref state);
+            if (length == 0)
+            {
+                return 0;
+            }
+            int finalBufferPos = state.totalBytesRetired + state.bufferPos + length;
+            uint result = 0;
+            do
+            {
+                // field=1, type=varint = tag of 8
+                if (ParsingPrimitives.ParseTag(ref buffer, ref state) == 8)
+                {
+                    result = ParsingPrimitives.ParseRawVarint32(ref buffer, ref state);
+                }
+                else
+                {
+                    ParsingPrimitivesMessages.SkipLastField(ref buffer, ref state);
+                }
+            }
+            while (state.totalBytesRetired + state.bufferPos < finalBufferPos);
+            return result;
+        }
+
+        internal static int? ReadInt32Wrapper(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            return (int?)ReadUInt32Wrapper(ref buffer, ref state);
+        }
+
+        internal static ulong? ReadUInt64Wrapper(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            // field=1, type=varint = tag of 8
+            const int expectedTag = 8;
+            // length:1 + tag:1 + value:10(varint64-max) = 12 bytes
+            if (state.bufferPos + 12 <= state.bufferSize)
+            {
+                // The entire wrapper message is already contained in `buffer`.
+                int pos0 = state.bufferPos;
+                int length = buffer[state.bufferPos++];
+                if (length == 0)
+                {
+                    return 0L;
+                }
+                // Length will always fit in a single byte.
+                if (length >= 128)
+                {
+                    state.bufferPos = pos0;
+                    return ReadUInt64WrapperSlow(ref buffer, ref state);
+                }
+                int finalBufferPos = state.bufferPos + length;
+                if (buffer[state.bufferPos++] != expectedTag)
+                {
+                    state.bufferPos = pos0;
+                    return ReadUInt64WrapperSlow(ref buffer, ref state);
+                }
+                var result = ParsingPrimitives.ParseRawVarint64(ref buffer, ref state);
+                // Verify this message only contained a single field.
+                if (state.bufferPos != finalBufferPos)
+                {
+                    state.bufferPos = pos0;
+                    return ReadUInt64WrapperSlow(ref buffer, ref state);
+                }
+                return result;
+            }
+            else
+            {
+                return ReadUInt64WrapperSlow(ref buffer, ref state);
+            }
+        }
+
+        internal static ulong? ReadUInt64WrapperSlow(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            // field=1, type=varint = tag of 8
+            const int expectedTag = 8;
+            int length = ParsingPrimitives.ParseLength(ref buffer, ref state);
+            if (length == 0)
+            {
+                return 0L;
+            }
+            int finalBufferPos = state.totalBytesRetired + state.bufferPos + length;
+            ulong result = 0L;
+            do
+            {
+                if (ParsingPrimitives.ParseTag(ref buffer, ref state) == expectedTag)
+                {
+                    result = ParsingPrimitives.ParseRawVarint64(ref buffer, ref state);
+                }
+                else
+                {
+                    ParsingPrimitivesMessages.SkipLastField(ref buffer, ref state);
+                }
+            }
+            while (state.totalBytesRetired + state.bufferPos < finalBufferPos);
+            return result;
+        }
+
+        internal static long? ReadInt64Wrapper(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            return (long?)ReadUInt64Wrapper(ref buffer, ref state);
+        }
+
+        internal static float? ReadFloatWrapperLittleEndian(ref ParseContext ctx)
+        {
+            return ParsingPrimitivesWrappers.ReadFloatWrapperLittleEndian(ref ctx.buffer, ref ctx.state);
+        }
+
+        internal static float? ReadFloatWrapperSlow(ref ParseContext ctx)
+        {
+            return ParsingPrimitivesWrappers.ReadFloatWrapperSlow(ref ctx.buffer, ref ctx.state);
+        }
+
+        internal static double? ReadDoubleWrapperLittleEndian(ref ParseContext ctx)
+        {
+            return ParsingPrimitivesWrappers.ReadDoubleWrapperLittleEndian(ref ctx.buffer, ref ctx.state);
+        }
+
+        internal static double? ReadDoubleWrapperSlow(ref ParseContext ctx)
+        {
+            return ParsingPrimitivesWrappers.ReadDoubleWrapperSlow(ref ctx.buffer, ref ctx.state);
+        }
+
+        internal static bool? ReadBoolWrapper(ref ParseContext ctx)
+        {
+            return ParsingPrimitivesWrappers.ReadBoolWrapper(ref ctx.buffer, ref ctx.state);
+        }
+
+        internal static uint? ReadUInt32Wrapper(ref ParseContext ctx)
+        {
+            return ParsingPrimitivesWrappers.ReadUInt32Wrapper(ref ctx.buffer, ref ctx.state);
+        }
+
+        internal static int? ReadInt32Wrapper(ref ParseContext ctx)
+        {
+            return ParsingPrimitivesWrappers.ReadInt32Wrapper(ref ctx.buffer, ref ctx.state);
+        }
+
+        internal static ulong? ReadUInt64Wrapper(ref ParseContext ctx)
+        {
+            return ParsingPrimitivesWrappers.ReadUInt64Wrapper(ref ctx.buffer, ref ctx.state);
+        }
+
+        internal static ulong? ReadUInt64WrapperSlow(ref ParseContext ctx)
+        {
+            return ParsingPrimitivesWrappers.ReadUInt64WrapperSlow(ref ctx.buffer, ref ctx.state);
+        }
+
+        internal static long? ReadInt64Wrapper(ref ParseContext ctx)
+        {
+            return ParsingPrimitivesWrappers.ReadInt64Wrapper(ref ctx.buffer, ref ctx.state);
+        }
+    }
+}

+ 7 - 0
csharp/src/Google.Protobuf/Properties/AssemblyInfo.cs

@@ -47,3 +47,10 @@ using System.Security;
     "981207041fd5b2da3b498346fcfcd94910d52f25537c4a43ce3fbe17dc7d43e6cbdb4d8f1242dc" +
     "b6bd9b5906be74da8daa7d7280f97130f318a16c07baf118839b156299a48522f9fae2371c9665" +
     "c5ae9cb6")]
+
+[assembly: InternalsVisibleTo("Google.Protobuf.Benchmarks, PublicKey=" +
+    "002400000480000094000000060200000024000052534131000400000100010025800fbcfc63a1" +
+    "7c66b303aae80b03a6beaa176bb6bef883be436f2a1579edd80ce23edf151a1f4ced97af83abcd" +
+    "981207041fd5b2da3b498346fcfcd94910d52f25537c4a43ce3fbe17dc7d43e6cbdb4d8f1242dc" +
+    "b6bd9b5906be74da8daa7d7280f97130f318a16c07baf118839b156299a48522f9fae2371c9665" +
+    "c5ae9cb6")]

+ 234 - 99
csharp/src/Google.Protobuf/Reflection/Descriptor.cs

@@ -194,7 +194,7 @@ namespace Google.Protobuf.Reflection {
   /// The protocol compiler can output a FileDescriptorSet containing the .proto
   /// files it parses.
   /// </summary>
-  public sealed partial class FileDescriptorSet : pb::IMessage<FileDescriptorSet> {
+  public sealed partial class FileDescriptorSet : pb::IMessage<FileDescriptorSet>, pb::IBufferMessage {
     private static readonly pb::MessageParser<FileDescriptorSet> _parser = new pb::MessageParser<FileDescriptorSet>(() => new FileDescriptorSet());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -299,14 +299,19 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            file_.AddEntriesFrom(input, _repeated_file_codec);
+            file_.AddEntriesFrom(ref input, _repeated_file_codec);
             break;
           }
         }
@@ -318,7 +323,7 @@ namespace Google.Protobuf.Reflection {
   /// <summary>
   /// Describes a complete .proto file.
   /// </summary>
-  public sealed partial class FileDescriptorProto : pb::IMessage<FileDescriptorProto> {
+  public sealed partial class FileDescriptorProto : pb::IMessage<FileDescriptorProto>, pb::IBufferMessage {
     private static readonly pb::MessageParser<FileDescriptorProto> _parser = new pb::MessageParser<FileDescriptorProto>(() => new FileDescriptorProto());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -710,11 +715,16 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
@@ -725,23 +735,23 @@ namespace Google.Protobuf.Reflection {
             break;
           }
           case 26: {
-            dependency_.AddEntriesFrom(input, _repeated_dependency_codec);
+            dependency_.AddEntriesFrom(ref input, _repeated_dependency_codec);
             break;
           }
           case 34: {
-            messageType_.AddEntriesFrom(input, _repeated_messageType_codec);
+            messageType_.AddEntriesFrom(ref input, _repeated_messageType_codec);
             break;
           }
           case 42: {
-            enumType_.AddEntriesFrom(input, _repeated_enumType_codec);
+            enumType_.AddEntriesFrom(ref input, _repeated_enumType_codec);
             break;
           }
           case 50: {
-            service_.AddEntriesFrom(input, _repeated_service_codec);
+            service_.AddEntriesFrom(ref input, _repeated_service_codec);
             break;
           }
           case 58: {
-            extension_.AddEntriesFrom(input, _repeated_extension_codec);
+            extension_.AddEntriesFrom(ref input, _repeated_extension_codec);
             break;
           }
           case 66: {
@@ -760,12 +770,12 @@ namespace Google.Protobuf.Reflection {
           }
           case 82:
           case 80: {
-            publicDependency_.AddEntriesFrom(input, _repeated_publicDependency_codec);
+            publicDependency_.AddEntriesFrom(ref input, _repeated_publicDependency_codec);
             break;
           }
           case 90:
           case 88: {
-            weakDependency_.AddEntriesFrom(input, _repeated_weakDependency_codec);
+            weakDependency_.AddEntriesFrom(ref input, _repeated_weakDependency_codec);
             break;
           }
           case 98: {
@@ -781,7 +791,7 @@ namespace Google.Protobuf.Reflection {
   /// <summary>
   /// Describes a message type.
   /// </summary>
-  public sealed partial class DescriptorProto : pb::IMessage<DescriptorProto> {
+  public sealed partial class DescriptorProto : pb::IMessage<DescriptorProto>, pb::IBufferMessage {
     private static readonly pb::MessageParser<DescriptorProto> _parser = new pb::MessageParser<DescriptorProto>(() => new DescriptorProto());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1065,34 +1075,39 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
             break;
           }
           case 18: {
-            field_.AddEntriesFrom(input, _repeated_field_codec);
+            field_.AddEntriesFrom(ref input, _repeated_field_codec);
             break;
           }
           case 26: {
-            nestedType_.AddEntriesFrom(input, _repeated_nestedType_codec);
+            nestedType_.AddEntriesFrom(ref input, _repeated_nestedType_codec);
             break;
           }
           case 34: {
-            enumType_.AddEntriesFrom(input, _repeated_enumType_codec);
+            enumType_.AddEntriesFrom(ref input, _repeated_enumType_codec);
             break;
           }
           case 42: {
-            extensionRange_.AddEntriesFrom(input, _repeated_extensionRange_codec);
+            extensionRange_.AddEntriesFrom(ref input, _repeated_extensionRange_codec);
             break;
           }
           case 50: {
-            extension_.AddEntriesFrom(input, _repeated_extension_codec);
+            extension_.AddEntriesFrom(ref input, _repeated_extension_codec);
             break;
           }
           case 58: {
@@ -1103,15 +1118,15 @@ namespace Google.Protobuf.Reflection {
             break;
           }
           case 66: {
-            oneofDecl_.AddEntriesFrom(input, _repeated_oneofDecl_codec);
+            oneofDecl_.AddEntriesFrom(ref input, _repeated_oneofDecl_codec);
             break;
           }
           case 74: {
-            reservedRange_.AddEntriesFrom(input, _repeated_reservedRange_codec);
+            reservedRange_.AddEntriesFrom(ref input, _repeated_reservedRange_codec);
             break;
           }
           case 82: {
-            reservedName_.AddEntriesFrom(input, _repeated_reservedName_codec);
+            reservedName_.AddEntriesFrom(ref input, _repeated_reservedName_codec);
             break;
           }
         }
@@ -1122,7 +1137,7 @@ namespace Google.Protobuf.Reflection {
     /// <summary>Container for nested types declared in the DescriptorProto message type.</summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static partial class Types {
-      public sealed partial class ExtensionRange : pb::IMessage<ExtensionRange> {
+      public sealed partial class ExtensionRange : pb::IMessage<ExtensionRange>, pb::IBufferMessage {
         private static readonly pb::MessageParser<ExtensionRange> _parser = new pb::MessageParser<ExtensionRange>(() => new ExtensionRange());
         private pb::UnknownFieldSet _unknownFields;
         private int _hasBits0;
@@ -1320,11 +1335,16 @@ namespace Google.Protobuf.Reflection {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 8: {
                 Start = input.ReadInt32();
@@ -1352,7 +1372,7 @@ namespace Google.Protobuf.Reflection {
       /// fields or extension ranges in the same message. Reserved ranges may
       /// not overlap.
       /// </summary>
-      public sealed partial class ReservedRange : pb::IMessage<ReservedRange> {
+      public sealed partial class ReservedRange : pb::IMessage<ReservedRange>, pb::IBufferMessage {
         private static readonly pb::MessageParser<ReservedRange> _parser = new pb::MessageParser<ReservedRange>(() => new ReservedRange());
         private pb::UnknownFieldSet _unknownFields;
         private int _hasBits0;
@@ -1523,11 +1543,16 @@ namespace Google.Protobuf.Reflection {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 8: {
                 Start = input.ReadInt32();
@@ -1548,7 +1573,7 @@ namespace Google.Protobuf.Reflection {
 
   }
 
-  public sealed partial class ExtensionRangeOptions : pb::IExtendableMessage<ExtensionRangeOptions> {
+  public sealed partial class ExtensionRangeOptions : pb::IExtendableMessage<ExtensionRangeOptions>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ExtensionRangeOptions> _parser = new pb::MessageParser<ExtensionRangeOptions>(() => new ExtensionRangeOptions());
     private pb::UnknownFieldSet _unknownFields;
     internal pb::ExtensionSet<ExtensionRangeOptions> _extensions;
@@ -1672,16 +1697,21 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
-              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             }
             break;
           case 7994: {
-            uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
+            uninterpretedOption_.AddEntriesFrom(ref input, _repeated_uninterpretedOption_codec);
             break;
           }
         }
@@ -1715,7 +1745,7 @@ namespace Google.Protobuf.Reflection {
   /// <summary>
   /// Describes a field within a message.
   /// </summary>
-  public sealed partial class FieldDescriptorProto : pb::IMessage<FieldDescriptorProto> {
+  public sealed partial class FieldDescriptorProto : pb::IMessage<FieldDescriptorProto>, pb::IBufferMessage {
     private static readonly pb::MessageParser<FieldDescriptorProto> _parser = new pb::MessageParser<FieldDescriptorProto>(() => new FieldDescriptorProto());
     private pb::UnknownFieldSet _unknownFields;
     private int _hasBits0;
@@ -2253,11 +2283,16 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
@@ -2382,7 +2417,7 @@ namespace Google.Protobuf.Reflection {
   /// <summary>
   /// Describes a oneof.
   /// </summary>
-  public sealed partial class OneofDescriptorProto : pb::IMessage<OneofDescriptorProto> {
+  public sealed partial class OneofDescriptorProto : pb::IMessage<OneofDescriptorProto>, pb::IBufferMessage {
     private static readonly pb::MessageParser<OneofDescriptorProto> _parser = new pb::MessageParser<OneofDescriptorProto>(() => new OneofDescriptorProto());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2534,11 +2569,16 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
@@ -2560,7 +2600,7 @@ namespace Google.Protobuf.Reflection {
   /// <summary>
   /// Describes an enum type.
   /// </summary>
-  public sealed partial class EnumDescriptorProto : pb::IMessage<EnumDescriptorProto> {
+  public sealed partial class EnumDescriptorProto : pb::IMessage<EnumDescriptorProto>, pb::IBufferMessage {
     private static readonly pb::MessageParser<EnumDescriptorProto> _parser = new pb::MessageParser<EnumDescriptorProto>(() => new EnumDescriptorProto());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -2769,18 +2809,23 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
             break;
           }
           case 18: {
-            value_.AddEntriesFrom(input, _repeated_value_codec);
+            value_.AddEntriesFrom(ref input, _repeated_value_codec);
             break;
           }
           case 26: {
@@ -2791,11 +2836,11 @@ namespace Google.Protobuf.Reflection {
             break;
           }
           case 34: {
-            reservedRange_.AddEntriesFrom(input, _repeated_reservedRange_codec);
+            reservedRange_.AddEntriesFrom(ref input, _repeated_reservedRange_codec);
             break;
           }
           case 42: {
-            reservedName_.AddEntriesFrom(input, _repeated_reservedName_codec);
+            reservedName_.AddEntriesFrom(ref input, _repeated_reservedName_codec);
             break;
           }
         }
@@ -2814,7 +2859,7 @@ namespace Google.Protobuf.Reflection {
       /// is inclusive such that it can appropriately represent the entire int32
       /// domain.
       /// </summary>
-      public sealed partial class EnumReservedRange : pb::IMessage<EnumReservedRange> {
+      public sealed partial class EnumReservedRange : pb::IMessage<EnumReservedRange>, pb::IBufferMessage {
         private static readonly pb::MessageParser<EnumReservedRange> _parser = new pb::MessageParser<EnumReservedRange>(() => new EnumReservedRange());
         private pb::UnknownFieldSet _unknownFields;
         private int _hasBits0;
@@ -2985,11 +3030,16 @@ namespace Google.Protobuf.Reflection {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 8: {
                 Start = input.ReadInt32();
@@ -3013,7 +3063,7 @@ namespace Google.Protobuf.Reflection {
   /// <summary>
   /// Describes a value within an enum.
   /// </summary>
-  public sealed partial class EnumValueDescriptorProto : pb::IMessage<EnumValueDescriptorProto> {
+  public sealed partial class EnumValueDescriptorProto : pb::IMessage<EnumValueDescriptorProto>, pb::IBufferMessage {
     private static readonly pb::MessageParser<EnumValueDescriptorProto> _parser = new pb::MessageParser<EnumValueDescriptorProto>(() => new EnumValueDescriptorProto());
     private pb::UnknownFieldSet _unknownFields;
     private int _hasBits0;
@@ -3204,11 +3254,16 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
@@ -3234,7 +3289,7 @@ namespace Google.Protobuf.Reflection {
   /// <summary>
   /// Describes a service.
   /// </summary>
-  public sealed partial class ServiceDescriptorProto : pb::IMessage<ServiceDescriptorProto> {
+  public sealed partial class ServiceDescriptorProto : pb::IMessage<ServiceDescriptorProto>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ServiceDescriptorProto> _parser = new pb::MessageParser<ServiceDescriptorProto>(() => new ServiceDescriptorProto());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -3402,18 +3457,23 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
             break;
           }
           case 18: {
-            method_.AddEntriesFrom(input, _repeated_method_codec);
+            method_.AddEntriesFrom(ref input, _repeated_method_codec);
             break;
           }
           case 26: {
@@ -3432,7 +3492,7 @@ namespace Google.Protobuf.Reflection {
   /// <summary>
   /// Describes a method of a service.
   /// </summary>
-  public sealed partial class MethodDescriptorProto : pb::IMessage<MethodDescriptorProto> {
+  public sealed partial class MethodDescriptorProto : pb::IMessage<MethodDescriptorProto>, pb::IBufferMessage {
     private static readonly pb::MessageParser<MethodDescriptorProto> _parser = new pb::MessageParser<MethodDescriptorProto>(() => new MethodDescriptorProto());
     private pb::UnknownFieldSet _unknownFields;
     private int _hasBits0;
@@ -3742,11 +3802,16 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
@@ -3781,7 +3846,7 @@ namespace Google.Protobuf.Reflection {
 
   }
 
-  public sealed partial class FileOptions : pb::IExtendableMessage<FileOptions> {
+  public sealed partial class FileOptions : pb::IExtendableMessage<FileOptions>, pb::IBufferMessage {
     private static readonly pb::MessageParser<FileOptions> _parser = new pb::MessageParser<FileOptions>(() => new FileOptions());
     private pb::UnknownFieldSet _unknownFields;
     internal pb::ExtensionSet<FileOptions> _extensions;
@@ -4734,12 +4799,17 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
-              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             }
             break;
           case 10: {
@@ -4823,7 +4893,7 @@ namespace Google.Protobuf.Reflection {
             break;
           }
           case 7994: {
-            uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
+            uninterpretedOption_.AddEntriesFrom(ref input, _repeated_uninterpretedOption_codec);
             break;
           }
         }
@@ -4879,7 +4949,7 @@ namespace Google.Protobuf.Reflection {
 
   }
 
-  public sealed partial class MessageOptions : pb::IExtendableMessage<MessageOptions> {
+  public sealed partial class MessageOptions : pb::IExtendableMessage<MessageOptions>, pb::IBufferMessage {
     private static readonly pb::MessageParser<MessageOptions> _parser = new pb::MessageParser<MessageOptions>(() => new MessageOptions());
     private pb::UnknownFieldSet _unknownFields;
     internal pb::ExtensionSet<MessageOptions> _extensions;
@@ -5207,12 +5277,17 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
-              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             }
             break;
           case 8: {
@@ -5232,7 +5307,7 @@ namespace Google.Protobuf.Reflection {
             break;
           }
           case 7994: {
-            uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
+            uninterpretedOption_.AddEntriesFrom(ref input, _repeated_uninterpretedOption_codec);
             break;
           }
         }
@@ -5263,7 +5338,7 @@ namespace Google.Protobuf.Reflection {
 
   }
 
-  public sealed partial class FieldOptions : pb::IExtendableMessage<FieldOptions> {
+  public sealed partial class FieldOptions : pb::IExtendableMessage<FieldOptions>, pb::IBufferMessage {
     private static readonly pb::MessageParser<FieldOptions> _parser = new pb::MessageParser<FieldOptions>(() => new FieldOptions());
     private pb::UnknownFieldSet _unknownFields;
     internal pb::ExtensionSet<FieldOptions> _extensions;
@@ -5675,12 +5750,17 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
-              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             }
             break;
           case 8: {
@@ -5708,7 +5788,7 @@ namespace Google.Protobuf.Reflection {
             break;
           }
           case 7994: {
-            uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
+            uninterpretedOption_.AddEntriesFrom(ref input, _repeated_uninterpretedOption_codec);
             break;
           }
         }
@@ -5770,7 +5850,7 @@ namespace Google.Protobuf.Reflection {
 
   }
 
-  public sealed partial class OneofOptions : pb::IExtendableMessage<OneofOptions> {
+  public sealed partial class OneofOptions : pb::IExtendableMessage<OneofOptions>, pb::IBufferMessage {
     private static readonly pb::MessageParser<OneofOptions> _parser = new pb::MessageParser<OneofOptions>(() => new OneofOptions());
     private pb::UnknownFieldSet _unknownFields;
     internal pb::ExtensionSet<OneofOptions> _extensions;
@@ -5894,16 +5974,21 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
-              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             }
             break;
           case 7994: {
-            uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
+            uninterpretedOption_.AddEntriesFrom(ref input, _repeated_uninterpretedOption_codec);
             break;
           }
         }
@@ -5934,7 +6019,7 @@ namespace Google.Protobuf.Reflection {
 
   }
 
-  public sealed partial class EnumOptions : pb::IExtendableMessage<EnumOptions> {
+  public sealed partial class EnumOptions : pb::IExtendableMessage<EnumOptions>, pb::IBufferMessage {
     private static readonly pb::MessageParser<EnumOptions> _parser = new pb::MessageParser<EnumOptions>(() => new EnumOptions());
     private pb::UnknownFieldSet _unknownFields;
     internal pb::ExtensionSet<EnumOptions> _extensions;
@@ -6144,12 +6229,17 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
-              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             }
             break;
           case 16: {
@@ -6161,7 +6251,7 @@ namespace Google.Protobuf.Reflection {
             break;
           }
           case 7994: {
-            uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
+            uninterpretedOption_.AddEntriesFrom(ref input, _repeated_uninterpretedOption_codec);
             break;
           }
         }
@@ -6192,7 +6282,7 @@ namespace Google.Protobuf.Reflection {
 
   }
 
-  public sealed partial class EnumValueOptions : pb::IExtendableMessage<EnumValueOptions> {
+  public sealed partial class EnumValueOptions : pb::IExtendableMessage<EnumValueOptions>, pb::IBufferMessage {
     private static readonly pb::MessageParser<EnumValueOptions> _parser = new pb::MessageParser<EnumValueOptions>(() => new EnumValueOptions());
     private pb::UnknownFieldSet _unknownFields;
     internal pb::ExtensionSet<EnumValueOptions> _extensions;
@@ -6361,12 +6451,17 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
-              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             }
             break;
           case 8: {
@@ -6374,7 +6469,7 @@ namespace Google.Protobuf.Reflection {
             break;
           }
           case 7994: {
-            uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
+            uninterpretedOption_.AddEntriesFrom(ref input, _repeated_uninterpretedOption_codec);
             break;
           }
         }
@@ -6405,7 +6500,7 @@ namespace Google.Protobuf.Reflection {
 
   }
 
-  public sealed partial class ServiceOptions : pb::IExtendableMessage<ServiceOptions> {
+  public sealed partial class ServiceOptions : pb::IExtendableMessage<ServiceOptions>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ServiceOptions> _parser = new pb::MessageParser<ServiceOptions>(() => new ServiceOptions());
     private pb::UnknownFieldSet _unknownFields;
     internal pb::ExtensionSet<ServiceOptions> _extensions;
@@ -6574,12 +6669,17 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
-              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             }
             break;
           case 264: {
@@ -6587,7 +6687,7 @@ namespace Google.Protobuf.Reflection {
             break;
           }
           case 7994: {
-            uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
+            uninterpretedOption_.AddEntriesFrom(ref input, _repeated_uninterpretedOption_codec);
             break;
           }
         }
@@ -6618,7 +6718,7 @@ namespace Google.Protobuf.Reflection {
 
   }
 
-  public sealed partial class MethodOptions : pb::IExtendableMessage<MethodOptions> {
+  public sealed partial class MethodOptions : pb::IExtendableMessage<MethodOptions>, pb::IBufferMessage {
     private static readonly pb::MessageParser<MethodOptions> _parser = new pb::MessageParser<MethodOptions>(() => new MethodOptions());
     private pb::UnknownFieldSet _unknownFields;
     internal pb::ExtensionSet<MethodOptions> _extensions;
@@ -6824,12 +6924,17 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
-              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             }
             break;
           case 264: {
@@ -6841,7 +6946,7 @@ namespace Google.Protobuf.Reflection {
             break;
           }
           case 7994: {
-            uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
+            uninterpretedOption_.AddEntriesFrom(ref input, _repeated_uninterpretedOption_codec);
             break;
           }
         }
@@ -6904,7 +7009,7 @@ namespace Google.Protobuf.Reflection {
   /// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
   /// in them.
   /// </summary>
-  public sealed partial class UninterpretedOption : pb::IMessage<UninterpretedOption> {
+  public sealed partial class UninterpretedOption : pb::IMessage<UninterpretedOption>, pb::IBufferMessage {
     private static readonly pb::MessageParser<UninterpretedOption> _parser = new pb::MessageParser<UninterpretedOption>(() => new UninterpretedOption());
     private pb::UnknownFieldSet _unknownFields;
     private int _hasBits0;
@@ -7234,14 +7339,19 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 18: {
-            name_.AddEntriesFrom(input, _repeated_name_codec);
+            name_.AddEntriesFrom(ref input, _repeated_name_codec);
             break;
           }
           case 26: {
@@ -7283,7 +7393,7 @@ namespace Google.Protobuf.Reflection {
       /// E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
       /// "foo.(bar.baz).qux".
       /// </summary>
-      public sealed partial class NamePart : pb::IMessage<NamePart> {
+      public sealed partial class NamePart : pb::IMessage<NamePart>, pb::IBufferMessage {
         private static readonly pb::MessageParser<NamePart> _parser = new pb::MessageParser<NamePart>(() => new NamePart());
         private pb::UnknownFieldSet _unknownFields;
         private int _hasBits0;
@@ -7447,11 +7557,16 @@ namespace Google.Protobuf.Reflection {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 10: {
                 NamePart_ = input.ReadString();
@@ -7476,7 +7591,7 @@ namespace Google.Protobuf.Reflection {
   /// Encapsulates information about the original source file from which a
   /// FileDescriptorProto was generated.
   /// </summary>
-  public sealed partial class SourceCodeInfo : pb::IMessage<SourceCodeInfo> {
+  public sealed partial class SourceCodeInfo : pb::IMessage<SourceCodeInfo>, pb::IBufferMessage {
     private static readonly pb::MessageParser<SourceCodeInfo> _parser = new pb::MessageParser<SourceCodeInfo>(() => new SourceCodeInfo());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -7626,14 +7741,19 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            location_.AddEntriesFrom(input, _repeated_location_codec);
+            location_.AddEntriesFrom(ref input, _repeated_location_codec);
             break;
           }
         }
@@ -7644,7 +7764,7 @@ namespace Google.Protobuf.Reflection {
     /// <summary>Container for nested types declared in the SourceCodeInfo message type.</summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static partial class Types {
-      public sealed partial class Location : pb::IMessage<Location> {
+      public sealed partial class Location : pb::IMessage<Location>, pb::IBufferMessage {
         private static readonly pb::MessageParser<Location> _parser = new pb::MessageParser<Location>(() => new Location());
         private pb::UnknownFieldSet _unknownFields;
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -7934,20 +8054,25 @@ namespace Google.Protobuf.Reflection {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 10:
               case 8: {
-                path_.AddEntriesFrom(input, _repeated_path_codec);
+                path_.AddEntriesFrom(ref input, _repeated_path_codec);
                 break;
               }
               case 18:
               case 16: {
-                span_.AddEntriesFrom(input, _repeated_span_codec);
+                span_.AddEntriesFrom(ref input, _repeated_span_codec);
                 break;
               }
               case 26: {
@@ -7959,7 +8084,7 @@ namespace Google.Protobuf.Reflection {
                 break;
               }
               case 50: {
-                leadingDetachedComments_.AddEntriesFrom(input, _repeated_leadingDetachedComments_codec);
+                leadingDetachedComments_.AddEntriesFrom(ref input, _repeated_leadingDetachedComments_codec);
                 break;
               }
             }
@@ -7978,7 +8103,7 @@ namespace Google.Protobuf.Reflection {
   /// file. A GeneratedCodeInfo message is associated with only one generated
   /// source file, but may contain references to different source .proto files.
   /// </summary>
-  public sealed partial class GeneratedCodeInfo : pb::IMessage<GeneratedCodeInfo> {
+  public sealed partial class GeneratedCodeInfo : pb::IMessage<GeneratedCodeInfo>, pb::IBufferMessage {
     private static readonly pb::MessageParser<GeneratedCodeInfo> _parser = new pb::MessageParser<GeneratedCodeInfo>(() => new GeneratedCodeInfo());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -8087,14 +8212,19 @@ namespace Google.Protobuf.Reflection {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            annotation_.AddEntriesFrom(input, _repeated_annotation_codec);
+            annotation_.AddEntriesFrom(ref input, _repeated_annotation_codec);
             break;
           }
         }
@@ -8105,7 +8235,7 @@ namespace Google.Protobuf.Reflection {
     /// <summary>Container for nested types declared in the GeneratedCodeInfo message type.</summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static partial class Types {
-      public sealed partial class Annotation : pb::IMessage<Annotation> {
+      public sealed partial class Annotation : pb::IMessage<Annotation>, pb::IBufferMessage {
         private static readonly pb::MessageParser<Annotation> _parser = new pb::MessageParser<Annotation>(() => new Annotation());
         private pb::UnknownFieldSet _unknownFields;
         private int _hasBits0;
@@ -8338,15 +8468,20 @@ namespace Google.Protobuf.Reflection {
 
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public void MergeFrom(pb::CodedInputStream input) {
+          input.ReadRawMessage(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
           uint tag;
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                 break;
               case 10:
               case 8: {
-                path_.AddEntriesFrom(input, _repeated_path_codec);
+                path_.AddEntriesFrom(ref input, _repeated_path_codec);
                 break;
               }
               case 18: {

+ 296 - 0
csharp/src/Google.Protobuf/SegmentedBufferHelper.cs

@@ -0,0 +1,296 @@
+#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 reading from a stream / read only sequence.
+    /// Parsing from the buffer is a loop of reading from current buffer / refreshing the buffer once done.
+    /// </summary>
+    [SecuritySafeCritical]
+    internal struct SegmentedBufferHelper
+    {
+        private int? totalLength;
+        private ReadOnlySequence<byte>.Enumerator readOnlySequenceEnumerator;
+        private CodedInputStream codedInputStream;
+
+        /// <summary>
+        /// Initialize an instance with a coded input 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(CodedInputStream codedInputStream, out SegmentedBufferHelper instance)
+        {
+            instance.totalLength = codedInputStream.InternalInputStream == null ? (int?)codedInputStream.InternalBuffer.Length : null;
+            instance.readOnlySequenceEnumerator = default;
+            instance.codedInputStream = codedInputStream;
+        }
+
+        /// <summary>
+        /// Initialize an instance with a read only sequence.
+        /// 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(ReadOnlySequence<byte> sequence, out SegmentedBufferHelper instance, out ReadOnlySpan<byte> firstSpan)
+        {
+            instance.codedInputStream = null;
+            if (sequence.IsSingleSegment)
+            {
+                firstSpan = sequence.First.Span;
+                instance.totalLength = firstSpan.Length;
+                instance.readOnlySequenceEnumerator = default;
+            }
+            else
+            {
+                instance.readOnlySequenceEnumerator = sequence.GetEnumerator();
+                instance.totalLength = (int) sequence.Length;
+
+                // set firstSpan to the first segment
+                instance.readOnlySequenceEnumerator.MoveNext();
+                firstSpan = instance.readOnlySequenceEnumerator.Current.Span;
+            }
+        }
+        
+        public bool RefillBuffer(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, bool mustSucceed)
+        {
+            if (codedInputStream != null)
+            {
+                return RefillFromCodedInputStream(ref buffer, ref state, mustSucceed);
+            }
+            else
+            {
+                return RefillFromReadOnlySequence(ref buffer, ref state, mustSucceed);
+            }
+        }
+
+        public int? TotalLength => totalLength;
+
+        public CodedInputStream CodedInputStream => codedInputStream;
+
+        /// <summary>
+        /// Sets currentLimit to (current position) + byteLimit. This is called
+        /// when descending into a length-delimited embedded message. The previous
+        /// limit is returned.
+        /// </summary>
+        /// <returns>The old limit.</returns>
+        public static int PushLimit(ref ParserInternalState state, int byteLimit)
+        {
+            if (byteLimit < 0)
+            {
+                throw InvalidProtocolBufferException.NegativeSize();
+            }
+            byteLimit += state.totalBytesRetired + state.bufferPos;
+            int oldLimit = state.currentLimit;
+            if (byteLimit > oldLimit)
+            {
+                throw InvalidProtocolBufferException.TruncatedMessage();
+            }
+            state.currentLimit = byteLimit;
+
+            RecomputeBufferSizeAfterLimit(ref state);
+
+            return oldLimit;
+        }
+
+        /// <summary>
+        /// Discards the current limit, returning the previous limit.
+        /// </summary>
+        public static void PopLimit(ref ParserInternalState state, int oldLimit)
+        {
+            state.currentLimit = oldLimit;
+            RecomputeBufferSizeAfterLimit(ref state);
+        }
+
+        /// <summary>
+        /// Returns whether or not all the data before the limit has been read.
+        /// </summary>
+        /// <returns></returns>
+        public static bool IsReachedLimit(ref ParserInternalState state)
+        {
+            if (state.currentLimit == int.MaxValue)
+            {
+                return false;
+            }
+            int currentAbsolutePosition = state.totalBytesRetired + state.bufferPos;
+            return currentAbsolutePosition >= state.currentLimit;
+        }
+
+        /// <summary>
+        /// Returns true if the stream has reached the end of the input. This is the
+        /// case if either the end of the underlying input source has been reached or
+        /// the stream has reached a limit created using PushLimit.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool IsAtEnd(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
+        {
+            return state.bufferPos == state.bufferSize && !state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, false);
+        }
+
+        private bool RefillFromReadOnlySequence(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, bool mustSucceed)
+        {
+            CheckCurrentBufferIsEmpty(ref state);
+
+            if (state.totalBytesRetired + state.bufferSize == state.currentLimit)
+            {
+                // Oops, we hit a limit.
+                if (mustSucceed)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+                else
+                {
+                    return false;
+                }
+            }
+
+            state.totalBytesRetired += state.bufferSize;
+
+            state.bufferPos = 0;
+            state.bufferSize = 0;
+            while (readOnlySequenceEnumerator.MoveNext())
+            {
+                buffer = readOnlySequenceEnumerator.Current.Span;
+                state.bufferSize = buffer.Length;
+                if (buffer.Length != 0)
+                {
+                    break;
+                }
+            }
+
+            if (state.bufferSize == 0)
+            {
+                if (mustSucceed)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+                else
+                {
+                    return false;
+                }
+            }
+            else
+            {
+                RecomputeBufferSizeAfterLimit(ref state);
+                int totalBytesRead =
+                    state.totalBytesRetired + state.bufferSize + state.bufferSizeAfterLimit;
+                if (totalBytesRead < 0 || totalBytesRead > state.sizeLimit)
+                {
+                    throw InvalidProtocolBufferException.SizeLimitExceeded();
+                }
+                return true;
+            }
+        }
+
+        private bool RefillFromCodedInputStream(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, bool mustSucceed)
+        {
+            CheckCurrentBufferIsEmpty(ref state);
+
+            if (state.totalBytesRetired + state.bufferSize == state.currentLimit)
+            {
+                // Oops, we hit a limit.
+                if (mustSucceed)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+                else
+                {
+                    return false;
+                }
+            }
+
+            Stream input = codedInputStream.InternalInputStream;
+
+            state.totalBytesRetired += state.bufferSize;
+
+            state.bufferPos = 0;
+            state.bufferSize = (input == null) ? 0 : input.Read(codedInputStream.InternalBuffer, 0, buffer.Length);
+            if (state.bufferSize < 0)
+            {
+                throw new InvalidOperationException("Stream.Read returned a negative count");
+            }
+            if (state.bufferSize == 0)
+            {
+                if (mustSucceed)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+                else
+                {
+                    return false;
+                }
+            }
+            else
+            {
+                RecomputeBufferSizeAfterLimit(ref state);
+                int totalBytesRead =
+                    state.totalBytesRetired + state.bufferSize + state.bufferSizeAfterLimit;
+                if (totalBytesRead < 0 || totalBytesRead > state.sizeLimit)
+                {
+                    throw InvalidProtocolBufferException.SizeLimitExceeded();
+                }
+                return true;
+            }
+        }
+
+        private static void RecomputeBufferSizeAfterLimit(ref ParserInternalState state)
+        {
+            state.bufferSize += state.bufferSizeAfterLimit;
+            int bufferEnd = state.totalBytesRetired + state.bufferSize;
+            if (bufferEnd > state.currentLimit)
+            {
+                // Limit is in current buffer.
+                state.bufferSizeAfterLimit = bufferEnd - state.currentLimit;
+                state.bufferSize -= state.bufferSizeAfterLimit;
+            }
+            else
+            {
+                state.bufferSizeAfterLimit = 0;
+            }
+        }
+
+        private static void CheckCurrentBufferIsEmpty(ref ParserInternalState state)
+        {
+            if (state.bufferPos < state.bufferSize)
+            {
+                throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
+            }
+        }
+    }
+}

+ 44 - 19
csharp/src/Google.Protobuf/UnknownFieldSet.cs

@@ -33,6 +33,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Security;
 using Google.Protobuf.Reflection;
 
 namespace Google.Protobuf
@@ -176,47 +177,47 @@ namespace Google.Protobuf
             fields[number] = field;
             return this;
         }
-
+        
         /// <summary>
-        /// Parse a single field from <paramref name="input"/> and merge it
+        /// Parse a single field from <paramref name="ctx"/> and merge it
         /// into this set.
         /// </summary>
-        /// <param name="input">The coded input stream containing the field</param>
+        /// <param name="ctx">The parse context from which to read the field</param>
         /// <returns>false if the tag is an "end group" tag, true otherwise</returns>
-        private bool MergeFieldFrom(CodedInputStream input)
+        private bool MergeFieldFrom(ref ParseContext ctx)
         {
-            uint tag = input.LastTag;
+            uint tag = ctx.LastTag;
             int number = WireFormat.GetTagFieldNumber(tag);
             switch (WireFormat.GetTagWireType(tag))
             {
                 case WireFormat.WireType.Varint:
                     {
-                        ulong uint64 = input.ReadUInt64();
+                        ulong uint64 = ctx.ReadUInt64();
                         GetOrAddField(number).AddVarint(uint64);
                         return true;
                     }
                 case WireFormat.WireType.Fixed32:
                     {
-                        uint uint32 = input.ReadFixed32();
+                        uint uint32 = ctx.ReadFixed32();
                         GetOrAddField(number).AddFixed32(uint32);
                         return true;
                     }
                 case WireFormat.WireType.Fixed64:
                     {
-                        ulong uint64 = input.ReadFixed64();
+                        ulong uint64 = ctx.ReadFixed64();
                         GetOrAddField(number).AddFixed64(uint64);
                         return true;
                     }
                 case WireFormat.WireType.LengthDelimited:
                     {
-                        ByteString bytes = input.ReadBytes();
+                        ByteString bytes = ctx.ReadBytes();
                         GetOrAddField(number).AddLengthDelimited(bytes);
                         return true;
                     }
                 case WireFormat.WireType.StartGroup:
                     {
                         UnknownFieldSet set = new UnknownFieldSet();
-                        input.ReadGroup(number, set);
+                        ParsingPrimitivesMessages.ReadGroup(ref ctx, number, set);
                         GetOrAddField(number).AddGroup(set);
                         return true;
                     }
@@ -229,16 +230,16 @@ namespace Google.Protobuf
             }
         }
 
-        internal void MergeGroupFrom(CodedInputStream input)
+        internal void MergeGroupFrom(ref ParseContext ctx)
         {
             while (true)
             {
-                uint tag = input.ReadTag();
+                uint tag = ctx.ReadTag();
                 if (tag == 0)
                 {
                     break;
                 }
-                if (!MergeFieldFrom(input))
+                if (!MergeFieldFrom(ref ctx))
                 {
                     break;
                 }
@@ -257,21 +258,45 @@ namespace Google.Protobuf
         public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields,
                                                      CodedInputStream input)
         {
-            if (input.DiscardUnknownFields)
+            ParseContext.Initialize(input, out ParseContext ctx);
+            try
+            {
+                return MergeFieldFrom(unknownFields, ref ctx);
+            }
+            finally
             {
-                input.SkipLastField();
+                ctx.CopyStateTo(input);
+            }
+        }
+
+        /// <summary>
+        /// Create a new UnknownFieldSet if unknownFields is null.
+        /// Parse a single field from <paramref name="ctx"/> and merge it
+        /// into unknownFields. If <paramref name="ctx"/> is configured to discard unknown fields,
+        /// <paramref name="unknownFields"/> will be returned as-is and the field will be skipped.
+        /// </summary>
+        /// <param name="unknownFields">The UnknownFieldSet which need to be merged</param>
+        /// <param name="ctx">The parse context from which to read the field</param>
+        /// <returns>The merged UnknownFieldSet</returns>
+        [SecuritySafeCritical]
+        public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields,
+                                                     ref ParseContext ctx)
+        {
+            if (ctx.DiscardUnknownFields)
+            {
+                ParsingPrimitivesMessages.SkipLastField(ref ctx.buffer, ref ctx.state);
                 return unknownFields;
             }
             if (unknownFields == null)
             {
                 unknownFields = new UnknownFieldSet();
             }
-            if (!unknownFields.MergeFieldFrom(input))
-            {
-                throw new InvalidProtocolBufferException("Merge an unknown field of end-group tag, indicating that the corresponding start-group was missing."); // match the old code-gen
+            if (!unknownFields.MergeFieldFrom(ref ctx))
+            {
+                throw new InvalidProtocolBufferException("Merge an unknown field of end-group tag, indicating that the corresponding start-group was missing."); // match the old code-gen
             }
             return unknownFields;
-        }
+        }
 
         /// <summary>
         /// Merges the fields from <paramref name="other"/> into this set.

+ 7 - 2
csharp/src/Google.Protobuf/WellKnownTypes/Any.cs

@@ -119,7 +119,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///       "value": "1.212s"
   ///     }
   /// </summary>
-  public sealed partial class Any : pb::IMessage<Any> {
+  public sealed partial class Any : pb::IMessage<Any>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Any> _parser = new pb::MessageParser<Any>(() => new Any());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -288,11 +288,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             TypeUrl = input.ReadString();

+ 25 - 10
csharp/src/Google.Protobuf/WellKnownTypes/Api.cs

@@ -64,7 +64,7 @@ namespace Google.Protobuf.WellKnownTypes {
   /// this message itself. See https://cloud.google.com/apis/design/glossary for
   /// detailed terminology.
   /// </summary>
-  public sealed partial class Api : pb::IMessage<Api> {
+  public sealed partial class Api : pb::IMessage<Api>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Api> _parser = new pb::MessageParser<Api>(() => new Api());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -341,22 +341,27 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
             break;
           }
           case 18: {
-            methods_.AddEntriesFrom(input, _repeated_methods_codec);
+            methods_.AddEntriesFrom(ref input, _repeated_methods_codec);
             break;
           }
           case 26: {
-            options_.AddEntriesFrom(input, _repeated_options_codec);
+            options_.AddEntriesFrom(ref input, _repeated_options_codec);
             break;
           }
           case 34: {
@@ -371,7 +376,7 @@ namespace Google.Protobuf.WellKnownTypes {
             break;
           }
           case 50: {
-            mixins_.AddEntriesFrom(input, _repeated_mixins_codec);
+            mixins_.AddEntriesFrom(ref input, _repeated_mixins_codec);
             break;
           }
           case 56: {
@@ -387,7 +392,7 @@ namespace Google.Protobuf.WellKnownTypes {
   /// <summary>
   /// Method represents a method of an API interface.
   /// </summary>
-  public sealed partial class Method : pb::IMessage<Method> {
+  public sealed partial class Method : pb::IMessage<Method>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Method> _parser = new pb::MessageParser<Method>(() => new Method());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -657,11 +662,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
@@ -684,7 +694,7 @@ namespace Google.Protobuf.WellKnownTypes {
             break;
           }
           case 50: {
-            options_.AddEntriesFrom(input, _repeated_options_codec);
+            options_.AddEntriesFrom(ref input, _repeated_options_codec);
             break;
           }
           case 56: {
@@ -777,7 +787,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///       ...
   ///     }
   /// </summary>
-  public sealed partial class Mixin : pb::IMessage<Mixin> {
+  public sealed partial class Mixin : pb::IMessage<Mixin>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Mixin> _parser = new pb::MessageParser<Mixin>(() => new Mixin());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -921,11 +931,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();

+ 7 - 2
csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs

@@ -100,7 +100,7 @@ namespace Google.Protobuf.WellKnownTypes {
   /// be expressed in JSON format as "3.000000001s", and 3 seconds and 1
   /// microsecond should be expressed in JSON format as "3.000001s".
   /// </summary>
-  public sealed partial class Duration : pb::IMessage<Duration> {
+  public sealed partial class Duration : pb::IMessage<Duration>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Duration> _parser = new pb::MessageParser<Duration>(() => new Duration());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -250,11 +250,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Seconds = input.ReadInt64();

+ 7 - 2
csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs

@@ -50,7 +50,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `Empty` is empty JSON object `{}`.
   /// </summary>
-  public sealed partial class Empty : pb::IMessage<Empty> {
+  public sealed partial class Empty : pb::IMessage<Empty>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Empty> _parser = new pb::MessageParser<Empty>(() => new Empty());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -139,11 +139,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
         }
       }

+ 8 - 3
csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs

@@ -240,7 +240,7 @@ namespace Google.Protobuf.WellKnownTypes {
   /// request should verify the included field paths, and return an
   /// `INVALID_ARGUMENT` error if any path is unmappable.
   /// </summary>
-  public sealed partial class FieldMask : pb::IMessage<FieldMask> {
+  public sealed partial class FieldMask : pb::IMessage<FieldMask>, pb::IBufferMessage {
     private static readonly pb::MessageParser<FieldMask> _parser = new pb::MessageParser<FieldMask>(() => new FieldMask());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -348,14 +348,19 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            paths_.AddEntriesFrom(input, _repeated_paths_codec);
+            paths_.AddEntriesFrom(ref input, _repeated_paths_codec);
             break;
           }
         }

+ 7 - 2
csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs

@@ -44,7 +44,7 @@ namespace Google.Protobuf.WellKnownTypes {
   /// `SourceContext` represents information about the source of a
   /// protobuf element, like the file in which it is defined.
   /// </summary>
-  public sealed partial class SourceContext : pb::IMessage<SourceContext> {
+  public sealed partial class SourceContext : pb::IMessage<SourceContext>, pb::IBufferMessage {
     private static readonly pb::MessageParser<SourceContext> _parser = new pb::MessageParser<SourceContext>(() => new SourceContext());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -161,11 +161,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             FileName = input.ReadString();

+ 23 - 8
csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs

@@ -77,7 +77,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `Struct` is JSON object.
   /// </summary>
-  public sealed partial class Struct : pb::IMessage<Struct> {
+  public sealed partial class Struct : pb::IMessage<Struct>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Struct> _parser = new pb::MessageParser<Struct>(() => new Struct());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -185,14 +185,19 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            fields_.AddEntriesFrom(input, _map_fields_codec);
+            fields_.AddEntriesFrom(ref input, _map_fields_codec);
             break;
           }
         }
@@ -209,7 +214,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `Value` is JSON value.
   /// </summary>
-  public sealed partial class Value : pb::IMessage<Value> {
+  public sealed partial class Value : pb::IMessage<Value>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Value> _parser = new pb::MessageParser<Value>(() => new Value());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -509,11 +514,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             kind_ = input.ReadEnum();
@@ -561,7 +571,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `ListValue` is JSON array.
   /// </summary>
-  public sealed partial class ListValue : pb::IMessage<ListValue> {
+  public sealed partial class ListValue : pb::IMessage<ListValue>, pb::IBufferMessage {
     private static readonly pb::MessageParser<ListValue> _parser = new pb::MessageParser<ListValue>(() => new ListValue());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -669,14 +679,19 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
-            values_.AddEntriesFrom(input, _repeated_values_codec);
+            values_.AddEntriesFrom(ref input, _repeated_values_codec);
             break;
           }
         }

+ 7 - 2
csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs

@@ -123,7 +123,7 @@ namespace Google.Protobuf.WellKnownTypes {
   /// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
   /// ) to obtain a formatter capable of generating timestamps in this format.
   /// </summary>
-  public sealed partial class Timestamp : pb::IMessage<Timestamp> {
+  public sealed partial class Timestamp : pb::IMessage<Timestamp>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Timestamp> _parser = new pb::MessageParser<Timestamp>(() => new Timestamp());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -271,11 +271,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Seconds = input.ReadInt64();

+ 42 - 17
csharp/src/Google.Protobuf/WellKnownTypes/Type.cs

@@ -94,7 +94,7 @@ namespace Google.Protobuf.WellKnownTypes {
   /// <summary>
   /// A protocol buffer message type.
   /// </summary>
-  public sealed partial class Type : pb::IMessage<Type> {
+  public sealed partial class Type : pb::IMessage<Type>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Type> _parser = new pb::MessageParser<Type>(() => new Type());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -324,26 +324,31 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
             break;
           }
           case 18: {
-            fields_.AddEntriesFrom(input, _repeated_fields_codec);
+            fields_.AddEntriesFrom(ref input, _repeated_fields_codec);
             break;
           }
           case 26: {
-            oneofs_.AddEntriesFrom(input, _repeated_oneofs_codec);
+            oneofs_.AddEntriesFrom(ref input, _repeated_oneofs_codec);
             break;
           }
           case 34: {
-            options_.AddEntriesFrom(input, _repeated_options_codec);
+            options_.AddEntriesFrom(ref input, _repeated_options_codec);
             break;
           }
           case 42: {
@@ -366,7 +371,7 @@ namespace Google.Protobuf.WellKnownTypes {
   /// <summary>
   /// A single field of a message type.
   /// </summary>
-  public sealed partial class Field : pb::IMessage<Field> {
+  public sealed partial class Field : pb::IMessage<Field>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Field> _parser = new pb::MessageParser<Field>(() => new Field());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -719,11 +724,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Kind = (global::Google.Protobuf.WellKnownTypes.Field.Types.Kind) input.ReadEnum();
@@ -754,7 +764,7 @@ namespace Google.Protobuf.WellKnownTypes {
             break;
           }
           case 74: {
-            options_.AddEntriesFrom(input, _repeated_options_codec);
+            options_.AddEntriesFrom(ref input, _repeated_options_codec);
             break;
           }
           case 82: {
@@ -885,7 +895,7 @@ namespace Google.Protobuf.WellKnownTypes {
   /// <summary>
   /// Enum type definition.
   /// </summary>
-  public sealed partial class Enum : pb::IMessage<Enum> {
+  public sealed partial class Enum : pb::IMessage<Enum>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Enum> _parser = new pb::MessageParser<Enum>(() => new Enum());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1096,22 +1106,27 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
             break;
           }
           case 18: {
-            enumvalue_.AddEntriesFrom(input, _repeated_enumvalue_codec);
+            enumvalue_.AddEntriesFrom(ref input, _repeated_enumvalue_codec);
             break;
           }
           case 26: {
-            options_.AddEntriesFrom(input, _repeated_options_codec);
+            options_.AddEntriesFrom(ref input, _repeated_options_codec);
             break;
           }
           case 34: {
@@ -1134,7 +1149,7 @@ namespace Google.Protobuf.WellKnownTypes {
   /// <summary>
   /// Enum value definition.
   /// </summary>
-  public sealed partial class EnumValue : pb::IMessage<EnumValue> {
+  public sealed partial class EnumValue : pb::IMessage<EnumValue>, pb::IBufferMessage {
     private static readonly pb::MessageParser<EnumValue> _parser = new pb::MessageParser<EnumValue>(() => new EnumValue());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1296,11 +1311,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();
@@ -1311,7 +1331,7 @@ namespace Google.Protobuf.WellKnownTypes {
             break;
           }
           case 26: {
-            options_.AddEntriesFrom(input, _repeated_options_codec);
+            options_.AddEntriesFrom(ref input, _repeated_options_codec);
             break;
           }
         }
@@ -1324,7 +1344,7 @@ namespace Google.Protobuf.WellKnownTypes {
   /// A protocol buffer option, which can be attached to a message, field,
   /// enumeration, etc.
   /// </summary>
-  public sealed partial class Option : pb::IMessage<Option> {
+  public sealed partial class Option : pb::IMessage<Option>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Option> _parser = new pb::MessageParser<Option>(() => new Option());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1476,11 +1496,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Name = input.ReadString();

+ 63 - 18
csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs

@@ -57,7 +57,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `DoubleValue` is JSON number.
   /// </summary>
-  public sealed partial class DoubleValue : pb::IMessage<DoubleValue> {
+  public sealed partial class DoubleValue : pb::IMessage<DoubleValue>, pb::IBufferMessage {
     private static readonly pb::MessageParser<DoubleValue> _parser = new pb::MessageParser<DoubleValue>(() => new DoubleValue());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -173,11 +173,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 9: {
             Value = input.ReadDouble();
@@ -194,7 +199,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `FloatValue` is JSON number.
   /// </summary>
-  public sealed partial class FloatValue : pb::IMessage<FloatValue> {
+  public sealed partial class FloatValue : pb::IMessage<FloatValue>, pb::IBufferMessage {
     private static readonly pb::MessageParser<FloatValue> _parser = new pb::MessageParser<FloatValue>(() => new FloatValue());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -310,11 +315,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 13: {
             Value = input.ReadFloat();
@@ -331,7 +341,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `Int64Value` is JSON string.
   /// </summary>
-  public sealed partial class Int64Value : pb::IMessage<Int64Value> {
+  public sealed partial class Int64Value : pb::IMessage<Int64Value>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Int64Value> _parser = new pb::MessageParser<Int64Value>(() => new Int64Value());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -447,11 +457,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Value = input.ReadInt64();
@@ -468,7 +483,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `UInt64Value` is JSON string.
   /// </summary>
-  public sealed partial class UInt64Value : pb::IMessage<UInt64Value> {
+  public sealed partial class UInt64Value : pb::IMessage<UInt64Value>, pb::IBufferMessage {
     private static readonly pb::MessageParser<UInt64Value> _parser = new pb::MessageParser<UInt64Value>(() => new UInt64Value());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -584,11 +599,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Value = input.ReadUInt64();
@@ -605,7 +625,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `Int32Value` is JSON number.
   /// </summary>
-  public sealed partial class Int32Value : pb::IMessage<Int32Value> {
+  public sealed partial class Int32Value : pb::IMessage<Int32Value>, pb::IBufferMessage {
     private static readonly pb::MessageParser<Int32Value> _parser = new pb::MessageParser<Int32Value>(() => new Int32Value());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -721,11 +741,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Value = input.ReadInt32();
@@ -742,7 +767,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `UInt32Value` is JSON number.
   /// </summary>
-  public sealed partial class UInt32Value : pb::IMessage<UInt32Value> {
+  public sealed partial class UInt32Value : pb::IMessage<UInt32Value>, pb::IBufferMessage {
     private static readonly pb::MessageParser<UInt32Value> _parser = new pb::MessageParser<UInt32Value>(() => new UInt32Value());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -858,11 +883,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Value = input.ReadUInt32();
@@ -879,7 +909,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `BoolValue` is JSON `true` and `false`.
   /// </summary>
-  public sealed partial class BoolValue : pb::IMessage<BoolValue> {
+  public sealed partial class BoolValue : pb::IMessage<BoolValue>, pb::IBufferMessage {
     private static readonly pb::MessageParser<BoolValue> _parser = new pb::MessageParser<BoolValue>(() => new BoolValue());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -995,11 +1025,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 8: {
             Value = input.ReadBool();
@@ -1016,7 +1051,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `StringValue` is JSON string.
   /// </summary>
-  public sealed partial class StringValue : pb::IMessage<StringValue> {
+  public sealed partial class StringValue : pb::IMessage<StringValue>, pb::IBufferMessage {
     private static readonly pb::MessageParser<StringValue> _parser = new pb::MessageParser<StringValue>(() => new StringValue());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1132,11 +1167,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Value = input.ReadString();
@@ -1153,7 +1193,7 @@ namespace Google.Protobuf.WellKnownTypes {
   ///
   /// The JSON representation for `BytesValue` is JSON string.
   /// </summary>
-  public sealed partial class BytesValue : pb::IMessage<BytesValue> {
+  public sealed partial class BytesValue : pb::IMessage<BytesValue>, pb::IBufferMessage {
     private static readonly pb::MessageParser<BytesValue> _parser = new pb::MessageParser<BytesValue>(() => new BytesValue());
     private pb::UnknownFieldSet _unknownFields;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1269,11 +1309,16 @@ namespace Google.Protobuf.WellKnownTypes {
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void MergeFrom(pb::CodedInputStream input) {
+      input.ReadRawMessage(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
       uint tag;
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
             break;
           case 10: {
             Value = input.ReadBytes();

+ 1 - 2
src/google/protobuf/compiler/cpp/cpp_unittest.inc

@@ -44,11 +44,10 @@
 // correctly and produces the interfaces we expect, which is why this test
 // is written this way.
 
-#include <google/protobuf/compiler/cpp/cpp_unittest.h>
-
 #include <memory>
 #include <vector>
 
+#include <google/protobuf/compiler/cpp/cpp_unittest.h>
 #include <google/protobuf/unittest_no_arena.pb.h>
 #include <google/protobuf/stubs/strutil.h>
 #if !defined(GOOGLE_PROTOBUF_CMAKE_BUILD) && !defined(_MSC_VER)

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

@@ -96,7 +96,7 @@ void MapFieldGenerator::GenerateMergingCode(io::Printer* printer) {
 void MapFieldGenerator::GenerateParsingCode(io::Printer* printer) {
   printer->Print(
     variables_,
-    "$name$_.AddEntriesFrom(input, _map_$name$_codec);\n");
+    "$name$_.AddEntriesFrom(ref input, _map_$name$_codec);\n");
 }
 
 void MapFieldGenerator::GenerateSerializationCode(io::Printer* printer) {

+ 11 - 3
src/google/protobuf/compiler/csharp/csharp_message.cc

@@ -130,6 +130,7 @@ void MessageGenerator::Generate(io::Printer* printer) {
   else {
     printer->Print(vars, "pb::IMessage<$class_name$>");
   }
+  printer->Print(", pb::IBufferMessage");
   printer->Print(" {\n");
   printer->Indent();
 
@@ -636,6 +637,13 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
   WriteGeneratedCodeAttributes(printer);
   printer->Print("public void MergeFrom(pb::CodedInputStream input) {\n");
   printer->Indent();
+  printer->Print("input.ReadRawMessage(this);\n");
+  printer->Outdent();
+  printer->Print("}\n\n");
+
+  WriteGeneratedCodeAttributes(printer);
+  printer->Print("void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {\n");
+  printer->Indent();
   printer->Print(
     "uint tag;\n"
     "while ((tag = input.ReadTag()) != 0) {\n"
@@ -651,14 +659,14 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
   if (has_extension_ranges_) {
     printer->Print(
       "default:\n"
-      "  if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {\n"
-      "    _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);\n"
+      "  if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {\n"
+      "    _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);\n"
       "  }\n"
       "  break;\n");
   } else {
     printer->Print(
       "default:\n"
-      "  _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);\n"
+      "  _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);\n"
       "  break;\n");
   }
   for (int i = 0; i < fields_by_number().size(); i++) {

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

@@ -80,7 +80,7 @@ void RepeatedEnumFieldGenerator::GenerateMergingCode(io::Printer* printer) {
 void RepeatedEnumFieldGenerator::GenerateParsingCode(io::Printer* printer) {
   printer->Print(
     variables_,
-    "$name$_.AddEntriesFrom(input, _repeated_$name$_codec);\n");
+    "$name$_.AddEntriesFrom(ref input, _repeated_$name$_codec);\n");
 }
 
 void RepeatedEnumFieldGenerator::GenerateSerializationCode(io::Printer* printer) {

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

@@ -95,7 +95,7 @@ void RepeatedMessageFieldGenerator::GenerateMergingCode(io::Printer* printer) {
 void RepeatedMessageFieldGenerator::GenerateParsingCode(io::Printer* printer) {
   printer->Print(
     variables_,
-    "$name$_.AddEntriesFrom(input, _repeated_$name$_codec);\n");
+    "$name$_.AddEntriesFrom(ref input, _repeated_$name$_codec);\n");
 }
 
 void RepeatedMessageFieldGenerator::GenerateSerializationCode(io::Printer* printer) {

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

@@ -80,7 +80,7 @@ void RepeatedPrimitiveFieldGenerator::GenerateMergingCode(io::Printer* printer)
 void RepeatedPrimitiveFieldGenerator::GenerateParsingCode(io::Printer* printer) {
   printer->Print(
     variables_,
-    "$name$_.AddEntriesFrom(input, _repeated_$name$_codec);\n");
+    "$name$_.AddEntriesFrom(ref input, _repeated_$name$_codec);\n");
 }
 
 void RepeatedPrimitiveFieldGenerator::GenerateSerializationCode(io::Printer* printer) {

+ 2 - 2
src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc

@@ -116,7 +116,7 @@ void WrapperFieldGenerator::GenerateMergingCode(io::Printer* printer) {
 void WrapperFieldGenerator::GenerateParsingCode(io::Printer* printer) {
   printer->Print(
     variables_,
-    "$type_name$ value = _single_$name$_codec.Read(input);\n"
+    "$type_name$ value = _single_$name$_codec.Read(ref input);\n"
     "if ($has_not_property_check$ || value != $default_value$) {\n"
     "  $property_name$ = value;\n"
     "}\n");
@@ -250,7 +250,7 @@ void WrapperOneofFieldGenerator::GenerateMergingCode(io::Printer* printer) {
 void WrapperOneofFieldGenerator::GenerateParsingCode(io::Printer* printer) {
   printer->Print(
     variables_,
-    "$property_name$ = _oneof_$name$_codec.Read(input);\n");
+    "$property_name$ = _oneof_$name$_codec.Read(ref input);\n");
 }
 
 void WrapperOneofFieldGenerator::GenerateSerializationCode(io::Printer* printer) {

+ 25 - 25
src/google/protobuf/map_test.cc

@@ -2139,7 +2139,7 @@ TEST(GeneratedMapFieldTest, SerializationToArray) {
   unittest::TestMap message1, message2;
   std::string data;
   MapTestUtil::SetMapFields(&message1);
-  int size = message1.ByteSize();
+  size_t size = message1.ByteSizeLong();
   data.resize(size);
   uint8* start = reinterpret_cast<uint8*>(::google::protobuf::string_as_array(&data));
   uint8* end = message1.SerializeWithCachedSizesToArray(start);
@@ -2152,7 +2152,7 @@ TEST(GeneratedMapFieldTest, SerializationToArray) {
 TEST(GeneratedMapFieldTest, SerializationToStream) {
   unittest::TestMap message1, message2;
   MapTestUtil::SetMapFields(&message1);
-  int size = message1.ByteSize();
+  size_t size = message1.ByteSizeLong();
   std::string data;
   data.resize(size);
   {
@@ -2369,7 +2369,7 @@ TEST(GeneratedMapFieldTest, MissedValueTextFormat) {
 
   EXPECT_TRUE(TextFormat::ParseFromString(text, &message));
   EXPECT_EQ(1, message.map_int32_foreign_message().size());
-  EXPECT_EQ(11, message.ByteSize());
+  EXPECT_EQ(11, message.ByteSizeLong());
 }
 
 TEST(GeneratedMapFieldTest, UnknownFieldWireFormat) {
@@ -2481,7 +2481,7 @@ TEST(GeneratedMapFieldReflectionTest, SpaceUsed) {
   MapReflectionTester reflection_tester(unittest::TestMap::descriptor());
   reflection_tester.SetMapFieldsViaReflection(&message);
 
-  EXPECT_LT(0, message.GetReflection()->SpaceUsed(message));
+  EXPECT_LT(0, message.GetReflection()->SpaceUsedLong(message));
 }
 
 TEST(GeneratedMapFieldReflectionTest, Accessors) {
@@ -2767,7 +2767,7 @@ TEST_F(MapFieldInDynamicMessageTest, DynamicMapReflection) {
 }
 
 TEST_F(MapFieldInDynamicMessageTest, MapSpaceUsed) {
-  // Test that SpaceUsed() works properly
+  // Test that SpaceUsedLong() works properly
 
   // Since we share the implementation with generated messages, we don't need
   // to test very much here.  Just make sure it appears to be working.
@@ -2775,10 +2775,10 @@ TEST_F(MapFieldInDynamicMessageTest, MapSpaceUsed) {
   std::unique_ptr<Message> message(map_prototype_->New());
   MapReflectionTester reflection_tester(map_descriptor_);
 
-  int initial_space_used = message->SpaceUsed();
+  int initial_space_used = message->SpaceUsedLong();
 
   reflection_tester.SetMapFieldsViaReflection(message.get());
-  EXPECT_LT(initial_space_used, message->SpaceUsed());
+  EXPECT_LT(initial_space_used, message->SpaceUsedLong());
 }
 
 TEST_F(MapFieldInDynamicMessageTest, RecursiveMap) {
@@ -2952,9 +2952,9 @@ TEST(WireFormatForMapFieldTest, MapByteSize) {
   unittest::TestMap message;
   MapTestUtil::SetMapFields(&message);
 
-  EXPECT_EQ(message.ByteSize(), WireFormat::ByteSize(message));
+  EXPECT_EQ(message.ByteSizeLong(), WireFormat::ByteSize(message));
   message.Clear();
-  EXPECT_EQ(0, message.ByteSize());
+  EXPECT_EQ(0, message.ByteSizeLong());
   EXPECT_EQ(0, WireFormat::ByteSize(message));
 }
 
@@ -2967,7 +2967,7 @@ TEST(WireFormatForMapFieldTest, SerializeMap) {
 
   // Serialize using the generated code.
   {
-    message.ByteSize();
+    message.ByteSizeLong();
     io::StringOutputStream raw_output(&generated_data);
     io::CodedOutputStream output(&raw_output);
     message.SerializeWithCachedSizes(&output);
@@ -2978,7 +2978,7 @@ TEST(WireFormatForMapFieldTest, SerializeMap) {
   {
     io::StringOutputStream raw_output(&dynamic_data);
     io::CodedOutputStream output(&raw_output);
-    int size = WireFormat::ByteSize(message);
+    size_t size = WireFormat::ByteSize(message);
     WireFormat::SerializeWithCachedSizes(message, size, &output);
     ASSERT_FALSE(output.HadError());
   }
@@ -3026,7 +3026,7 @@ TEST(WireFormatForMapFieldTest, MapByteSizeDynamicMessage) {
   std::string expected_serialized_data;
   dynamic_message->SerializeToString(&expected_serialized_data);
   int expected_size = expected_serialized_data.size();
-  EXPECT_EQ(dynamic_message->ByteSize(), expected_size);
+  EXPECT_EQ(dynamic_message->ByteSizeLong(), expected_size);
 
   std::unique_ptr<Message> message2;
   message2.reset(factory.GetPrototype(unittest::TestMap::descriptor())->New());
@@ -3040,27 +3040,27 @@ TEST(WireFormatForMapFieldTest, MapByteSizeDynamicMessage) {
   reflection->RemoveLast(dynamic_message.get(), field);
   dynamic_message->MergeFrom(*message2);
   dynamic_message->MergeFrom(*message2);
-  // The map field is marked as STATE_MODIFIED_REPEATED, ByteSize() will use
+  // The map field is marked as STATE_MODIFIED_REPEATED, ByteSizeLong() will use
   // repeated field which have duplicate keys to calculate.
-  int duplicate_size = dynamic_message->ByteSize();
+  size_t duplicate_size = dynamic_message->ByteSizeLong();
   EXPECT_TRUE(duplicate_size > expected_size);
   std::string duplicate_serialized_data;
   dynamic_message->SerializeToString(&duplicate_serialized_data);
-  EXPECT_EQ(dynamic_message->ByteSize(), duplicate_serialized_data.size());
+  EXPECT_EQ(dynamic_message->ByteSizeLong(), duplicate_serialized_data.size());
 
   // Force the map field to mark with map CLEAN
   EXPECT_EQ(reflection_tester.MapSize(*dynamic_message, "map_int32_int32"), 2);
-  // The map field is marked as CLEAN, ByteSize() will use map which do not
+  // The map field is marked as CLEAN, ByteSizeLong() will use map which do not
   // have duplicate keys to calculate.
-  int size = dynamic_message->ByteSize();
+  int size = dynamic_message->ByteSizeLong();
   EXPECT_EQ(expected_size, size);
 
   // Protobuf used to have a bug for serialize when map it marked CLEAN. It used
-  // repeated field to calculate ByteSize but use map to serialize the real
-  // data, thus the ByteSize may bigger than real serialized size. A crash might
-  // be happen at SerializeToString(). Or an "unexpect end group" warning was
-  // raised at parse back if user use SerializeWithCachedSizes() which avoids
-  // size check at serialize.
+  // repeated field to calculate ByteSizeLong but use map to serialize the real
+  // data, thus the ByteSizeLong may bigger than real serialized size. A crash
+  // might be happen at SerializeToString(). Or an "unexpect end group" warning
+  // was raised at parse back if user use SerializeWithCachedSizes() which
+  // avoids size check at serialize.
   std::string serialized_data;
   dynamic_message->SerializeToString(&serialized_data);
   EXPECT_EQ(serialized_data, expected_serialized_data);
@@ -3118,7 +3118,7 @@ TEST(WireFormatForMapFieldTest, MapParseHelpers) {
 template <typename T>
 static std::string DeterministicSerializationWithSerializePartialToCodedStream(
     const T& t) {
-  const int size = t.ByteSize();
+  const size_t size = t.ByteSizeLong();
   std::string result(size, '\0');
   io::ArrayOutputStream array_stream(::google::protobuf::string_as_array(&result), size);
   io::CodedOutputStream output_stream(&array_stream);
@@ -3132,7 +3132,7 @@ static std::string DeterministicSerializationWithSerializePartialToCodedStream(
 template <typename T>
 static std::string DeterministicSerializationWithSerializeToCodedStream(
     const T& t) {
-  const int size = t.ByteSize();
+  const size_t size = t.ByteSizeLong();
   std::string result(size, '\0');
   io::ArrayOutputStream array_stream(::google::protobuf::string_as_array(&result), size);
   io::CodedOutputStream output_stream(&array_stream);
@@ -3145,7 +3145,7 @@ static std::string DeterministicSerializationWithSerializeToCodedStream(
 
 template <typename T>
 static std::string DeterministicSerialization(const T& t) {
-  const int size = t.ByteSize();
+  const size_t size = t.ByteSizeLong();
   std::string result(size, '\0');
   io::ArrayOutputStream array_stream(::google::protobuf::string_as_array(&result), size);
   {

+ 20 - 0
src/google/protobuf/port_def.inc

@@ -547,14 +547,26 @@ PROTOBUF_EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport));
 // Windows declares several inconvenient macro names.  We #undef them and then
 // restore them in port_undef.inc.
 #ifdef _MSC_VER
+#pragma push_macro("CREATE_NEW")
+#undef CREATE_NEW
+#pragma push_macro("DOUBLE_CLICK")
+#undef DOUBLE_CLICK
 #pragma push_macro("ERROR")
 #undef ERROR
+#pragma push_macro("ERROR_BUSY")
+#undef ERROR_BUSY
+#pragma push_macro("ERROR_NOT_FOUND")
+#undef ERROR_NOT_FOUND
 #pragma push_macro("GetMessage")
 #undef GetMessage
 #pragma push_macro("IGNORE")
 #undef IGNORE
 #pragma push_macro("IN")
 #undef IN
+#pragma push_macro("INPUT_KEYBOARD")
+#undef INPUT_KEYBOARD
+#pragma push_macro("NO_ERROR")
+#undef NO_ERROR
 #pragma push_macro("OUT")
 #undef OUT
 #pragma push_macro("OPTIONAL")
@@ -563,6 +575,14 @@ PROTOBUF_EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport));
 #undef min
 #pragma push_macro("max")
 #undef max
+#pragma push_macro("REASON_UNKNOWN")
+#undef REASON_UNKNOWN
+#pragma push_macro("SERVICE_DISABLED")
+#undef SERVICE_DISABLED
+#pragma push_macro("SEVERITY_ERROR")
+#undef SEVERITY_ERROR
+#pragma push_macro("STRICT")
+#undef STRICT
 #endif  // _MSC_VER
 
 #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER)

+ 10 - 0
src/google/protobuf/port_undef.inc

@@ -87,14 +87,24 @@
 
 // Restore macro that may have been #undef'd in port_def.inc.
 #ifdef _MSC_VER
+#pragma pop_macro("CREATE_NEW")
+#pragma pop_macro("DOUBLE_CLICK")
 #pragma pop_macro("ERROR")
+#pragma pop_macro("ERROR_BUSY")
+#pragma pop_macro("ERROR_NOT_FOUND")
 #pragma pop_macro("GetMessage")
 #pragma pop_macro("IGNORE")
 #pragma pop_macro("IN")
+#pragma pop_macro("INPUT_KEYBOARD")
 #pragma pop_macro("OUT")
 #pragma pop_macro("OPTIONAL")
 #pragma pop_macro("min")
 #pragma pop_macro("max")
+#pragma pop_macro("NO_ERROR")
+#pragma pop_macro("REASON_UNKNOWN")
+#pragma pop_macro("SERVICE_DISABLED")
+#pragma pop_macro("SEVERITY_ERROR")
+#pragma pop_macro("STRICT")
 #endif
 
 #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER)

+ 3 - 2
src/google/protobuf/util/field_mask_util.h

@@ -63,8 +63,9 @@ class PROTOBUF_EXPORT FieldMaskUtil {
     for (const auto field_number : field_numbers) {
       const FieldDescriptor* field_desc =
           T::descriptor()->FindFieldByNumber(field_number);
-      GOOGLE_CHECK(field_desc != nullptr) << "Invalid field number for "
-                                   << typeid(T).name() << ": " << field_number;
+      GOOGLE_CHECK(field_desc != nullptr)
+          << "Invalid field number for " << T::descriptor()->full_name() << ": "
+          << field_number;
       AddPathToFieldMask<T>(field_desc->lowercase_name(), out);
     }
   }

+ 1 - 0
src/google/protobuf/util/internal/default_value_objectwriter_test.cc

@@ -29,6 +29,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <google/protobuf/util/internal/default_value_objectwriter.h>
+
 #include <google/protobuf/util/internal/expecting_objectwriter.h>
 #include <google/protobuf/util/internal/testdata/default_value_test.pb.h>
 #include <google/protobuf/util/internal/type_info_test_helper.h>

+ 1 - 1
src/google/protobuf/util/internal/protostream_objectwriter_test.cc

@@ -172,7 +172,7 @@ class BaseProtoStreamObjectWriterTest
 MATCHER_P(HasObjectLocation, expected,
           "Verifies the expected object location") {
   std::string actual = get<0>(arg).ToString();
-  if (actual.compare(expected) == 0) return true;
+  if (actual == expected) return true;
   *result_listener << "actual location is: " << actual;
   return false;
 }

部分文件因为文件数量过多而无法显示