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

C# Proto2 feature : Groups (#5183)

Add group support for proto2
Sydney Acksman 7 éve
szülő
commit
6f73c50936
30 módosított fájl, 619 hozzáadás és 173 törlés
  1. 9 3
      csharp/src/AddressBook/Addressbook.cs
  2. 6 2
      csharp/src/Google.Protobuf.Conformance/Conformance.cs
  3. 21 7
      csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs
  4. 9 3
      csharp/src/Google.Protobuf.Test/TestProtos/TestMessagesProto3.cs
  5. 63 21
      csharp/src/Google.Protobuf.Test/TestProtos/UnittestCustomOptionsProto3.cs
  6. 3 1
      csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs
  7. 3 1
      csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublicProto3.cs
  8. 39 13
      csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs
  9. 117 39
      csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs
  10. 12 4
      csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs
  11. BIN
      csharp/src/Google.Protobuf.Test/testprotos.pb
  12. 19 1
      csharp/src/Google.Protobuf/CodedInputStream.cs
  13. 9 0
      csharp/src/Google.Protobuf/CodedOutputStream.cs
  14. 4 0
      csharp/src/Google.Protobuf/Collections/RepeatedField.cs
  15. 30 3
      csharp/src/Google.Protobuf/FieldCodec.cs
  16. 57 19
      csharp/src/Google.Protobuf/Reflection/Descriptor.cs
  17. 28 1
      csharp/src/Google.Protobuf/UnknownField.cs
  18. 41 9
      csharp/src/Google.Protobuf/UnknownFieldSet.cs
  19. 3 1
      csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
  20. 9 3
      csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
  21. 3 1
      csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs
  22. 3 1
      csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs
  23. 3 1
      csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
  24. 3 1
      csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs
  25. 9 3
      csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs
  26. 3 1
      csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs
  27. 15 5
      csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
  28. 27 9
      csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs
  29. 3 1
      src/google/protobuf/compiler/csharp/csharp_message.cc
  30. 68 19
      src/google/protobuf/compiler/csharp/csharp_message_field.cc

+ 9 - 3
csharp/src/AddressBook/Addressbook.cs

