Forráskód Böngészése

make everything build

Jan Tattermusch 5 éve
szülő
commit
ea605381e6

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

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

@@ -202,29 +202,29 @@ namespace Google.Protobuf
         [Test]
         [Test]
         public void DecodeZigZag32()
         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]
         [Test]
         public void DecodeZigZag64()
         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]
         [Test]
@@ -339,64 +339,64 @@ namespace Google.Protobuf
             Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(input));
             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]
         [Test]
         public void MaliciousRecursion_UnknownFields()
         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));
             Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(payloadBeyondRecursiveLimit));
         }
         }
 
 
         [Test]
         [Test]
         public void ReadGroup_WrongEndGroupTag()
         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]
         [Test]
         public void ReadGroup_UnknownFields_WrongEndGroupTag()
         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]
         [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)
             // Some easier-to-verify round-trip tests.  The inputs (other than 0, 1, -1)
             // were chosen semi-randomly via keyboard bashing.
             // 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]
         [Test]
         public void RoundTripZigZag64()
         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,
             Assert.AreEqual(856912304801416L,
-                            CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(856912304801416L)));
+                            ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(856912304801416L)));
             Assert.AreEqual(-75123905439571256L,
             Assert.AreEqual(-75123905439571256L,
-                            CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-75123905439571256L)));
+                            ParsingPrimitives.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-75123905439571256L)));
         }
         }
 
 
         [Test]
         [Test]

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

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

+ 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;
             int capacity = stream.CanSeek ? checked((int) (stream.Length - stream.Position)) : 0;
             var memoryStream = new MemoryStream(capacity);
             var memoryStream = new MemoryStream(capacity);
             stream.CopyTo(memoryStream);
             stream.CopyTo(memoryStream);
-#if NETSTANDARD1_0 || NETSTANDARD2_0
+#if NETSTANDARD1_1 || NETSTANDARD2_0
             byte[] bytes = memoryStream.ToArray();
             byte[] bytes = memoryStream.ToArray();
 #else
 #else
             // Avoid an extra copy if we can.
             // 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
             // 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.
             // alone. But it's documented to use 81920 by default if not specified.
             await stream.CopyToAsync(memoryStream, 81920, cancellationToken);
             await stream.CopyToAsync(memoryStream, 81920, cancellationToken);
-#if NETSTANDARD1_0 || NETSTANDARD2_0
+#if NETSTANDARD1_1 || NETSTANDARD2_0
             byte[] bytes = memoryStream.ToArray();
             byte[] bytes = memoryStream.ToArray();
 #else
 #else
             // Avoid an extra copy if we can.
             // Avoid an extra copy if we can.

+ 2 - 3
csharp/src/Google.Protobuf/CodedInputStream.cs

@@ -431,15 +431,14 @@ namespace Google.Protobuf
         public void ReadMessage(IMessage builder)
         public void ReadMessage(IMessage builder)
         {
         {
             var span = new ReadOnlySpan<byte>(buffer);
             var span = new ReadOnlySpan<byte>(buffer);
-            var ctx = new CodedInputReader(ref span, ref state);
+            var ctx = new ParseContext(ref span, ref state);
             try
             try
             {
             {
                 ParsingPrimitivesMessages.ReadMessage(ref ctx, builder);
                 ParsingPrimitivesMessages.ReadMessage(ref ctx, builder);
             }
             }
             finally
             finally
             {
             {
-                // store the state
-                state = ctx.state;
+                ctx.CopyStateTo(this);
             }
             }
         }
         }
 
 

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

@@ -94,23 +94,41 @@ namespace Google.Protobuf.Collections
         /// <param name="input">The input stream to read from.</param>
         /// <param name="input">The input stream to read from.</param>
         /// <param name="codec">The codec to use in order to read each entry.</param>
         /// <param name="codec">The codec to use in order to read each entry.</param>
         public void AddEntriesFrom(CodedInputStream input, FieldCodec<T> codec)
         public void AddEntriesFrom(CodedInputStream input, FieldCodec<T> codec)
+        {
+            var ctx = new ParseContext(input);
+            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>
+        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
             // TODO: Inline some of the Add code, so we can avoid checking the size on every
             // iteration.
             // iteration.
-            uint tag = input.LastTag;
+            uint tag = ctx.state.lastTag;
             var reader = codec.ValueReader;
             var reader = codec.ValueReader;
             // Non-nullable value types can be packed or not.
             // Non-nullable value types can be packed or not.
             if (FieldCodec<T>.IsPackedRepeatedField(tag))
             if (FieldCodec<T>.IsPackedRepeatedField(tag))
             {
             {
-                int length = input.ReadLength();
+                int length = ctx.ReadLength();
                 if (length > 0)
                 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.
                 // Empty packed field. Odd, but valid - just ignore.
             }
             }
@@ -119,8 +137,8 @@ namespace Google.Protobuf.Collections
                 // Not packed... (possibly not packable)
                 // Not packed... (possibly not packable)
                 do
                 do
                 {
                 {
-                    Add(reader(input));
-                } while (input.MaybeConsumeTag(tag));
+                    Add(reader(ref ctx));
+                } while (ParsingPrimitives.MaybeConsumeTag(ref ctx.buffer, ref ctx.state, tag));
             }
             }
         }
         }
 
 

+ 35 - 22
csharp/src/Google.Protobuf/ExtensionValue.cs

@@ -93,14 +93,27 @@ namespace Google.Protobuf
 
 
         public void MergeFrom(CodedInputStream input)
         public void MergeFrom(CodedInputStream input)
         {
         {
-            codec.ValueMerger(input, ref field);
+            var ctx = new ParseContext(input);
+            try
+            {
+                codec.ValueMerger(ref ctx, ref field);
+            }
+            finally
+            {
+                ctx.CopyStateTo(input);
+            }
+        }
+
+        public void MergeFrom(ref ParseContext ctx)
+        {
+            codec.ValueMerger(ref ctx, ref field);
         }
         }
 
 
         public void MergeFrom(IExtensionValue value)
         public void MergeFrom(IExtensionValue value)
         {
         {
             if (value is ExtensionValue<T>)
             if (value is ExtensionValue<T>)
             {
             {
-                var extensionValue = value as ExtensionValue<T>;
+                var extensionValue = value as ExtensionValue<T>;
                 codec.FieldMerger(ref field, extensionValue.field);
                 codec.FieldMerger(ref field, extensionValue.field);
             }
             }
         }
         }
@@ -124,13 +137,13 @@ namespace Google.Protobuf
 
 
         public bool IsInitialized()
         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;
             }
             }
         }
         }
     }
     }
@@ -202,20 +215,20 @@ namespace Google.Protobuf
 
 
         public bool IsInitialized()
         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;
             return true;

+ 96 - 48
csharp/src/Google.Protobuf/FieldCodec.cs

@@ -218,7 +218,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<string> ForString(uint tag, string defaultValue)
         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>
         /// <summary>
@@ -229,7 +229,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<ByteString> ForBytes(uint tag, ByteString defaultValue)
         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>
         /// <summary>
@@ -240,7 +240,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<bool> ForBool(uint tag, bool defaultValue)
         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>
         /// <summary>
@@ -251,7 +251,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<int> ForInt32(uint tag, int defaultValue)
         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>
         /// <summary>
@@ -262,7 +262,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<int> ForSInt32(uint tag, int defaultValue)
         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>
         /// <summary>
@@ -273,7 +273,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<uint> ForFixed32(uint tag, uint defaultValue)
         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>
         /// <summary>
@@ -284,7 +284,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<int> ForSFixed32(uint tag, int defaultValue)
         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>
         /// <summary>
@@ -295,7 +295,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<uint> ForUInt32(uint tag, uint defaultValue)
         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>
         /// <summary>
@@ -306,7 +306,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<long> ForInt64(uint tag, long defaultValue)
         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>
         /// <summary>
@@ -317,7 +317,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<long> ForSInt64(uint tag, long defaultValue)
         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>
         /// <summary>
@@ -328,7 +328,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<ulong> ForFixed64(uint tag, ulong defaultValue)
         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>
         /// <summary>