@@ -260,7 +260,9 @@ namespace Google.Protobuf.Examples.AddressBook {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -440,7 +442,9 @@ namespace Google.Protobuf.Examples.AddressBook {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 10: {
                 Number = input.ReadString();
@@ -573,7 +577,9 @@ namespace Google.Protobuf.Examples.AddressBook {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             people_.AddEntriesFrom(input, _repeated_people_codec);

+ 6 - 2
csharp/src/Google.Protobuf.Conformance/Conformance.cs

@@ -343,7 +343,9 @@ namespace Conformance {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             ProtobufPayload = input.ReadBytes();
@@ -683,7 +685,9 @@ namespace Conformance {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             ParseError = input.ReadString();

+ 21 - 7
csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs

@@ -541,7 +541,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             mapInt32Int32_.AddEntriesFrom(input, _map_mapInt32Int32_codec);
@@ -737,7 +739,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             if (testMap_ == null) {
@@ -861,7 +865,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             mapInt32Message_.AddEntriesFrom(input, _map_mapInt32Message_codec);
@@ -1001,7 +1007,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             map1_.AddEntriesFrom(input, _map_map1_codec);
@@ -1350,7 +1358,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             mapInt32Int32_.AddEntriesFrom(input, _map_mapInt32Int32_codec);
@@ -1531,7 +1541,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             type_.AddEntriesFrom(input, _map_type_codec);
@@ -1666,7 +1678,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             entry_.AddEntriesFrom(input, _map_entry_codec);

+ 9 - 3
csharp/src/Google.Protobuf.Test/TestProtos/TestMessagesProto3.cs

@@ -2867,7 +2867,9 @@ namespace ProtobufTestMessages.Proto3 {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             OptionalInt32 = input.ReadInt32();
@@ -3582,7 +3584,9 @@ namespace ProtobufTestMessages.Proto3 {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 8: {
                 A = input.ReadInt32();
@@ -3723,7 +3727,9 @@ namespace ProtobufTestMessages.Proto3 {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             C = input.ReadInt32();

+ 63 - 21
csharp/src/Google.Protobuf.Test/TestProtos/UnittestCustomOptionsProto3.cs

@@ -352,7 +352,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Field1 = input.ReadString();
@@ -478,7 +480,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -579,7 +583,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -680,7 +686,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -781,7 +789,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -882,7 +892,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -996,7 +1008,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -1097,7 +1111,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -1198,7 +1214,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -1299,7 +1317,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -1400,7 +1420,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -1501,7 +1523,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -1690,7 +1714,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Foo = input.ReadInt32();
@@ -1902,7 +1928,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             if (bar_ == null) {
@@ -2051,7 +2079,9 @@ namespace UnitTest.Issues.TestProtos {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 8: {
                 Waldo = input.ReadInt32();
@@ -2185,7 +2215,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Qux = input.ReadInt32();
@@ -2293,7 +2325,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -2475,7 +2509,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             I = input.ReadInt32();
@@ -2615,7 +2651,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Fieldname = input.ReadInt32();
@@ -2723,7 +2761,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -2855,7 +2895,9 @@ namespace UnitTest.Issues.TestProtos {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 8: {
                 NestedField = input.ReadInt32();

+ 3 - 1
csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs

@@ -167,7 +167,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             D = input.ReadInt32();

+ 3 - 1
csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublicProto3.cs

@@ -155,7 +155,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             E = input.ReadInt32();

+ 39 - 13
csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs

@@ -181,7 +181,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -284,7 +286,9 @@ namespace UnitTest.Issues.TestProtos {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
             }
           }
@@ -387,7 +391,9 @@ namespace UnitTest.Issues.TestProtos {
               while ((tag = input.ReadTag()) != 0) {
                 switch(tag) {
                   default:
-                    _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                    if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                      return;
+                    }
                     break;
                 }
               }
@@ -554,7 +560,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Value = (global::UnitTest.Issues.TestProtos.NegativeEnum) input.ReadEnum();
@@ -669,7 +677,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -899,7 +909,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             PrimitiveValue = input.ReadInt32();
@@ -1056,7 +1068,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Item = input.ReadInt32();
@@ -1209,7 +1223,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Types_ = input.ReadInt32();
@@ -1323,7 +1339,9 @@ namespace UnitTest.Issues.TestProtos {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
             }
           }
@@ -1647,7 +1665,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             PlainString = input.ReadString();
@@ -1847,7 +1867,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -2047,7 +2069,9 @@ namespace UnitTest.Issues.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Text = input.ReadString();
@@ -2211,7 +2235,9 @@ namespace UnitTest.Issues.TestProtos {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 8: {
                 X = input.ReadInt32();

+ 117 - 39
csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs

@@ -1387,7 +1387,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             SingleInt32 = input.ReadInt32();
@@ -1755,7 +1757,9 @@ namespace Google.Protobuf.TestProtos {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 8: {
                 Bb = input.ReadInt32();
@@ -1938,7 +1942,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             if (child_ == null) {
@@ -2082,7 +2088,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             DeprecatedInt32 = input.ReadInt32();
@@ -2215,7 +2223,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             C = input.ReadInt32();
@@ -2320,7 +2330,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -2451,7 +2463,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             if (foreignNested_ == null) {
@@ -2614,7 +2628,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             A = input.ReadInt32();
@@ -2774,7 +2790,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             if (a_ == null) {
@@ -2916,7 +2934,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             if (bb_ == null) {
@@ -3075,7 +3095,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             if (a_ == null) {
@@ -3211,7 +3233,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Value = (global::Google.Protobuf.TestProtos.TestEnumWithDupValue) input.ReadEnum();
@@ -3483,7 +3507,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             PrimitiveField = input.ReadInt32();
@@ -3724,7 +3750,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             MyInt = input.ReadInt64();
@@ -3899,7 +3927,9 @@ namespace Google.Protobuf.TestProtos {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 8: {
                 Bb = input.ReadInt32();
@@ -4037,7 +4067,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             SparseEnum = (global::Google.Protobuf.TestProtos.TestSparseEnum) input.ReadEnum();
@@ -4169,7 +4201,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Data = input.ReadString();
@@ -4290,7 +4324,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             data_.AddEntriesFrom(input, _repeated_data_codec);
@@ -4419,7 +4455,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Data = input.ReadBytes();
@@ -4548,7 +4586,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Data = input.ReadBytes();
@@ -4680,7 +4720,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Data = input.ReadInt32();
@@ -4809,7 +4851,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Data = input.ReadUInt32();
@@ -4938,7 +4982,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Data = input.ReadInt64();
@@ -5067,7 +5113,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Data = input.ReadUInt64();
@@ -5196,7 +5244,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Data = input.ReadBool();
@@ -5413,7 +5463,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             FooInt = input.ReadInt32();
@@ -5755,7 +5807,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 722:
           case 720: {
@@ -6154,7 +6208,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 722:
           case 720: {
@@ -6435,7 +6491,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 98:
           case 101: {
@@ -6593,7 +6651,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             A = input.ReadString();
@@ -6701,7 +6761,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -6802,7 +6864,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -6903,7 +6967,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -7004,7 +7070,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -7105,7 +7173,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -7206,7 +7276,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -7307,7 +7379,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }
@@ -7438,7 +7512,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Text = input.ReadString();
@@ -7585,7 +7661,9 @@ namespace Google.Protobuf.TestProtos {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 10: {
                 NestedText = input.ReadString();

+ 12 - 4
csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs

@@ -788,7 +788,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             if (anyField_ == null) {
@@ -1316,7 +1318,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             anyField_.AddEntriesFrom(input, _repeated_anyField_codec);
@@ -2027,7 +2031,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             global::Google.Protobuf.WellKnownTypes.Any subBuilder = new global::Google.Protobuf.WellKnownTypes.Any();
@@ -2538,7 +2544,9 @@ namespace Google.Protobuf.TestProtos {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             anyField_.AddEntriesFrom(input, _map_anyField_codec);

BIN
csharp/src/Google.Protobuf.Test/testprotos.pb


+ 19 - 1
csharp/src/Google.Protobuf/CodedInputStream.cs

@@ -373,7 +373,7 @@ namespace Google.Protobuf
                 if (IsAtEnd)
                 {
                     lastTag = 0;
-                    return 0; // This is the only case in which we return 0.
+                    return 0;
                 }
 
                 lastTag = ReadRawVarint32();
@@ -383,6 +383,10 @@ namespace Google.Protobuf
                 // If we actually read a tag with a field of 0, that's not a valid tag.
                 throw InvalidProtocolBufferException.InvalidTag();
             }
+            if (ReachedLimit)
+            {
+                return 0;
+            }
             return lastTag;
         }
 
@@ -591,6 +595,20 @@ namespace Google.Protobuf
             PopLimit(oldLimit);
         }
 
+        /// <summary>
+        /// Reads an embedded group field from the stream.
+        /// </summary>
+        public void ReadGroup(IMessage builder)
+        {
+            if (recursionDepth >= recursionLimit)
+            {
+                throw InvalidProtocolBufferException.RecursionLimitExceeded();
+            }
+            ++recursionDepth;
+            builder.MergeFrom(this);
+            --recursionDepth;
+        }
+
         /// <summary>
         /// Reads a bytes field value from the stream.
         /// </summary>   

+ 9 - 0
csharp/src/Google.Protobuf/CodedOutputStream.cs

@@ -303,6 +303,15 @@ namespace Google.Protobuf
             value.WriteTo(this);
         }
 
+        /// <summary>
+        /// Writes a group, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteGroup(IMessage value)
+        {
+            value.WriteTo(this);
+        }
+
         /// <summary>
         /// Write a byte string, without a tag, to the stream.
         /// The data is length-prefixed.

+ 4 - 0
csharp/src/Google.Protobuf/Collections/RepeatedField.cs

@@ -208,6 +208,10 @@ namespace Google.Protobuf.Collections
                 {
                     output.WriteTag(tag);
                     writer(output, array[i]);
+                    if (codec.EndTag != 0)
+                    {
+                        output.WriteTag(codec.EndTag);
+                    }
                 }
             }
         }

+ 30 - 3
csharp/src/Google.Protobuf/FieldCodec.cs

@@ -225,6 +225,19 @@ namespace Google.Protobuf
                 (output, value) => output.WriteMessage(value), message => CodedOutputStream.ComputeMessageSize(message), tag);
         }
 
+        /// <summary>
+        /// Retrieves a codec suitable for a group field with the given tag.
+        /// </summary>
+        /// <param name="startTag">The start group tag.</param>
+        /// <param name="endTag">The end group tag.</param>
+        /// <param name="parser">A parser to use for the group message type.</param>
+        /// <returns>A codec for given tag</returns>
+        public static FieldCodec<T> ForGroup<T>(uint startTag, uint endTag, MessageParser<T> parser) where T : IMessage<T>
+        {
+            return new FieldCodec<T>(input => { T message = parser.CreateTemplate(); input.ReadGroup(message); return message; },
+                (output, value) => output.WriteGroup(value), message => CodedOutputStream.ComputeGroupSize(message), startTag, endTag);
+        }
+
         /// <summary>
         /// Creates a codec for a wrapper type of a class - which must be string or ByteString.
         /// </summary>
@@ -235,7 +248,7 @@ namespace Google.Protobuf
                 input => WrapperCodecs.Read<T>(input, nestedCodec),
                 (output, value) => WrapperCodecs.Write<T>(output, value, nestedCodec),
                 value => WrapperCodecs.CalculateSize<T>(value, nestedCodec),
-                tag,
+                tag, 0,
                 null); // Default value for the wrapper
         }
 
@@ -250,7 +263,7 @@ namespace Google.Protobuf
                 input => WrapperCodecs.Read<T>(input, nestedCodec),
                 (output, value) => WrapperCodecs.Write<T>(output, value.Value, nestedCodec),
                 value => value == null ? 0 : WrapperCodecs.CalculateSize<T>(value.Value, nestedCodec),
-                tag,
+                tag, 0,
                 null); // Default value for the wrapper
         }
 
@@ -399,6 +412,14 @@ namespace Google.Protobuf
         /// </value>
         internal uint Tag { get; }
 
+        /// <summary>
+        /// Gets the end tag of the codec or 0 if there is no end tag
+        /// </summary>
+        /// <value>
+        /// The end tag of the codec.
+        /// </value>
+        internal uint EndTag { get; }
+
         /// <summary>
         /// Default value for this codec. Usually the same for every instance of the same type, but
         /// for string/ByteString wrapper fields the codec's default value is null, whereas for
@@ -424,7 +445,8 @@ namespace Google.Protobuf
             Func<CodedInputStream, T> reader,
             Action<CodedOutputStream, T> writer,
             Func<T, int> sizeCalculator,
-            uint tag) : this(reader, writer, sizeCalculator, tag, DefaultDefault)
+            uint tag,
+            uint endTag = 0) : this(reader, writer, sizeCalculator, tag, endTag, DefaultDefault)
         {
         }
 
@@ -433,6 +455,7 @@ namespace Google.Protobuf
             Action<CodedOutputStream, T> writer,
             Func<T, int> sizeCalculator,
             uint tag,
+            uint endTag,
             T defaultValue)
         {
             ValueReader = reader;
@@ -455,6 +478,10 @@ namespace Google.Protobuf
             {
                 output.WriteTag(Tag);
                 ValueWriter(output, value);
+                if (EndTag != 0)
+                {
+                    output.WriteTag(EndTag);
+                }
             }
         }
 

+ 57 - 19
csharp/src/Google.Protobuf/Reflection/Descriptor.cs

@@ -302,7 +302,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             file_.AddEntriesFrom(input, _repeated_file_codec);
@@ -733,7 +735,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -1098,7 +1102,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -1357,7 +1363,9 @@ namespace Google.Protobuf.Reflection {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 8: {
                 Start = input.ReadInt32();
@@ -1560,7 +1568,9 @@ namespace Google.Protobuf.Reflection {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 8: {
                 Start = input.ReadInt32();
@@ -1693,7 +1703,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 7994: {
             uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
@@ -2200,7 +2212,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -2487,7 +2501,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -2732,7 +2748,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -2948,7 +2966,9 @@ namespace Google.Protobuf.Reflection {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 8: {
                 Start = input.ReadInt32();
@@ -3177,7 +3197,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -3385,7 +3407,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -3735,7 +3759,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -6923,7 +6949,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 18: {
             name_.AddEntriesFrom(input, _repeated_name_codec);
@@ -7136,7 +7164,9 @@ namespace Google.Protobuf.Reflection {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 10: {
                 NamePart_ = input.ReadString();
@@ -7315,7 +7345,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             location_.AddEntriesFrom(input, _repeated_location_codec);
@@ -7623,7 +7655,9 @@ namespace Google.Protobuf.Reflection {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 10:
               case 8: {
@@ -7776,7 +7810,9 @@ namespace Google.Protobuf.Reflection {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             annotation_.AddEntriesFrom(input, _repeated_annotation_codec);
@@ -8027,7 +8063,9 @@ namespace Google.Protobuf.Reflection {
           while ((tag = input.ReadTag()) != 0) {
             switch(tag) {
               default:
-                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+                  return;
+                }
                 break;
               case 10:
               case 8: {

+ 28 - 1
csharp/src/Google.Protobuf/UnknownField.cs

@@ -55,6 +55,7 @@ namespace Google.Protobuf
         private List<uint> fixed32List;
         private List<ulong> fixed64List;
         private List<ByteString> lengthDelimitedList;
+        private List<UnknownFieldSet> groupList;
 
         /// <summary>
         /// Creates a new UnknownField.
@@ -77,7 +78,8 @@ namespace Google.Protobuf
                    && Lists.Equals(varintList, otherField.varintList)
                    && Lists.Equals(fixed32List, otherField.fixed32List)
                    && Lists.Equals(fixed64List, otherField.fixed64List)
-                   && Lists.Equals(lengthDelimitedList, otherField.lengthDelimitedList);
+                   && Lists.Equals(lengthDelimitedList, otherField.lengthDelimitedList)
+                   && Lists.Equals(groupList, otherField.groupList);
         }
 
         /// <summary>
@@ -90,6 +92,7 @@ namespace Google.Protobuf
             hash = hash * 47 + Lists.GetHashCode(fixed32List);
             hash = hash * 47 + Lists.GetHashCode(fixed64List);
             hash = hash * 47 + Lists.GetHashCode(lengthDelimitedList);
+            hash = hash * 47 + Lists.GetHashCode(groupList);
             return hash;
         }
 
@@ -133,6 +136,15 @@ namespace Google.Protobuf
                     output.WriteBytes(value);
                 }
             }
+            if (groupList != null)
+            {
+                foreach (UnknownFieldSet value in groupList)
+                {
+                    output.WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
+                    value.WriteTo(output);
+                    output.WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
+                }
+            }
         }
 
         /// <summary>
@@ -168,6 +180,14 @@ namespace Google.Protobuf
                     result += CodedOutputStream.ComputeBytesSize(value);
                 }
             }
+            if (groupList != null)
+            {
+                result += CodedOutputStream.ComputeTagSize(fieldNumber) * 2 * groupList.Count;
+                foreach (UnknownFieldSet value in groupList)
+                {
+                    result += value.CalculateSize();
+                }
+            }
             return result;
         }
 
@@ -182,6 +202,7 @@ namespace Google.Protobuf
             fixed32List = AddAll(fixed32List, other.fixed32List);
             fixed64List = AddAll(fixed64List, other.fixed64List);
             lengthDelimitedList = AddAll(lengthDelimitedList, other.lengthDelimitedList);
+            groupList = AddAll(groupList, other.groupList);
             return this;
         }
 
@@ -245,6 +266,12 @@ namespace Google.Protobuf
             return this;
         }
 
+        internal UnknownField AddGroup(UnknownFieldSet value) 
+        {
+            groupList = Add(groupList, value);
+            return this;
+        }
+
         /// <summary>
         /// Adds <paramref name="value"/> to the <paramref name="list"/>, creating
         /// a new list if <paramref name="list"/> is null. The list is returned - either

+ 41 - 9
csharp/src/Google.Protobuf/UnknownFieldSet.cs

@@ -183,7 +183,7 @@ namespace Google.Protobuf
         /// </summary>
         /// <param name="input">The coded input stream containing the field</param>
         /// <returns>false if the tag is an "end group" tag, true otherwise</returns>
-        private void MergeFieldFrom(CodedInputStream input)
+        private bool MergeFieldFrom(CodedInputStream input)
         {
             uint tag = input.LastTag;
             int number = WireFormat.GetTagFieldNumber(tag);
@@ -193,34 +193,40 @@ namespace Google.Protobuf
                     {
                         ulong uint64 = input.ReadUInt64();
                         GetOrAddField(number).AddVarint(uint64);
-                        return;
+                        return true;
                     }
                 case WireFormat.WireType.Fixed32:
                     {
                         uint uint32 = input.ReadFixed32();
                         GetOrAddField(number).AddFixed32(uint32);
-                        return;
+                        return true;
                     }
                 case WireFormat.WireType.Fixed64:
                     {
                         ulong uint64 = input.ReadFixed64();
                         GetOrAddField(number).AddFixed64(uint64);
-                        return;
+                        return true;
                     }
                 case WireFormat.WireType.LengthDelimited:
                     {
                         ByteString bytes = input.ReadBytes();
                         GetOrAddField(number).AddLengthDelimited(bytes);
-                        return;
+                        return true;
                     }
                 case WireFormat.WireType.StartGroup:
                     {
-                        input.SkipGroup(tag);
-                        return;
+                        uint endTag = WireFormat.MakeTag(number, WireFormat.WireType.EndGroup);
+                        UnknownFieldSet set = new UnknownFieldSet();
+                        while (input.ReadTag() != endTag)
+                        {
+                            set.MergeFieldFrom(input);
+                        }
+                        GetOrAddField(number).AddGroup(set);
+                        return true;
                     }
                 case WireFormat.WireType.EndGroup:
                     {
-                        throw new InvalidProtocolBufferException("Merge an unknown field of end-group tag, indicating that the corresponding start-group was missing.");
+                        return false;
                     }
                 default:
                     throw new InvalidOperationException("Wire Type is invalid.");
@@ -248,8 +254,34 @@ namespace Google.Protobuf
             {
                 unknownFields = new UnknownFieldSet();
             }
-            unknownFields.MergeFieldFrom(input);
+            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
+            }
             return unknownFields;
+        }
+
+        /// <summary>
+        /// Create a new UnknownFieldSet if unknownFields is null.
+        /// Parse a single field from <paramref name="input"/> and merge it
+        /// into unknownFields. If <paramref name="input"/> 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="input">The coded input stream containing the field</param>
+        /// <returns>The merged UnknownFieldSet</returns>
+        public static bool MergeFieldFrom(ref UnknownFieldSet unknownFields, CodedInputStream input)
+        {
+            if (input.DiscardUnknownFields)
+            {
+                input.SkipLastField();
+                return true;
+            }
+            if (unknownFields == null)
+            {
+                unknownFields = new UnknownFieldSet();
+            }
+            return unknownFields.MergeFieldFrom(input);
         }
 
         /// <summary>

+ 3 - 1
csharp/src/Google.Protobuf/WellKnownTypes/Any.cs

@@ -292,7 +292,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             TypeUrl = input.ReadString();

+ 9 - 3
csharp/src/Google.Protobuf/WellKnownTypes/Api.cs

@@ -345,7 +345,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -661,7 +663,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -925,7 +929,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();

+ 3 - 1
csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs

@@ -254,7 +254,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Seconds = input.ReadInt64();

+ 3 - 1
csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs

@@ -143,7 +143,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
         }
       }

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

@@ -352,7 +352,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             paths_.AddEntriesFrom(input, _repeated_paths_codec);

+ 3 - 1
csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs

@@ -165,7 +165,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             FileName = input.ReadString();

+ 9 - 3
csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs

@@ -189,7 +189,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             fields_.AddEntriesFrom(input, _map_fields_codec);
@@ -513,7 +515,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             kind_ = input.ReadEnum();
@@ -673,7 +677,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             values_.AddEntriesFrom(input, _repeated_values_codec);

+ 3 - 1
csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs

@@ -273,7 +273,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Seconds = input.ReadInt64();

+ 15 - 5
csharp/src/Google.Protobuf/WellKnownTypes/Type.cs

@@ -328,7 +328,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -723,7 +725,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Kind = (global::Google.Protobuf.WellKnownTypes.Field.Types.Kind) input.ReadEnum();
@@ -1100,7 +1104,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -1300,7 +1306,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();
@@ -1480,7 +1488,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Name = input.ReadString();

+ 27 - 9
csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs

@@ -177,7 +177,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 9: {
             Value = input.ReadDouble();
@@ -314,7 +316,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 13: {
             Value = input.ReadFloat();
@@ -451,7 +455,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Value = input.ReadInt64();
@@ -588,7 +594,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Value = input.ReadUInt64();
@@ -725,7 +733,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Value = input.ReadInt32();
@@ -862,7 +872,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Value = input.ReadUInt32();
@@ -999,7 +1011,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 8: {
             Value = input.ReadBool();
@@ -1136,7 +1150,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Value = input.ReadString();
@@ -1273,7 +1289,9 @@ namespace Google.Protobuf.WellKnownTypes {
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {
+              return;
+            }
             break;
           case 10: {
             Value = input.ReadBytes();

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

@@ -539,7 +539,9 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
   } else {
     printer->Print(
       "default:\n"
-      "  _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);\n"
+      "  if (!pb::UnknownFieldSet.MergeFieldFrom(ref _unknownFields, input)) {\n"
+      "    return;\n"
+      "  }\n"
       "  break;\n");
   }
   for (int i = 0; i < fields_by_number().size(); i++) {

+ 68 - 19
src/google/protobuf/compiler/csharp/csharp_message_field.cc

@@ -37,6 +37,8 @@
 #include <google/protobuf/io/printer.h>
 #include <google/protobuf/io/zero_copy_stream.h>
 #include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/wire_format_lite.h>
 
 #include <google/protobuf/compiler/csharp/csharp_doc_comment.h>
 #include <google/protobuf/compiler/csharp/csharp_helpers.h>
@@ -56,6 +58,21 @@ MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor,
     variables_["has_property_check"] = name() + "_ != null";
     variables_["has_not_property_check"] = name() + "_ == null";
   }
+
+  if (descriptor_->type() == FieldDescriptor::Type::TYPE_GROUP) {
+    int tag_size = internal::WireFormat::TagSize(descriptor_->number(), descriptor_->type()) / 2;
+    uint tag = internal::WireFormatLite::MakeTag(
+        descriptor_->number(),
+        internal::WireFormatLite::WIRETYPE_END_GROUP);
+    uint8 tag_array[5];
+    io::CodedOutputStream::WriteTagToArray(tag, tag_array);
+    string tag_bytes = SimpleItoa(tag_array[0]);
+    for (int i = 1; i < tag_size; i++) {
+        tag_bytes += ", " + SimpleItoa(tag_array[i]);
+    }
+    variables_["end_tag"] = SimpleItoa(tag);
+    variables_["end_tag_bytes"] = tag_bytes;
+  }
 }
 
 MessageFieldGenerator::~MessageFieldGenerator() {
@@ -114,25 +131,47 @@ void MessageFieldGenerator::GenerateParsingCode(io::Printer* printer) {
     variables_,
     "if ($has_not_property_check$) {\n"
     "  $property_name$ = new $type_name$();\n"
-    "}\n"
-    "input.ReadMessage($property_name$);\n");
+    "}\n");
+  if (descriptor_->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
+    printer->Print(variables_, "input.ReadMessage($property_name$);\n");
+  } else {
+    printer->Print(variables_, "input.ReadGroup($property_name$);\n");
+  }
 }
 
 void MessageFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
-  printer->Print(
-    variables_,
-    "if ($has_property_check$) {\n"
-    "  output.WriteRawTag($tag_bytes$);\n"
-    "  output.WriteMessage($property_name$);\n"
-    "}\n");
+  if (descriptor_->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
+    printer->Print(
+      variables_,
+      "if ($has_property_check$) {\n"
+      "  output.WriteRawTag($tag_bytes$);\n"
+      "  output.WriteMessage($property_name$);\n"
+      "}\n");
+  } else {
+    printer->Print(
+      variables_,
+      "if ($has_property_check$) {\n"
+      "  output.WriteRawTag($tag_bytes$);\n"
+      "  output.WriteGroup($property_name$);\n"
+      "  output.WriteRawTag($end_tag_bytes$);\n"
+      "}\n");
+  }
 }
 
 void MessageFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
-  printer->Print(
-    variables_,
-    "if ($has_property_check$) {\n"
-    "  size += $tag_size$ + pb::CodedOutputStream.ComputeMessageSize($property_name$);\n"
-    "}\n");
+  if (descriptor_->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
+    printer->Print(
+      variables_,
+      "if ($has_property_check$) {\n"
+      "  size += $tag_size$ + pb::CodedOutputStream.ComputeMessageSize($property_name$);\n"
+      "}\n");
+  } else {
+    printer->Print(
+      variables_,
+      "if ($has_property_check$) {\n"
+      "  size += $tag_size$ + $tag_size$ + pb::CodedOutputStream.ComputeGroupSize($property_name$);\n"
+      "}\n");
+  }
 }
 
 void MessageFieldGenerator::WriteHash(io::Printer* printer) {
@@ -160,9 +199,15 @@ void MessageFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
 }
 
 void MessageFieldGenerator::GenerateCodecCode(io::Printer* printer) {
-  printer->Print(
-    variables_,
-    "pb::FieldCodec.ForMessage($tag$, $type_name$.Parser)");
+  if (descriptor_->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
+    printer->Print(
+      variables_,
+      "pb::FieldCodec.ForMessage($tag$, $type_name$.Parser)");
+  } else {
+    printer->Print(
+      variables_,
+      "pb::FieldCodec.ForGroup($tag$, $end_tag$, $type_name$.Parser)");
+  }
 }
 
 MessageOneofFieldGenerator::MessageOneofFieldGenerator(
@@ -228,9 +273,13 @@ void MessageOneofFieldGenerator::GenerateParsingCode(io::Printer* printer) {
     "$type_name$ subBuilder = new $type_name$();\n"
     "if ($has_property_check$) {\n"
     "  subBuilder.MergeFrom($property_name$);\n"
-    "}\n"
-    "input.ReadMessage(subBuilder);\n"
-    "$property_name$ = subBuilder;\n");
+    "}\n");
+  if (descriptor_->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
+    printer->Print("input.ReadMessage(subBuilder);\n");
+  } else {
+    printer->Print("input.ReadGroup(subBuilder);\n");
+  }
+  printer->Print(variables_, "$property_name$ = subBuilder;\n");
 }
 
 void MessageOneofFieldGenerator::WriteToString(io::Printer* printer) {