@@ -339,7 +339,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<long> ForSFixed64(uint tag, long defaultValue)
         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>
         /// <summary>
@@ -350,7 +350,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<ulong> ForUInt64(uint tag, ulong defaultValue)
         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>
         /// <summary>
@@ -361,7 +361,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<float> ForFloat(uint tag, float defaultValue)
         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>
         /// <summary>
@@ -372,7 +372,7 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <returns>A codec for the given tag.</returns>
         public static FieldCodec<double> ForDouble(uint tag, double defaultValue)
         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,
         // Enums are tricky. We can probably use expression trees to build these delegates automatically,
@@ -388,8 +388,8 @@ namespace Google.Protobuf
         /// <returns>A codec for the given tag.</returns>
         /// <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)
         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)),
                 (output, value) => output.WriteEnum(toInt32(value)),
                 value => CodedOutputStream.ComputeEnumSize(toInt32(value)), tag, defaultValue);
                 value => CodedOutputStream.ComputeEnumSize(toInt32(value)), tag, defaultValue);
         }
         }
@@ -403,21 +403,21 @@ namespace Google.Protobuf
         public static FieldCodec<T> ForMessage<T>(uint tag, MessageParser<T> parser) where T : class, IMessage<T>
         public static FieldCodec<T> ForMessage<T>(uint tag, MessageParser<T> parser) where T : class, IMessage<T>
         {
         {
             return new FieldCodec<T>(
             return new FieldCodec<T>(
-                input => 
+                (ref ParseContext ctx) => 
                 { 
                 { 
                     T message = parser.CreateTemplate(); 
                     T message = parser.CreateTemplate(); 
-                    input.ReadMessage(message); 
+                    ctx.ReadMessage(message); 
                     return message; 
                     return message; 
                 },
                 },
                 (output, value) => output.WriteMessage(value),
                 (output, value) => output.WriteMessage(value),
-                (CodedInputStream i, ref T v) => 
+                (ref ParseContext ctx, ref T v) => 
                 {
                 {
                     if (v == null)
                     if (v == null)
                     {
                     {
                         v = parser.CreateTemplate();
                         v = parser.CreateTemplate();
                     }
                     }
 
 
-                    i.ReadMessage(v);
+                    ctx.ReadMessage(v);
                 },
                 },
                 (ref T v, T v2) =>
                 (ref T v, T v2) =>
                 {
                 {
@@ -448,21 +448,21 @@ namespace Google.Protobuf
         public static FieldCodec<T> ForGroup<T>(uint startTag, uint endTag, MessageParser<T> parser) where T : class, IMessage<T>
         public static FieldCodec<T> ForGroup<T>(uint startTag, uint endTag, MessageParser<T> parser) where T : class, IMessage<T>
         {
         {
             return new FieldCodec<T>(
             return new FieldCodec<T>(
-                input => 
+                (ref ParseContext ctx) => 
                 { 
                 { 
                     T message = parser.CreateTemplate();
                     T message = parser.CreateTemplate();
-                    input.ReadGroup(message);
+                    ctx.ReadGroup(message);
                     return message;
                     return message;
                 },
                 },
                 (output, value) => output.WriteGroup(value), 
                 (output, value) => output.WriteGroup(value), 
-                (CodedInputStream i, ref T v) => 
+                (ref ParseContext ctx, ref T v) => 
                 {
                 {
                     if (v == null)
                     if (v == null)
                     {
                     {
                         v = parser.CreateTemplate();
                         v = parser.CreateTemplate();
                     }
                     }
 
 
-                    i.ReadGroup(v);
+                    ctx.ReadGroup(v);
                 },
                 },
                 (ref T v, T v2) =>
                 (ref T v, T v2) =>
                 {
                 {
@@ -490,9 +490,9 @@ namespace Google.Protobuf
         {
         {
             var nestedCodec = WrapperCodecs.GetCodec<T>();
             var nestedCodec = WrapperCodecs.GetCodec<T>();
             return new FieldCodec<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),
                 (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; },
                 (ref T v, T v2) => { v = v2; return v == null; },
                 value => WrapperCodecs.CalculateSize<T>(value, nestedCodec),
                 value => WrapperCodecs.CalculateSize<T>(value, nestedCodec),
                 tag, 0,
                 tag, 0,
@@ -509,7 +509,7 @@ namespace Google.Protobuf
             return new FieldCodec<T?>(
             return new FieldCodec<T?>(
                 WrapperCodecs.GetReader<T>(),
                 WrapperCodecs.GetReader<T>(),
                 (output, value) => WrapperCodecs.Write<T>(output, value.Value, nestedCodec),
                 (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; },
                 (ref T? v, T? v2) => { if (v2.HasValue) { v = v2; } return v.HasValue; },
                 value => value == null ? 0 : WrapperCodecs.CalculateSize<T>(value.Value, nestedCodec),
                 value => value == null ? 0 : WrapperCodecs.CalculateSize<T>(value.Value, nestedCodec),
                 tag, 0,
                 tag, 0,
@@ -542,17 +542,17 @@ namespace Google.Protobuf
             private static readonly Dictionary<System.Type, object> Readers = new Dictionary<System.Type, object>
             private static readonly Dictionary<System.Type, object> Readers = new Dictionary<System.Type, object>
             {
             {
                 // TODO: Provide more optimized readers.
                 // 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 ?
                 { 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 ?
                 { 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.
                 // `string` and `ByteString` less performance-sensitive. Do not implement for now.
                 { typeof(string), null },
                 { typeof(string), null },
                 { typeof(ByteString), null },
                 { typeof(ByteString), null },
@@ -572,7 +572,7 @@ namespace Google.Protobuf
                 return (FieldCodec<T>) value;
                 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;
                 object value;
                 if (!Readers.TryGetValue(typeof(T), out value))
                 if (!Readers.TryGetValue(typeof(T), out value))
@@ -583,10 +583,10 @@ namespace Google.Protobuf
                 {
                 {
                     // Return default unoptimized reader for the wrapper type.
                     // Return default unoptimized reader for the wrapper type.
                     var nestedCoded = GetCodec<T>();
                     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 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)
             internal static T Read<T>(CodedInputStream input, FieldCodec<T> codec)
@@ -614,6 +614,31 @@ namespace Google.Protobuf
                 return value;
                 return value;
             }
             }
 
 
+            internal static T Read<T>(ref ParseContext ctx, FieldCodec<T> codec)
+            {
+                int length = ctx.ReadLength();
+                int oldLimit = SegmentedBufferHelper.PushLimit(ref ctx.state, length);
+
+                uint tag;
+                T value = codec.DefaultValue;
+                while ((tag = ctx.ReadTag()) != 0)
+                {
+                    if (tag == codec.Tag)
+                    {
+                        value = codec.Read(ref ctx);
+                    }
+                    else
+                    {
+                        ParsingPrimitivesMessages.SkipLastField(ref ctx.buffer, ref ctx.state);
+                    }
+
+                }
+                ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref ctx.state);
+                SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit);
+
+                return value;
+            }
+
             internal static void Write<T>(CodedOutputStream output, T value, FieldCodec<T> codec)
             internal static void Write<T>(CodedOutputStream output, T value, FieldCodec<T> codec)
             {
             {
                 output.WriteLength(codec.CalculateSizeWithTag(value));
                 output.WriteLength(codec.CalculateSizeWithTag(value));
@@ -628,6 +653,8 @@ namespace Google.Protobuf
         }
         }
     }
     }
 
 
+    internal delegate TValue ValueReader<out TValue>(ref ParseContext ctx);
+
     /// <summary>
     /// <summary>
     /// <para>
     /// <para>
     /// An encode/decode pair for a single field. This effectively encapsulates
     /// An encode/decode pair for a single field. This effectively encapsulates
@@ -653,7 +680,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Merges an input stream into a value
         /// Merges an input stream into a value
         /// </summary>
         /// </summary>
-        internal delegate void InputMerger(CodedInputStream input, ref T value);
+        internal delegate void InputMerger(ref ParseContext ctx, ref T value);
 
 
         /// <summary>
         /// <summary>
         /// Merges a value into a reference to another value, returning a boolean if the value was set
         /// Merges a value into a reference to another value, returning a boolean if the value was set
@@ -692,7 +719,7 @@ namespace Google.Protobuf
         /// Returns a delegate to read a value from a coded input stream. It is assumed that
         /// 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.
         /// the stream is already positioned on the appropriate tag.
         /// </summary>
         /// </summary>
-        internal Func<CodedInputStream, T> ValueReader { get; }
+        internal ValueReader<T> ValueReader { get; }
 
 
         /// <summary>
         /// <summary>
         /// Returns a delegate to merge a value from a coded input stream.
         /// Returns a delegate to merge a value from a coded input stream.
@@ -739,7 +766,7 @@ namespace Google.Protobuf
         private readonly int tagSize;
         private readonly int tagSize;
 
 
         internal FieldCodec(
         internal FieldCodec(
-                Func<CodedInputStream, T> reader,
+                ValueReader<T> reader,
                 Action<CodedOutputStream, T> writer,
                 Action<CodedOutputStream, T> writer,
                 int fixedSize,
                 int fixedSize,
                 uint tag,
                 uint tag,
@@ -749,16 +776,16 @@ namespace Google.Protobuf
         }
         }
 
 
         internal FieldCodec(
         internal FieldCodec(
-            Func<CodedInputStream, T> reader,
+            ValueReader<T> reader,
             Action<CodedOutputStream, T> writer,
             Action<CodedOutputStream, T> writer,
             Func<T, int> sizeCalculator,
             Func<T, int> sizeCalculator,
             uint tag,
             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(
         internal FieldCodec(
-            Func<CodedInputStream, T> reader,
+            ValueReader<T> reader,
             Action<CodedOutputStream, T> writer,
             Action<CodedOutputStream, T> writer,
             InputMerger inputMerger,
             InputMerger inputMerger,
             ValuesMerger valuesMerger,
             ValuesMerger valuesMerger,
@@ -769,7 +796,7 @@ namespace Google.Protobuf
         }
         }
 
 
         internal FieldCodec(
         internal FieldCodec(
-            Func<CodedInputStream, T> reader,
+            ValueReader<T> reader,
             Action<CodedOutputStream, T> writer,
             Action<CodedOutputStream, T> writer,
             InputMerger inputMerger,
             InputMerger inputMerger,
             ValuesMerger valuesMerger,
             ValuesMerger valuesMerger,
@@ -815,7 +842,28 @@ namespace Google.Protobuf
         /// </summary>
         /// </summary>
         /// <param name="input">The input stream to read from.</param>
         /// <param name="input">The input stream to read from.</param>
         /// <returns>The value read from the stream.</returns>
         /// <returns>The value read from the stream.</returns>
-        public T Read(CodedInputStream input) => ValueReader(input);
+        public T Read(CodedInputStream input)
+        {
+            var ctx = new ParseContext(input);
+            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>
         /// <summary>
         /// Calculates the size required to write the given value, with a tag,
         /// 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>
   <PropertyGroup>
     <Description>C# runtime library for Protocol Buffers - Google's data interchange format.</Description>
     <Description>C# runtime library for Protocol Buffers - Google's data interchange format.</Description>
     <Copyright>Copyright 2015, Google Inc.</Copyright>
     <Copyright>Copyright 2015, Google Inc.</Copyright>
     <AssemblyTitle>Google Protocol Buffers</AssemblyTitle>
     <AssemblyTitle>Google Protocol Buffers</AssemblyTitle>
     <VersionPrefix>3.11.4</VersionPrefix>
     <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>
     <Authors>Google Inc.</Authors>
-    <TargetFrameworks>netstandard1.0;netstandard2.0;net45</TargetFrameworks>
+    <TargetFrameworks>netstandard1.1;netstandard2.0;net45</TargetFrameworks>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
     <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
     <SignAssembly>true</SignAssembly>
@@ -18,25 +19,26 @@
     <PackageLicenseUrl>https://github.com/protocolbuffers/protobuf/blob/master/LICENSE</PackageLicenseUrl>
     <PackageLicenseUrl>https://github.com/protocolbuffers/protobuf/blob/master/LICENSE</PackageLicenseUrl>
     <RepositoryType>git</RepositoryType>
     <RepositoryType>git</RepositoryType>
     <RepositoryUrl>https://github.com/protocolbuffers/protobuf.git</RepositoryUrl>
     <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 -->
     <!-- Include PDB in the built .nupkg -->
     <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
     <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
   </PropertyGroup>
   </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>
   </PropertyGroup>
 
 
-  <!-- Needed for the net45 build to work on Unix. See https://github.com/dotnet/designs/pull/33 -->
   <ItemGroup>
   <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="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>
 
 
-  <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>
   </ItemGroup>
 
 
 </Project>
 </Project>

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

@@ -35,29 +35,15 @@ namespace Google.Protobuf
 #if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY
 #if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY
     /// <summary>
     /// <summary>
     /// Interface for a Protocol Buffers message, supporting
     /// Interface for a Protocol Buffers message, supporting
-    /// <see cref="CodedInputReader"/> and <see cref="CodedOutputWriter"/>
-    /// serialization operations.
+    /// parsing from <see cref="ParseContext"/>.
     /// </summary>
     /// </summary>
     public interface IBufferMessage : IMessage
     public interface IBufferMessage : IMessage
     {
     {
-        /// <summary>
-        /// Merges the data from the specified <see cref="CodedInputReader"/> with the current message.
-        /// </summary>
-        /// <remarks>See the user guide for precise merge semantics.</remarks>
-        /// <param name="input"><see cref="CodedInputReader"/> to read data from. Must not be null.</param>
-        void MergeFrom(ref CodedInputReader input);
-
         /// <summary>
         /// <summary>
         /// Internal implementation of merging data from given parse context into this message.
         /// Internal implementation of merging data from given parse context into this message.
         /// Users should never invoke this method directly.
         /// Users should never invoke this method directly.
         /// </summary>        
         /// </summary>        
         void MergeFrom_Internal(ref ParseContext ctx);
         void MergeFrom_Internal(ref ParseContext ctx);
-
-        /// <summary>
-        /// Writes the data to the given <see cref="CodedOutputWriter"/>.
-        /// </summary>
-        /// <param name="output"><see cref="CodedOutputWriter"/> to write the data to. Must not be null.</param>
-        void WriteTo(ref CodedOutputWriter output);
     }
     }
 #endif
 #endif
 }
 }

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

@@ -318,5 +318,10 @@ namespace Google.Protobuf
         {
         {
             input.InternalState = state;
             input.InternalState = state;
         }
         }
+
+        internal void LoadStateFrom(CodedInputStream input)
+        {
+            state = input.InternalState;
+        }
     }
     }
 }
 }

+ 41 - 45
csharp/src/Google.Protobuf/ParsingPrimitivesMessages.cs

@@ -110,28 +110,6 @@ namespace Google.Protobuf
             state.recursionDepth--;
             state.recursionDepth--;
         }
         }
 
 
-        public static void ReadMessage(ref CodedInputReader 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 ReadMessage(ref ParseContext ctx, IMessage message)
         public static void ReadMessage(ref ParseContext ctx, IMessage message)
         {
         {
             int length = ParsingPrimitives.ParseLength(ref ctx.buffer, ref ctx.state);
             int length = ParsingPrimitives.ParseLength(ref ctx.buffer, ref ctx.state);
@@ -154,7 +132,7 @@ namespace Google.Protobuf
             SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit);
             SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit);
         }
         }
 
 
-        public static void ReadGroup(ref CodedInputReader ctx, IMessage message)
+        public static void ReadGroup(ref ParseContext ctx, IMessage message)
         {
         {
             if (ctx.state.recursionDepth >= ctx.state.recursionLimit)
             if (ctx.state.recursionDepth >= ctx.state.recursionLimit)
             {
             {
@@ -162,55 +140,66 @@ namespace Google.Protobuf
             }
             }
             ++ctx.state.recursionDepth;
             ++ctx.state.recursionDepth;
             
             
+            uint tag = ctx.state.lastTag;
+            int fieldNumber = WireFormat.GetTagFieldNumber(tag);
             ReadRawMessage(ref ctx, message);
             ReadRawMessage(ref ctx, message);
+            CheckLastTagWas(ref ctx.state, WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
 
 
             --ctx.state.recursionDepth;
             --ctx.state.recursionDepth;
         }
         }
 
 
-        public static void ReadGroup(ref ParseContext ctx, IMessage message)
+        public static void ReadGroup(ref ParseContext ctx, int fieldNumber, UnknownFieldSet set)
         {
         {
             if (ctx.state.recursionDepth >= ctx.state.recursionLimit)
             if (ctx.state.recursionDepth >= ctx.state.recursionLimit)
             {
             {
                 throw InvalidProtocolBufferException.RecursionLimitExceeded();
                 throw InvalidProtocolBufferException.RecursionLimitExceeded();
             }
             }
             ++ctx.state.recursionDepth;
             ++ctx.state.recursionDepth;
-            
-            ReadRawMessage(ref ctx, message);
+
+            set.MergeGroupFrom(ref ctx);
+            CheckLastTagWas(ref ctx.state, WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
 
 
             --ctx.state.recursionDepth;
             --ctx.state.recursionDepth;
         }
         }
 
 
-        public static void ReadRawMessage(ref CodedInputReader ctx, IMessage message)
+        public static void ReadRawMessage(ref ParseContext ctx, IMessage message)
         {
         {
             if (message is IBufferMessage bufferMessage)
             if (message is IBufferMessage bufferMessage)
             {
             {
-                bufferMessage.MergeFrom(ref ctx);
+                bufferMessage.MergeFrom_Internal(ref ctx);   
             }
             }
             else
             else
             {
             {
+                // If we reached here, it means we've ran into a nested message with older generated code
+                // which doesn't provide the MergeFrom_Internal 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 MergeFrom_Internal method we need.
+
                 if (ctx.state.codedInputStream == null)
                 if (ctx.state.codedInputStream == null)
                 {
                 {
-                    // TODO: improve the msg
-                    throw new InvalidProtocolBufferException("Cannot parse message with current parse context. Do you need to regenerate the code?");
+                    // 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()} doesn't provide the generated method that enables ParseContext-based parsing. You might need to regenerate the generated protobuf code.");
                 }
                 }
-                message.MergeFrom(ctx.state.codedInputStream);
-            }
-        }
 
 
-        public static void ReadRawMessage(ref ParseContext ctx, IMessage message)
-        {
-            if (message is IBufferMessage bufferMessage)
-            {
-                bufferMessage.MergeFrom_Internal(ref ctx);   
-            }
-            else
-            {
-                if (ctx.state.codedInputStream == null)
+                ctx.CopyStateTo(ctx.state.codedInputStream);
+                try
                 {
                 {
-                    // TODO: improve the msg
-                    throw new InvalidProtocolBufferException("Cannot parse message with current parse context. Do you need to regenerate the code?");
+                    // fallback parse using the CodedInputStream that started current parsing tree
+                    message.MergeFrom(ctx.state.codedInputStream);
+                }
+                finally
+                {
+                    ctx.LoadStateFrom(ctx.state.codedInputStream);
                 }
                 }
-                message.MergeFrom(ctx.state.codedInputStream);
             }
             }
         }
         }
 
 
@@ -227,5 +216,12 @@ namespace Google.Protobuf
                 throw InvalidProtocolBufferException.MoreDataAvailable();
                 throw InvalidProtocolBufferException.MoreDataAvailable();
             }
             }
         }
         }
+
+        private static void CheckLastTagWas(ref ParserInternalState state, uint expectedTag)
+        {
+            if (state.lastTag != expectedTag) {
+               throw InvalidProtocolBufferException.InvalidEndTag();
+            }
+        }
     }
     }
 }
 }

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

@@ -176,47 +176,48 @@ namespace Google.Protobuf
             fields[number] = field;
             fields[number] = field;
             return this;
             return this;
         }
         }
-
+        
         /// <summary>
         /// <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.
         /// into this set.
         /// </summary>
         /// </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>
         /// <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;
+            // TODO: deduplicate MergeFieldFrom implementations
+            uint tag = ctx.LastTag;
             int number = WireFormat.GetTagFieldNumber(tag);
             int number = WireFormat.GetTagFieldNumber(tag);
             switch (WireFormat.GetTagWireType(tag))
             switch (WireFormat.GetTagWireType(tag))
             {
             {
                 case WireFormat.WireType.Varint:
                 case WireFormat.WireType.Varint:
                     {
                     {
-                        ulong uint64 = input.ReadUInt64();
+                        ulong uint64 = ctx.ReadUInt64();
                         GetOrAddField(number).AddVarint(uint64);
                         GetOrAddField(number).AddVarint(uint64);
                         return true;
                         return true;
                     }
                     }
                 case WireFormat.WireType.Fixed32:
                 case WireFormat.WireType.Fixed32:
                     {
                     {
-                        uint uint32 = input.ReadFixed32();
+                        uint uint32 = ctx.ReadFixed32();
                         GetOrAddField(number).AddFixed32(uint32);
                         GetOrAddField(number).AddFixed32(uint32);
                         return true;
                         return true;
                     }
                     }
                 case WireFormat.WireType.Fixed64:
                 case WireFormat.WireType.Fixed64:
                     {
                     {
-                        ulong uint64 = input.ReadFixed64();
+                        ulong uint64 = ctx.ReadFixed64();
                         GetOrAddField(number).AddFixed64(uint64);
                         GetOrAddField(number).AddFixed64(uint64);
                         return true;
                         return true;
                     }
                     }
                 case WireFormat.WireType.LengthDelimited:
                 case WireFormat.WireType.LengthDelimited:
                     {
                     {
-                        ByteString bytes = input.ReadBytes();
+                        ByteString bytes = ctx.ReadBytes();
                         GetOrAddField(number).AddLengthDelimited(bytes);
                         GetOrAddField(number).AddLengthDelimited(bytes);
                         return true;
                         return true;
                     }
                     }
                 case WireFormat.WireType.StartGroup:
                 case WireFormat.WireType.StartGroup:
                     {
                     {
                         UnknownFieldSet set = new UnknownFieldSet();
                         UnknownFieldSet set = new UnknownFieldSet();
-                        input.ReadGroup(number, set);
+                        ParsingPrimitivesMessages.ReadGroup(ref ctx, number, set);
                         GetOrAddField(number).AddGroup(set);
                         GetOrAddField(number).AddGroup(set);
                         return true;
                         return true;
                     }
                     }
@@ -229,16 +230,16 @@ namespace Google.Protobuf
             }
             }
         }
         }
 
 
-        internal void MergeGroupFrom(CodedInputStream input)
+        internal void MergeGroupFrom(ref ParseContext ctx)
         {
         {
             while (true)
             while (true)
             {
             {
-                uint tag = input.ReadTag();
+                uint tag = ctx.ReadTag();
                 if (tag == 0)
                 if (tag == 0)
                 {
                 {
                     break;
                     break;
                 }
                 }
-                if (!MergeFieldFrom(input))
+                if (!MergeFieldFrom(ref ctx))
                 {
                 {
                     break;
                     break;
                 }
                 }
@@ -257,21 +258,44 @@ namespace Google.Protobuf
         public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields,
         public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields,
                                                      CodedInputStream input)
                                                      CodedInputStream input)
         {
         {
-            if (input.DiscardUnknownFields)
+            var ctx = new ParseContext(input);
+            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>
+        public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields,
+                                                     ref ParseContext ctx)
+        {
+            if (ctx.DiscardUnknownFields)
+            {
+                ParsingPrimitivesMessages.SkipLastField(ref ctx.buffer, ref ctx.state);
                 return unknownFields;
                 return unknownFields;
             }
             }
             if (unknownFields == null)
             if (unknownFields == null)
             {
             {
                 unknownFields = new UnknownFieldSet();
                 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;
             return unknownFields;
-        }
+        }
 
 
         /// <summary>
         /// <summary>
         /// Merges the fields from <paramref name="other"/> into this set.
         /// Merges the fields from <paramref name="other"/> into this set.