Bladeren bron

C# Proto2 feature : Field presence and default values (#4642)

* Compiler changes

* Generated code changes

* Library changes

* Compiler style changes

* Generated style changes

* Fix Windows build errors

* Implement changes from review

* Reintroduce proto2 check

* Compiler changes (required handling review)

* Generated code changes (required handling review)

* Library changes (required handling review

* Field presence rewrite (compiler changes)

* Field presence rewrite (generated code changes)

* Compiler comment

* IFieldAccessor.HasValue library implementation

* Remove Clear methods and default values from proto3 code (Compiler)

* Remove Clear methods and default values from proto3 code (Generated)

* Remove Clear methods and default values from proto3 code (Library)

* Fix distcheck error

* Rewrite default string values to use base64 and convert

* Library changes (IMessage2)

* Compiler changes (IMessage2)

* Generated changes (IMessage2)

* Rebased and regenerated

* Compiler changes (initialized extension)

* Generated changes (initialized extension)

* Library changes (initialized extension)

* Refactor MessageExtensions.IsRequired

* Move string default value creator and bytes default value creator back to seperate methods

* Dead code cleanup

* Fixed segmentation fault
Removed unused header method declarations
Sydney Acksman 7 jaren geleden
bovenliggende
commit
54176b26a9
51 gewijzigde bestanden met toevoegingen van 2486 en 1941 verwijderingen
  1. 0 1
      Makefile.am
  2. 4 4
      csharp/src/AddressBook/Addressbook.cs
  3. 2 2
      csharp/src/Google.Protobuf.Conformance/Conformance.cs
  4. 3 3
      csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs
  5. 41 32
      csharp/src/Google.Protobuf.Test/TestProtos/TestMessagesProto3.cs
  6. 9 9
      csharp/src/Google.Protobuf.Test/TestProtos/UnittestCustomOptionsProto3.cs
  7. 5 5
      csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs
  8. 42 42
      csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs
  9. 39 30
      csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs
  10. 771 771
      csharp/src/Google.Protobuf/Collections/MapField.cs
  11. 595 595
      csharp/src/Google.Protobuf/Collections/RepeatedField.cs
  12. 6 1
      csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs
  13. 50 0
      csharp/src/Google.Protobuf/MessageExtensions.cs
  14. 12 0
      csharp/src/Google.Protobuf/MessageParser.cs
  15. 78 78
      csharp/src/Google.Protobuf/ProtoPreconditions.cs
  16. 359 101
      csharp/src/Google.Protobuf/Reflection/Descriptor.cs
  17. 1 0
      csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs
  18. 15 16
      csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
  19. 1 1
      csharp/src/Google.Protobuf/Reflection/FieldType.cs
  20. 5 0
      csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs
  21. 5 0
      csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs
  22. 0 59
      csharp/src/Google.Protobuf/Reflection/PartialClasses.cs
  23. 10 0
      csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs
  24. 5 0
      csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs
  25. 24 9
      csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs
  26. 5 5
      csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
  27. 13 13
      csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
  28. 2 2
      csharp/src/Google.Protobuf/WireFormat.cs
  29. 5 5
      src/google/protobuf/compiler/csharp/csharp_enum_field.cc
  30. 2 2
      src/google/protobuf/compiler/csharp/csharp_enum_field.h
  31. 52 53
      src/google/protobuf/compiler/csharp/csharp_field_base.cc
  32. 4 5
      src/google/protobuf/compiler/csharp/csharp_field_base.h
  33. 3 3
      src/google/protobuf/compiler/csharp/csharp_generator.cc
  34. 48 13
      src/google/protobuf/compiler/csharp/csharp_helpers.cc
  35. 7 1
      src/google/protobuf/compiler/csharp/csharp_helpers.h
  36. 2 2
      src/google/protobuf/compiler/csharp/csharp_map_field.cc
  37. 1 1
      src/google/protobuf/compiler/csharp/csharp_map_field.h
  38. 40 18
      src/google/protobuf/compiler/csharp/csharp_message.cc
  39. 2 5
      src/google/protobuf/compiler/csharp/csharp_message.h
  40. 54 12
      src/google/protobuf/compiler/csharp/csharp_message_field.cc
  41. 2 2
      src/google/protobuf/compiler/csharp/csharp_message_field.h
  42. 100 20
      src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
  43. 2 2
      src/google/protobuf/compiler/csharp/csharp_primitive_field.h
  44. 2 2
      src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc
  45. 1 1
      src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.h
  46. 4 4
      src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc
  47. 1 1
      src/google/protobuf/compiler/csharp/csharp_repeated_message_field.h
  48. 2 2
      src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc
  49. 1 1
      src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h
  50. 47 5
      src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc
  51. 2 2
      src/google/protobuf/compiler/csharp/csharp_wrapper_field.h

+ 0 - 1
Makefile.am

@@ -179,7 +179,6 @@ csharp_EXTRA_DIST=                                                           \
   csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs                   \
   csharp/src/Google.Protobuf/Reflection/OriginalNameAttribute.cs             \
   csharp/src/Google.Protobuf/Reflection/PackageDescriptor.cs                 \
-  csharp/src/Google.Protobuf/Reflection/PartialClasses.cs                    \
   csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs                    \
   csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs             \
   csharp/src/Google.Protobuf/Reflection/ServiceDescriptor.cs                 \

+ 4 - 4
csharp/src/AddressBook/Addressbook.cs

@@ -247,7 +247,7 @@ namespace Google.Protobuf.Examples.AddressBook {
       phones_.Add(other.phones_);
       if (other.lastUpdated_ != null) {
         if (lastUpdated_ == null) {
-          lastUpdated_ = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+          LastUpdated = new global::Google.Protobuf.WellKnownTypes.Timestamp();
         }
         LastUpdated.MergeFrom(other.LastUpdated);
       }
@@ -280,9 +280,9 @@ namespace Google.Protobuf.Examples.AddressBook {
           }
           case 42: {
             if (lastUpdated_ == null) {
-              lastUpdated_ = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+              LastUpdated = new global::Google.Protobuf.WellKnownTypes.Timestamp();
             }
-            input.ReadMessage(lastUpdated_);
+            input.ReadMessage(LastUpdated);
             break;
           }
         }
@@ -447,7 +447,7 @@ namespace Google.Protobuf.Examples.AddressBook {
                 break;
               }
               case 16: {
-                type_ = (global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType) input.ReadEnum();
+                Type = (global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType) input.ReadEnum();
                 break;
               }
             }

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

@@ -354,7 +354,7 @@ namespace Conformance {
             break;
           }
           case 24: {
-            requestedOutputFormat_ = (global::Conformance.WireFormat) input.ReadEnum();
+            RequestedOutputFormat = (global::Conformance.WireFormat) input.ReadEnum();
             break;
           }
           case 34: {
@@ -362,7 +362,7 @@ namespace Conformance {
             break;
           }
           case 40: {
-            testCategory_ = (global::Conformance.TestCategory) input.ReadEnum();
+            TestCategory = (global::Conformance.TestCategory) input.ReadEnum();
             break;
           }
         }

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

@@ -724,7 +724,7 @@ namespace Google.Protobuf.TestProtos {
       }
       if (other.testMap_ != null) {
         if (testMap_ == null) {
-          testMap_ = new global::Google.Protobuf.TestProtos.TestMap();
+          TestMap = new global::Google.Protobuf.TestProtos.TestMap();
         }
         TestMap.MergeFrom(other.TestMap);
       }
@@ -741,9 +741,9 @@ namespace Google.Protobuf.TestProtos {
             break;
           case 10: {
             if (testMap_ == null) {
-              testMap_ = new global::Google.Protobuf.TestProtos.TestMap();
+              TestMap = new global::Google.Protobuf.TestProtos.TestMap();
             }
-            input.ReadMessage(testMap_);
+            input.ReadMessage(TestMap);
             break;
           }
         }

+ 41 - 32
csharp/src/Google.Protobuf.Test/TestProtos/TestMessagesProto3.cs

@@ -1171,6 +1171,7 @@ namespace ProtobufTestMessages.Proto3 {
       }
     }
 
+
     /// <summary>Field number for the "optional_int32_wrapper" field.</summary>
     public const int OptionalInt32WrapperFieldNumber = 202;
     private static readonly pb::FieldCodec<int?> _single_optionalInt32Wrapper_codec = pb::FieldCodec.ForStructWrapper<int>(1618);
@@ -1183,6 +1184,7 @@ namespace ProtobufTestMessages.Proto3 {
       }
     }
 
+
     /// <summary>Field number for the "optional_int64_wrapper" field.</summary>
     public const int OptionalInt64WrapperFieldNumber = 203;
     private static readonly pb::FieldCodec<long?> _single_optionalInt64Wrapper_codec = pb::FieldCodec.ForStructWrapper<long>(1626);
@@ -1195,6 +1197,7 @@ namespace ProtobufTestMessages.Proto3 {
       }
     }
 
+
     /// <summary>Field number for the "optional_uint32_wrapper" field.</summary>
     public const int OptionalUint32WrapperFieldNumber = 204;
     private static readonly pb::FieldCodec<uint?> _single_optionalUint32Wrapper_codec = pb::FieldCodec.ForStructWrapper<uint>(1634);
@@ -1207,6 +1210,7 @@ namespace ProtobufTestMessages.Proto3 {
       }
     }
 
+
     /// <summary>Field number for the "optional_uint64_wrapper" field.</summary>
     public const int OptionalUint64WrapperFieldNumber = 205;
     private static readonly pb::FieldCodec<ulong?> _single_optionalUint64Wrapper_codec = pb::FieldCodec.ForStructWrapper<ulong>(1642);
@@ -1219,6 +1223,7 @@ namespace ProtobufTestMessages.Proto3 {
       }
     }
 
+
     /// <summary>Field number for the "optional_float_wrapper" field.</summary>
     public const int OptionalFloatWrapperFieldNumber = 206;
     private static readonly pb::FieldCodec<float?> _single_optionalFloatWrapper_codec = pb::FieldCodec.ForStructWrapper<float>(1650);
@@ -1231,6 +1236,7 @@ namespace ProtobufTestMessages.Proto3 {
       }
     }
 
+
     /// <summary>Field number for the "optional_double_wrapper" field.</summary>
     public const int OptionalDoubleWrapperFieldNumber = 207;
     private static readonly pb::FieldCodec<double?> _single_optionalDoubleWrapper_codec = pb::FieldCodec.ForStructWrapper<double>(1658);
@@ -1243,6 +1249,7 @@ namespace ProtobufTestMessages.Proto3 {
       }
     }
 
+
     /// <summary>Field number for the "optional_string_wrapper" field.</summary>
     public const int OptionalStringWrapperFieldNumber = 208;
     private static readonly pb::FieldCodec<string> _single_optionalStringWrapper_codec = pb::FieldCodec.ForClassWrapper<string>(1666);
@@ -1255,6 +1262,7 @@ namespace ProtobufTestMessages.Proto3 {
       }
     }
 
+
     /// <summary>Field number for the "optional_bytes_wrapper" field.</summary>
     public const int OptionalBytesWrapperFieldNumber = 209;
     private static readonly pb::FieldCodec<pb::ByteString> _single_optionalBytesWrapper_codec = pb::FieldCodec.ForClassWrapper<pb::ByteString>(1674);
@@ -1267,6 +1275,7 @@ namespace ProtobufTestMessages.Proto3 {
       }
     }
 
+
     /// <summary>Field number for the "repeated_bool_wrapper" field.</summary>
     public const int RepeatedBoolWrapperFieldNumber = 211;
     private static readonly pb::FieldCodec<bool?> _repeated_repeatedBoolWrapper_codec
@@ -2598,13 +2607,13 @@ namespace ProtobufTestMessages.Proto3 {
       }
       if (other.optionalNestedMessage_ != null) {
         if (optionalNestedMessage_ == null) {
-          optionalNestedMessage_ = new global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage();
+          OptionalNestedMessage = new global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage();
         }
         OptionalNestedMessage.MergeFrom(other.OptionalNestedMessage);
       }
       if (other.optionalForeignMessage_ != null) {
         if (optionalForeignMessage_ == null) {
-          optionalForeignMessage_ = new global::ProtobufTestMessages.Proto3.ForeignMessage();
+          OptionalForeignMessage = new global::ProtobufTestMessages.Proto3.ForeignMessage();
         }
         OptionalForeignMessage.MergeFrom(other.OptionalForeignMessage);
       }
@@ -2622,7 +2631,7 @@ namespace ProtobufTestMessages.Proto3 {
       }
       if (other.recursiveMessage_ != null) {
         if (recursiveMessage_ == null) {
-          recursiveMessage_ = new global::ProtobufTestMessages.Proto3.TestAllTypesProto3();
+          RecursiveMessage = new global::ProtobufTestMessages.Proto3.TestAllTypesProto3();
         }
         RecursiveMessage.MergeFrom(other.RecursiveMessage);
       }
@@ -2722,37 +2731,37 @@ namespace ProtobufTestMessages.Proto3 {
       repeatedBytesWrapper_.Add(other.repeatedBytesWrapper_);
       if (other.optionalDuration_ != null) {
         if (optionalDuration_ == null) {
-          optionalDuration_ = new global::Google.Protobuf.WellKnownTypes.Duration();
+          OptionalDuration = new global::Google.Protobuf.WellKnownTypes.Duration();
         }
         OptionalDuration.MergeFrom(other.OptionalDuration);
       }
       if (other.optionalTimestamp_ != null) {
         if (optionalTimestamp_ == null) {
-          optionalTimestamp_ = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+          OptionalTimestamp = new global::Google.Protobuf.WellKnownTypes.Timestamp();
         }
         OptionalTimestamp.MergeFrom(other.OptionalTimestamp);
       }
       if (other.optionalFieldMask_ != null) {
         if (optionalFieldMask_ == null) {
-          optionalFieldMask_ = new global::Google.Protobuf.WellKnownTypes.FieldMask();
+          OptionalFieldMask = new global::Google.Protobuf.WellKnownTypes.FieldMask();
         }
         OptionalFieldMask.MergeFrom(other.OptionalFieldMask);
       }
       if (other.optionalStruct_ != null) {
         if (optionalStruct_ == null) {
-          optionalStruct_ = new global::Google.Protobuf.WellKnownTypes.Struct();
+          OptionalStruct = new global::Google.Protobuf.WellKnownTypes.Struct();
         }
         OptionalStruct.MergeFrom(other.OptionalStruct);
       }
       if (other.optionalAny_ != null) {
         if (optionalAny_ == null) {
-          optionalAny_ = new global::Google.Protobuf.WellKnownTypes.Any();
+          OptionalAny = new global::Google.Protobuf.WellKnownTypes.Any();
         }
         OptionalAny.MergeFrom(other.OptionalAny);
       }
       if (other.optionalValue_ != null) {
         if (optionalValue_ == null) {
-          optionalValue_ = new global::Google.Protobuf.WellKnownTypes.Value();
+          OptionalValue = new global::Google.Protobuf.WellKnownTypes.Value();
         }
         OptionalValue.MergeFrom(other.OptionalValue);
       }
@@ -2922,24 +2931,24 @@ namespace ProtobufTestMessages.Proto3 {
           }
           case 146: {
             if (optionalNestedMessage_ == null) {
-              optionalNestedMessage_ = new global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage();
+              OptionalNestedMessage = new global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage();
             }
-            input.ReadMessage(optionalNestedMessage_);
+            input.ReadMessage(OptionalNestedMessage);
             break;
           }
           case 154: {
             if (optionalForeignMessage_ == null) {
-              optionalForeignMessage_ = new global::ProtobufTestMessages.Proto3.ForeignMessage();
+              OptionalForeignMessage = new global::ProtobufTestMessages.Proto3.ForeignMessage();
             }
-            input.ReadMessage(optionalForeignMessage_);
+            input.ReadMessage(OptionalForeignMessage);
             break;
           }
           case 168: {
-            optionalNestedEnum_ = (global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum) input.ReadEnum();
+            OptionalNestedEnum = (global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum) input.ReadEnum();
             break;
           }
           case 176: {
-            optionalForeignEnum_ = (global::ProtobufTestMessages.Proto3.ForeignEnum) input.ReadEnum();
+            OptionalForeignEnum = (global::ProtobufTestMessages.Proto3.ForeignEnum) input.ReadEnum();
             break;
           }
           case 194: {
@@ -2952,9 +2961,9 @@ namespace ProtobufTestMessages.Proto3 {
           }
           case 218: {
             if (recursiveMessage_ == null) {
-              recursiveMessage_ = new global::ProtobufTestMessages.Proto3.TestAllTypesProto3();
+              RecursiveMessage = new global::ProtobufTestMessages.Proto3.TestAllTypesProto3();
             }
-            input.ReadMessage(recursiveMessage_);
+            input.ReadMessage(RecursiveMessage);
             break;
           }
           case 250:
@@ -3275,44 +3284,44 @@ namespace ProtobufTestMessages.Proto3 {
           }
           case 2410: {
             if (optionalDuration_ == null) {
-              optionalDuration_ = new global::Google.Protobuf.WellKnownTypes.Duration();
+              OptionalDuration = new global::Google.Protobuf.WellKnownTypes.Duration();
             }
-            input.ReadMessage(optionalDuration_);
+            input.ReadMessage(OptionalDuration);
             break;
           }
           case 2418: {
             if (optionalTimestamp_ == null) {
-              optionalTimestamp_ = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+              OptionalTimestamp = new global::Google.Protobuf.WellKnownTypes.Timestamp();
             }
-            input.ReadMessage(optionalTimestamp_);
+            input.ReadMessage(OptionalTimestamp);
             break;
           }
           case 2426: {
             if (optionalFieldMask_ == null) {
-              optionalFieldMask_ = new global::Google.Protobuf.WellKnownTypes.FieldMask();
+              OptionalFieldMask = new global::Google.Protobuf.WellKnownTypes.FieldMask();
             }
-            input.ReadMessage(optionalFieldMask_);
+            input.ReadMessage(OptionalFieldMask);
             break;
           }
           case 2434: {
             if (optionalStruct_ == null) {
-              optionalStruct_ = new global::Google.Protobuf.WellKnownTypes.Struct();
+              OptionalStruct = new global::Google.Protobuf.WellKnownTypes.Struct();
             }
-            input.ReadMessage(optionalStruct_);
+            input.ReadMessage(OptionalStruct);
             break;
           }
           case 2442: {
             if (optionalAny_ == null) {
-              optionalAny_ = new global::Google.Protobuf.WellKnownTypes.Any();
+              OptionalAny = new global::Google.Protobuf.WellKnownTypes.Any();
             }
-            input.ReadMessage(optionalAny_);
+            input.ReadMessage(OptionalAny);
             break;
           }
           case 2450: {
             if (optionalValue_ == null) {
-              optionalValue_ = new global::Google.Protobuf.WellKnownTypes.Value();
+              OptionalValue = new global::Google.Protobuf.WellKnownTypes.Value();
             }
-            input.ReadMessage(optionalValue_);
+            input.ReadMessage(OptionalValue);
             break;
           }
           case 2490: {
@@ -3560,7 +3569,7 @@ namespace ProtobufTestMessages.Proto3 {
           }
           if (other.corecursive_ != null) {
             if (corecursive_ == null) {
-              corecursive_ = new global::ProtobufTestMessages.Proto3.TestAllTypesProto3();
+              Corecursive = new global::ProtobufTestMessages.Proto3.TestAllTypesProto3();
             }
             Corecursive.MergeFrom(other.Corecursive);
           }
@@ -3581,9 +3590,9 @@ namespace ProtobufTestMessages.Proto3 {
               }
               case 18: {
                 if (corecursive_ == null) {
-                  corecursive_ = new global::ProtobufTestMessages.Proto3.TestAllTypesProto3();
+                  Corecursive = new global::ProtobufTestMessages.Proto3.TestAllTypesProto3();
                 }
-                input.ReadMessage(corecursive_);
+                input.ReadMessage(Corecursive);
                 break;
               }
             }

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

@@ -1879,7 +1879,7 @@ namespace UnitTest.Issues.TestProtos {
       }
       if (other.bar_ != null) {
         if (bar_ == null) {
-          bar_ = new global::UnitTest.Issues.TestProtos.ComplexOptionType1();
+          Bar = new global::UnitTest.Issues.TestProtos.ComplexOptionType1();
         }
         Bar.MergeFrom(other.Bar);
       }
@@ -1888,7 +1888,7 @@ namespace UnitTest.Issues.TestProtos {
       }
       if (other.fred_ != null) {
         if (fred_ == null) {
-          fred_ = new global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4();
+          Fred = new global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4();
         }
         Fred.MergeFrom(other.Fred);
       }
@@ -1906,9 +1906,9 @@ namespace UnitTest.Issues.TestProtos {
             break;
           case 10: {
             if (bar_ == null) {
-              bar_ = new global::UnitTest.Issues.TestProtos.ComplexOptionType1();
+              Bar = new global::UnitTest.Issues.TestProtos.ComplexOptionType1();
             }
-            input.ReadMessage(bar_);
+            input.ReadMessage(Bar);
             break;
           }
           case 16: {
@@ -1917,9 +1917,9 @@ namespace UnitTest.Issues.TestProtos {
           }
           case 26: {
             if (fred_ == null) {
-              fred_ = new global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4();
+              Fred = new global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4();
             }
-            input.ReadMessage(fred_);
+            input.ReadMessage(Fred);
             break;
           }
           case 34: {
@@ -2462,7 +2462,7 @@ namespace UnitTest.Issues.TestProtos {
       }
       if (other.sub_ != null) {
         if (sub_ == null) {
-          sub_ = new global::UnitTest.Issues.TestProtos.Aggregate();
+          Sub = new global::UnitTest.Issues.TestProtos.Aggregate();
         }
         Sub.MergeFrom(other.Sub);
       }
@@ -2487,9 +2487,9 @@ namespace UnitTest.Issues.TestProtos {
           }
           case 26: {
             if (sub_ == null) {
-              sub_ = new global::UnitTest.Issues.TestProtos.Aggregate();
+              Sub = new global::UnitTest.Issues.TestProtos.Aggregate();
             }
-            input.ReadMessage(sub_);
+            input.ReadMessage(Sub);
             break;
           }
         }

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

@@ -557,7 +557,7 @@ namespace UnitTest.Issues.TestProtos {
             _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
             break;
           case 8: {
-            value_ = (global::UnitTest.Issues.TestProtos.NegativeEnum) input.ReadEnum();
+            Value = (global::UnitTest.Issues.TestProtos.NegativeEnum) input.ReadEnum();
             break;
           }
           case 18:
@@ -881,7 +881,7 @@ namespace UnitTest.Issues.TestProtos {
       primitiveArray_.Add(other.primitiveArray_);
       if (other.messageValue_ != null) {
         if (messageValue_ == null) {
-          messageValue_ = new global::UnitTest.Issues.TestProtos.DeprecatedChild();
+          MessageValue = new global::UnitTest.Issues.TestProtos.DeprecatedChild();
         }
         MessageValue.MergeFrom(other.MessageValue);
       }
@@ -912,9 +912,9 @@ namespace UnitTest.Issues.TestProtos {
           }
           case 26: {
             if (messageValue_ == null) {
-              messageValue_ = new global::UnitTest.Issues.TestProtos.DeprecatedChild();
+              MessageValue = new global::UnitTest.Issues.TestProtos.DeprecatedChild();
             }
-            input.ReadMessage(messageValue_);
+            input.ReadMessage(MessageValue);
             break;
           }
           case 34: {
@@ -922,7 +922,7 @@ namespace UnitTest.Issues.TestProtos {
             break;
           }
           case 40: {
-            enumValue_ = (global::UnitTest.Issues.TestProtos.DeprecatedEnum) input.ReadEnum();
+            EnumValue = (global::UnitTest.Issues.TestProtos.DeprecatedEnum) input.ReadEnum();
             break;
           }
           case 50:

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

@@ -1307,19 +1307,19 @@ namespace Google.Protobuf.TestProtos {
       }
       if (other.singleNestedMessage_ != null) {
         if (singleNestedMessage_ == null) {
-          singleNestedMessage_ = new global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage();
+          SingleNestedMessage = new global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage();
         }
         SingleNestedMessage.MergeFrom(other.SingleNestedMessage);
       }
       if (other.singleForeignMessage_ != null) {
         if (singleForeignMessage_ == null) {
-          singleForeignMessage_ = new global::Google.Protobuf.TestProtos.ForeignMessage();
+          SingleForeignMessage = new global::Google.Protobuf.TestProtos.ForeignMessage();
         }
         SingleForeignMessage.MergeFrom(other.SingleForeignMessage);
       }
       if (other.singleImportMessage_ != null) {
         if (singleImportMessage_ == null) {
-          singleImportMessage_ = new global::Google.Protobuf.TestProtos.ImportMessage();
+          SingleImportMessage = new global::Google.Protobuf.TestProtos.ImportMessage();
         }
         SingleImportMessage.MergeFrom(other.SingleImportMessage);
       }
@@ -1334,7 +1334,7 @@ namespace Google.Protobuf.TestProtos {
       }
       if (other.singlePublicImportMessage_ != null) {
         if (singlePublicImportMessage_ == null) {
-          singlePublicImportMessage_ = new global::Google.Protobuf.TestProtos.PublicImportMessage();
+          SinglePublicImportMessage = new global::Google.Protobuf.TestProtos.PublicImportMessage();
         }
         SinglePublicImportMessage.MergeFrom(other.SinglePublicImportMessage);
       }
@@ -1451,42 +1451,42 @@ namespace Google.Protobuf.TestProtos {
           }
           case 146: {
             if (singleNestedMessage_ == null) {
-              singleNestedMessage_ = new global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage();
+              SingleNestedMessage = new global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage();
             }
-            input.ReadMessage(singleNestedMessage_);
+            input.ReadMessage(SingleNestedMessage);
             break;
           }
           case 154: {
             if (singleForeignMessage_ == null) {
-              singleForeignMessage_ = new global::Google.Protobuf.TestProtos.ForeignMessage();
+              SingleForeignMessage = new global::Google.Protobuf.TestProtos.ForeignMessage();
             }
-            input.ReadMessage(singleForeignMessage_);
+            input.ReadMessage(SingleForeignMessage);
             break;
           }
           case 162: {
             if (singleImportMessage_ == null) {
-              singleImportMessage_ = new global::Google.Protobuf.TestProtos.ImportMessage();
+              SingleImportMessage = new global::Google.Protobuf.TestProtos.ImportMessage();
             }
-            input.ReadMessage(singleImportMessage_);
+            input.ReadMessage(SingleImportMessage);
             break;
           }
           case 168: {
-            singleNestedEnum_ = (global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum) input.ReadEnum();
+            SingleNestedEnum = (global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum) input.ReadEnum();
             break;
           }
           case 176: {
-            singleForeignEnum_ = (global::Google.Protobuf.TestProtos.ForeignEnum) input.ReadEnum();
+            SingleForeignEnum = (global::Google.Protobuf.TestProtos.ForeignEnum) input.ReadEnum();
             break;
           }
           case 184: {
-            singleImportEnum_ = (global::Google.Protobuf.TestProtos.ImportEnum) input.ReadEnum();
+            SingleImportEnum = (global::Google.Protobuf.TestProtos.ImportEnum) input.ReadEnum();
             break;
           }
           case 210: {
             if (singlePublicImportMessage_ == null) {
-              singlePublicImportMessage_ = new global::Google.Protobuf.TestProtos.PublicImportMessage();
+              SinglePublicImportMessage = new global::Google.Protobuf.TestProtos.PublicImportMessage();
             }
-            input.ReadMessage(singlePublicImportMessage_);
+            input.ReadMessage(SinglePublicImportMessage);
             break;
           }
           case 250:
@@ -1918,13 +1918,13 @@ namespace Google.Protobuf.TestProtos {
       }
       if (other.child_ != null) {
         if (child_ == null) {
-          child_ = new global::Google.Protobuf.TestProtos.NestedTestAllTypes();
+          Child = new global::Google.Protobuf.TestProtos.NestedTestAllTypes();
         }
         Child.MergeFrom(other.Child);
       }
       if (other.payload_ != null) {
         if (payload_ == null) {
-          payload_ = new global::Google.Protobuf.TestProtos.TestAllTypes();
+          Payload = new global::Google.Protobuf.TestProtos.TestAllTypes();
         }
         Payload.MergeFrom(other.Payload);
       }
@@ -1942,16 +1942,16 @@ namespace Google.Protobuf.TestProtos {
             break;
           case 10: {
             if (child_ == null) {
-              child_ = new global::Google.Protobuf.TestProtos.NestedTestAllTypes();
+              Child = new global::Google.Protobuf.TestProtos.NestedTestAllTypes();
             }
-            input.ReadMessage(child_);
+            input.ReadMessage(Child);
             break;
           }
           case 18: {
             if (payload_ == null) {
-              payload_ = new global::Google.Protobuf.TestProtos.TestAllTypes();
+              Payload = new global::Google.Protobuf.TestProtos.TestAllTypes();
             }
-            input.ReadMessage(payload_);
+            input.ReadMessage(Payload);
             break;
           }
           case 26: {
@@ -2438,7 +2438,7 @@ namespace Google.Protobuf.TestProtos {
       }
       if (other.foreignNested_ != null) {
         if (foreignNested_ == null) {
-          foreignNested_ = new global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage();
+          ForeignNested = new global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage();
         }
         ForeignNested.MergeFrom(other.ForeignNested);
       }
@@ -2455,9 +2455,9 @@ namespace Google.Protobuf.TestProtos {
             break;
           case 10: {
             if (foreignNested_ == null) {
-              foreignNested_ = new global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage();
+              ForeignNested = new global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage();
             }
-            input.ReadMessage(foreignNested_);
+            input.ReadMessage(ForeignNested);
             break;
           }
         }
@@ -2758,7 +2758,7 @@ namespace Google.Protobuf.TestProtos {
       }
       if (other.a_ != null) {
         if (a_ == null) {
-          a_ = new global::Google.Protobuf.TestProtos.TestRecursiveMessage();
+          A = new global::Google.Protobuf.TestProtos.TestRecursiveMessage();
         }
         A.MergeFrom(other.A);
       }
@@ -2778,9 +2778,9 @@ namespace Google.Protobuf.TestProtos {
             break;
           case 10: {
             if (a_ == null) {
-              a_ = new global::Google.Protobuf.TestProtos.TestRecursiveMessage();
+              A = new global::Google.Protobuf.TestProtos.TestRecursiveMessage();
             }
-            input.ReadMessage(a_);
+            input.ReadMessage(A);
             break;
           }
           case 16: {
@@ -2903,7 +2903,7 @@ namespace Google.Protobuf.TestProtos {
       }
       if (other.bb_ != null) {
         if (bb_ == null) {
-          bb_ = new global::Google.Protobuf.TestProtos.TestMutualRecursionB();
+          Bb = new global::Google.Protobuf.TestProtos.TestMutualRecursionB();
         }
         Bb.MergeFrom(other.Bb);
       }
@@ -2920,9 +2920,9 @@ namespace Google.Protobuf.TestProtos {
             break;
           case 10: {
             if (bb_ == null) {
-              bb_ = new global::Google.Protobuf.TestProtos.TestMutualRecursionB();
+              Bb = new global::Google.Protobuf.TestProtos.TestMutualRecursionB();
             }
-            input.ReadMessage(bb_);
+            input.ReadMessage(Bb);
             break;
           }
         }
@@ -3059,7 +3059,7 @@ namespace Google.Protobuf.TestProtos {
       }
       if (other.a_ != null) {
         if (a_ == null) {
-          a_ = new global::Google.Protobuf.TestProtos.TestMutualRecursionA();
+          A = new global::Google.Protobuf.TestProtos.TestMutualRecursionA();
         }
         A.MergeFrom(other.A);
       }
@@ -3079,9 +3079,9 @@ namespace Google.Protobuf.TestProtos {
             break;
           case 10: {
             if (a_ == null) {
-              a_ = new global::Google.Protobuf.TestProtos.TestMutualRecursionA();
+              A = new global::Google.Protobuf.TestProtos.TestMutualRecursionA();
             }
-            input.ReadMessage(a_);
+            input.ReadMessage(A);
             break;
           }
           case 16: {
@@ -3214,7 +3214,7 @@ namespace Google.Protobuf.TestProtos {
             _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
             break;
           case 8: {
-            value_ = (global::Google.Protobuf.TestProtos.TestEnumWithDupValue) input.ReadEnum();
+            Value = (global::Google.Protobuf.TestProtos.TestEnumWithDupValue) input.ReadEnum();
             break;
           }
         }
@@ -3466,7 +3466,7 @@ namespace Google.Protobuf.TestProtos {
       }
       if (other.messageField_ != null) {
         if (messageField_ == null) {
-          messageField_ = new global::Google.Protobuf.TestProtos.ForeignMessage();
+          MessageField = new global::Google.Protobuf.TestProtos.ForeignMessage();
         }
         MessageField.MergeFrom(other.MessageField);
       }
@@ -3494,14 +3494,14 @@ namespace Google.Protobuf.TestProtos {
             break;
           }
           case 24: {
-            enumField_ = (global::Google.Protobuf.TestProtos.ForeignEnum) input.ReadEnum();
+            EnumField = (global::Google.Protobuf.TestProtos.ForeignEnum) input.ReadEnum();
             break;
           }
           case 34: {
             if (messageField_ == null) {
-              messageField_ = new global::Google.Protobuf.TestProtos.ForeignMessage();
+              MessageField = new global::Google.Protobuf.TestProtos.ForeignMessage();
             }
-            input.ReadMessage(messageField_);
+            input.ReadMessage(MessageField);
             break;
           }
           case 58:
@@ -3711,7 +3711,7 @@ namespace Google.Protobuf.TestProtos {
       }
       if (other.singleNestedMessage_ != null) {
         if (singleNestedMessage_ == null) {
-          singleNestedMessage_ = new global::Google.Protobuf.TestProtos.TestFieldOrderings.Types.NestedMessage();
+          SingleNestedMessage = new global::Google.Protobuf.TestProtos.TestFieldOrderings.Types.NestedMessage();
         }
         SingleNestedMessage.MergeFrom(other.SingleNestedMessage);
       }
@@ -3740,9 +3740,9 @@ namespace Google.Protobuf.TestProtos {
           }
           case 1602: {
             if (singleNestedMessage_ == null) {
-              singleNestedMessage_ = new global::Google.Protobuf.TestProtos.TestFieldOrderings.Types.NestedMessage();
+              SingleNestedMessage = new global::Google.Protobuf.TestProtos.TestFieldOrderings.Types.NestedMessage();
             }
-            input.ReadMessage(singleNestedMessage_);
+            input.ReadMessage(SingleNestedMessage);
             break;
           }
         }
@@ -4040,7 +4040,7 @@ namespace Google.Protobuf.TestProtos {
             _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
             break;
           case 8: {
-            sparseEnum_ = (global::Google.Protobuf.TestProtos.TestSparseEnum) input.ReadEnum();
+            SparseEnum = (global::Google.Protobuf.TestProtos.TestSparseEnum) input.ReadEnum();
             break;
           }
         }

+ 39 - 30
csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs

@@ -342,6 +342,7 @@ namespace Google.Protobuf.TestProtos {
       }
     }
 
+
     /// <summary>Field number for the "float_field" field.</summary>
     public const int FloatFieldFieldNumber = 11;
     private static readonly pb::FieldCodec<float?> _single_floatField_codec = pb::FieldCodec.ForStructWrapper<float>(90);
@@ -354,6 +355,7 @@ namespace Google.Protobuf.TestProtos {
       }
     }
 
+
     /// <summary>Field number for the "int64_field" field.</summary>
     public const int Int64FieldFieldNumber = 12;
     private static readonly pb::FieldCodec<long?> _single_int64Field_codec = pb::FieldCodec.ForStructWrapper<long>(98);
@@ -366,6 +368,7 @@ namespace Google.Protobuf.TestProtos {
       }
     }
 
+
     /// <summary>Field number for the "uint64_field" field.</summary>
     public const int Uint64FieldFieldNumber = 13;
     private static readonly pb::FieldCodec<ulong?> _single_uint64Field_codec = pb::FieldCodec.ForStructWrapper<ulong>(106);
@@ -378,6 +381,7 @@ namespace Google.Protobuf.TestProtos {
       }
     }
 
+
     /// <summary>Field number for the "int32_field" field.</summary>
     public const int Int32FieldFieldNumber = 14;
     private static readonly pb::FieldCodec<int?> _single_int32Field_codec = pb::FieldCodec.ForStructWrapper<int>(114);
@@ -390,6 +394,7 @@ namespace Google.Protobuf.TestProtos {
       }
     }
 
+
     /// <summary>Field number for the "uint32_field" field.</summary>
     public const int Uint32FieldFieldNumber = 15;
     private static readonly pb::FieldCodec<uint?> _single_uint32Field_codec = pb::FieldCodec.ForStructWrapper<uint>(122);
@@ -402,6 +407,7 @@ namespace Google.Protobuf.TestProtos {
       }
     }
 
+
     /// <summary>Field number for the "bool_field" field.</summary>
     public const int BoolFieldFieldNumber = 16;
     private static readonly pb::FieldCodec<bool?> _single_boolField_codec = pb::FieldCodec.ForStructWrapper<bool>(130);
@@ -414,6 +420,7 @@ namespace Google.Protobuf.TestProtos {
       }
     }
 
+
     /// <summary>Field number for the "string_field" field.</summary>
     public const int StringFieldFieldNumber = 17;
     private static readonly pb::FieldCodec<string> _single_stringField_codec = pb::FieldCodec.ForClassWrapper<string>(138);
@@ -426,6 +433,7 @@ namespace Google.Protobuf.TestProtos {
       }
     }
 
+
     /// <summary>Field number for the "bytes_field" field.</summary>
     public const int BytesFieldFieldNumber = 18;
     private static readonly pb::FieldCodec<pb::ByteString> _single_bytesField_codec = pb::FieldCodec.ForClassWrapper<pb::ByteString>(146);
@@ -438,6 +446,7 @@ namespace Google.Protobuf.TestProtos {
       }
     }
 
+
     /// <summary>Field number for the "value_field" field.</summary>
     public const int ValueFieldFieldNumber = 19;
     private global::Google.Protobuf.WellKnownTypes.Value valueField_;
@@ -667,55 +676,55 @@ namespace Google.Protobuf.TestProtos {
       }
       if (other.anyField_ != null) {
         if (anyField_ == null) {
-          anyField_ = new global::Google.Protobuf.WellKnownTypes.Any();
+          AnyField = new global::Google.Protobuf.WellKnownTypes.Any();
         }
         AnyField.MergeFrom(other.AnyField);
       }
       if (other.apiField_ != null) {
         if (apiField_ == null) {
-          apiField_ = new global::Google.Protobuf.WellKnownTypes.Api();
+          ApiField = new global::Google.Protobuf.WellKnownTypes.Api();
         }
         ApiField.MergeFrom(other.ApiField);
       }
       if (other.durationField_ != null) {
         if (durationField_ == null) {
-          durationField_ = new global::Google.Protobuf.WellKnownTypes.Duration();
+          DurationField = new global::Google.Protobuf.WellKnownTypes.Duration();
         }
         DurationField.MergeFrom(other.DurationField);
       }
       if (other.emptyField_ != null) {
         if (emptyField_ == null) {
-          emptyField_ = new global::Google.Protobuf.WellKnownTypes.Empty();
+          EmptyField = new global::Google.Protobuf.WellKnownTypes.Empty();
         }
         EmptyField.MergeFrom(other.EmptyField);
       }
       if (other.fieldMaskField_ != null) {
         if (fieldMaskField_ == null) {
-          fieldMaskField_ = new global::Google.Protobuf.WellKnownTypes.FieldMask();
+          FieldMaskField = new global::Google.Protobuf.WellKnownTypes.FieldMask();
         }
         FieldMaskField.MergeFrom(other.FieldMaskField);
       }
       if (other.sourceContextField_ != null) {
         if (sourceContextField_ == null) {
-          sourceContextField_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
+          SourceContextField = new global::Google.Protobuf.WellKnownTypes.SourceContext();
         }
         SourceContextField.MergeFrom(other.SourceContextField);
       }
       if (other.structField_ != null) {
         if (structField_ == null) {
-          structField_ = new global::Google.Protobuf.WellKnownTypes.Struct();
+          StructField = new global::Google.Protobuf.WellKnownTypes.Struct();
         }
         StructField.MergeFrom(other.StructField);
       }
       if (other.timestampField_ != null) {
         if (timestampField_ == null) {
-          timestampField_ = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+          TimestampField = new global::Google.Protobuf.WellKnownTypes.Timestamp();
         }
         TimestampField.MergeFrom(other.TimestampField);
       }
       if (other.typeField_ != null) {
         if (typeField_ == null) {
-          typeField_ = new global::Google.Protobuf.WellKnownTypes.Type();
+          TypeField = new global::Google.Protobuf.WellKnownTypes.Type();
         }
         TypeField.MergeFrom(other.TypeField);
       }
@@ -766,7 +775,7 @@ namespace Google.Protobuf.TestProtos {
       }
       if (other.valueField_ != null) {
         if (valueField_ == null) {
-          valueField_ = new global::Google.Protobuf.WellKnownTypes.Value();
+          ValueField = new global::Google.Protobuf.WellKnownTypes.Value();
         }
         ValueField.MergeFrom(other.ValueField);
       }
@@ -783,65 +792,65 @@ namespace Google.Protobuf.TestProtos {
             break;
           case 10: {
             if (anyField_ == null) {
-              anyField_ = new global::Google.Protobuf.WellKnownTypes.Any();
+              AnyField = new global::Google.Protobuf.WellKnownTypes.Any();
             }
-            input.ReadMessage(anyField_);
+            input.ReadMessage(AnyField);
             break;
           }
           case 18: {
             if (apiField_ == null) {
-              apiField_ = new global::Google.Protobuf.WellKnownTypes.Api();
+              ApiField = new global::Google.Protobuf.WellKnownTypes.Api();
             }
-            input.ReadMessage(apiField_);
+            input.ReadMessage(ApiField);
             break;
           }
           case 26: {
             if (durationField_ == null) {
-              durationField_ = new global::Google.Protobuf.WellKnownTypes.Duration();
+              DurationField = new global::Google.Protobuf.WellKnownTypes.Duration();
             }
-            input.ReadMessage(durationField_);
+            input.ReadMessage(DurationField);
             break;
           }
           case 34: {
             if (emptyField_ == null) {
-              emptyField_ = new global::Google.Protobuf.WellKnownTypes.Empty();
+              EmptyField = new global::Google.Protobuf.WellKnownTypes.Empty();
             }
-            input.ReadMessage(emptyField_);
+            input.ReadMessage(EmptyField);
             break;
           }
           case 42: {
             if (fieldMaskField_ == null) {
-              fieldMaskField_ = new global::Google.Protobuf.WellKnownTypes.FieldMask();
+              FieldMaskField = new global::Google.Protobuf.WellKnownTypes.FieldMask();
             }
-            input.ReadMessage(fieldMaskField_);
+            input.ReadMessage(FieldMaskField);
             break;
           }
           case 50: {
             if (sourceContextField_ == null) {
-              sourceContextField_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
+              SourceContextField = new global::Google.Protobuf.WellKnownTypes.SourceContext();
             }
-            input.ReadMessage(sourceContextField_);
+            input.ReadMessage(SourceContextField);
             break;
           }
           case 58: {
             if (structField_ == null) {
-              structField_ = new global::Google.Protobuf.WellKnownTypes.Struct();
+              StructField = new global::Google.Protobuf.WellKnownTypes.Struct();
             }
-            input.ReadMessage(structField_);
+            input.ReadMessage(StructField);
             break;
           }
           case 66: {
             if (timestampField_ == null) {
-              timestampField_ = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+              TimestampField = new global::Google.Protobuf.WellKnownTypes.Timestamp();
             }
-            input.ReadMessage(timestampField_);
+            input.ReadMessage(TimestampField);
             break;
           }
           case 74: {
             if (typeField_ == null) {
-              typeField_ = new global::Google.Protobuf.WellKnownTypes.Type();
+              TypeField = new global::Google.Protobuf.WellKnownTypes.Type();
             }
-            input.ReadMessage(typeField_);
+            input.ReadMessage(TypeField);
             break;
           }
           case 82: {
@@ -909,9 +918,9 @@ namespace Google.Protobuf.TestProtos {
           }
           case 154: {
             if (valueField_ == null) {
-              valueField_ = new global::Google.Protobuf.WellKnownTypes.Value();
+              ValueField = new global::Google.Protobuf.WellKnownTypes.Value();
             }
-            input.ReadMessage(valueField_);
+            input.ReadMessage(ValueField);
             break;
           }
         }

+ 771 - 771
csharp/src/Google.Protobuf/Collections/MapField.cs

@@ -1,771 +1,771 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2015 Google Inc.  All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#endregion
-
-using Google.Protobuf.Compatibility;
-using Google.Protobuf.Reflection;
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace Google.Protobuf.Collections
-{
-    /// <summary>
-    /// Representation of a map field in a Protocol Buffer message.
-    /// </summary>
-    /// <typeparam name="TKey">Key type in the map. Must be a type supported by Protocol Buffer map keys.</typeparam>
-    /// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam>
-    /// <remarks>
-    /// <para>
-    /// For string keys, the equality comparison is provided by <see cref="StringComparer.Ordinal" />.
-    /// </para>
-    /// <para>
-    /// Null values are not permitted in the map, either for wrapper types or regular messages.
-    /// If a map is deserialized from a data stream and the value is missing from an entry, a default value
-    /// is created instead. For primitive types, that is the regular default value (0, the empty string and so
-    /// on); for message types, an empty instance of the message is created, as if the map entry contained a 0-length
-    /// encoded value for the field.
-    /// </para>
-    /// <para>
-    /// This implementation does not generally prohibit the use of key/value types which are not
-    /// supported by Protocol Buffers (e.g. using a key type of <code>byte</code>) but nor does it guarantee
-    /// that all operations will work in such cases.
-    /// </para>
-    /// <para>
-    /// The order in which entries are returned when iterating over this object is undefined, and may change
-    /// in future versions.
-    /// </para>
-    /// </remarks>
-    public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary
-#if !NET35
-        , IReadOnlyDictionary<TKey, TValue>
-#endif
-    {
-        private static readonly EqualityComparer<TValue> ValueEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TValue>();
-        private static readonly EqualityComparer<TKey> KeyEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TKey>();
-
-        // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
-        private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> map =
-            new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>(KeyEqualityComparer);
-        private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new LinkedList<KeyValuePair<TKey, TValue>>();
-
-        /// <summary>
-        /// Creates a deep clone of this object.
-        /// </summary>
-        /// <returns>
-        /// A deep clone of this object.
-        /// </returns>
-        public MapField<TKey, TValue> Clone()
-        {
-            var clone = new MapField<TKey, TValue>();
-            // Keys are never cloneable. Values might be.
-            if (typeof(IDeepCloneable<TValue>).IsAssignableFrom(typeof(TValue)))
-            {
-                foreach (var pair in list)
-                {
-                    clone.Add(pair.Key, ((IDeepCloneable<TValue>)pair.Value).Clone());
-                }
-            }
-            else
-            {
-                // Nothing is cloneable, so we don't need to worry.
-                clone.Add(this);
-            }
-            return clone;
-        }
-
-        /// <summary>
-        /// Adds the specified key/value pair to the map.
-        /// </summary>
-        /// <remarks>
-        /// This operation fails if the key already exists in the map. To replace an existing entry, use the indexer.
-        /// </remarks>
-        /// <param name="key">The key to add</param>
-        /// <param name="value">The value to add.</param>
-        /// <exception cref="System.ArgumentException">The given key already exists in map.</exception>
-        public void Add(TKey key, TValue value)
-        {
-            // Validation of arguments happens in ContainsKey and the indexer
-            if (ContainsKey(key))
-            {
-                throw new ArgumentException("Key already exists in map", nameof(key));
-            }
-            this[key] = value;
-        }
-
-        /// <summary>
-        /// Determines whether the specified key is present in the map.
-        /// </summary>
-        /// <param name="key">The key to check.</param>
-        /// <returns><c>true</c> if the map contains the given key; <c>false</c> otherwise.</returns>
-        public bool ContainsKey(TKey key)
-        {
-            ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
-            return map.ContainsKey(key);
-        }
-
-        private bool ContainsValue(TValue value) =>
-            list.Any(pair => ValueEqualityComparer.Equals(pair.Value, value));
-
-        /// <summary>
-        /// Removes the entry identified by the given key from the map.
-        /// </summary>
-        /// <param name="key">The key indicating the entry to remove from the map.</param>
-        /// <returns><c>true</c> if the map contained the given key before the entry was removed; <c>false</c> otherwise.</returns>
-        public bool Remove(TKey key)
-        {
-            ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (map.TryGetValue(key, out node))
-            {
-                map.Remove(key);
-                node.List.Remove(node);
-                return true;
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        /// <summary>
-        /// Gets the value associated with the specified key.
-        /// </summary>
-        /// <param name="key">The key whose value to get.</param>
-        /// <param name="value">When this method returns, the value associated with the specified key, if the key is found;
-        /// otherwise, the default value for the type of the <paramref name="value"/> parameter.
-        /// This parameter is passed uninitialized.</param>
-        /// <returns><c>true</c> if the map contains an element with the specified key; otherwise, <c>false</c>.</returns>
-        public bool TryGetValue(TKey key, out TValue value)
-        {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (map.TryGetValue(key, out node))
-            {
-                value = node.Value.Value;
-                return true;
-            }
-            else
-            {
-                value = default(TValue);
-                return false;
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets the value associated with the specified key.
-        /// </summary>
-        /// <param name="key">The key of the value to get or set.</param>
-        /// <exception cref="KeyNotFoundException">The property is retrieved and key does not exist in the collection.</exception>
-        /// <returns>The value associated with the specified key. If the specified key is not found,
-        /// a get operation throws a <see cref="KeyNotFoundException"/>, and a set operation creates a new element with the specified key.</returns>
-        public TValue this[TKey key]
-        {
-            get
-            {
-                ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
-                TValue value;
-                if (TryGetValue(key, out value))
-                {
-                    return value;
-                }
-                throw new KeyNotFoundException();
-            }
-            set
-            {
-                ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
-                // value == null check here is redundant, but avoids boxing.
-                if (value == null)
-                {
-                    ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
-                }
-                LinkedListNode<KeyValuePair<TKey, TValue>> node;
-                var pair = new KeyValuePair<TKey, TValue>(key, value);
-                if (map.TryGetValue(key, out node))
-                {
-                    node.Value = pair;
-                }
-                else
-                {
-                    node = list.AddLast(pair);
-                    map[key] = node;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Gets a collection containing the keys in the map.
-        /// </summary>
-        public ICollection<TKey> Keys { get { return new MapView<TKey>(this, pair => pair.Key, ContainsKey); } }
-
-        /// <summary>
-        /// Gets a collection containing the values in the map.
-        /// </summary>
-        public ICollection<TValue> Values { get { return new MapView<TValue>(this, pair => pair.Value, ContainsValue); } }
-
-        /// <summary>
-        /// Adds the specified entries to the map. The keys and values are not automatically cloned.
-        /// </summary>
-        /// <param name="entries">The entries to add to the map.</param>
-        public void Add(IDictionary<TKey, TValue> entries)
-        {
-            ProtoPreconditions.CheckNotNull(entries, nameof(entries));
-            foreach (var pair in entries)
-            {
-                Add(pair.Key, pair.Value);
-            }
-        }
-
-        /// <summary>
-        /// Returns an enumerator that iterates through the collection.
-        /// </summary>
-        /// <returns>
-        /// An enumerator that can be used to iterate through the collection.
-        /// </returns>
-        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
-        {
-            return list.GetEnumerator();
-        }
-
-        /// <summary>
-        /// Returns an enumerator that iterates through a collection.
-        /// </summary>
-        /// <returns>
-        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
-        /// </returns>
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return GetEnumerator();
-        }
-
-        /// <summary>
-        /// Adds the specified item to the map.
-        /// </summary>
-        /// <param name="item">The item to add to the map.</param>
-        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
-        {
-            Add(item.Key, item.Value);
-        }
-
-        /// <summary>
-        /// Removes all items from the map.
-        /// </summary>
-        public void Clear()
-        {
-            list.Clear();
-            map.Clear();
-        }
-
-        /// <summary>
-        /// Determines whether map contains an entry equivalent to the given key/value pair.
-        /// </summary>
-        /// <param name="item">The key/value pair to find.</param>
-        /// <returns></returns>
-        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
-        {
-            TValue value;
-            return TryGetValue(item.Key, out value) && ValueEqualityComparer.Equals(item.Value, value);
-        }
-
-        /// <summary>
-        /// Copies the key/value pairs in this map to an array.
-        /// </summary>
-        /// <param name="array">The array to copy the entries into.</param>
-        /// <param name="arrayIndex">The index of the array at which to start copying values.</param>
-        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
-        {
-            list.CopyTo(array, arrayIndex);
-        }
-
-        /// <summary>
-        /// Removes the specified key/value pair from the map.
-        /// </summary>
-        /// <remarks>Both the key and the value must be found for the entry to be removed.</remarks>
-        /// <param name="item">The key/value pair to remove.</param>
-        /// <returns><c>true</c> if the key/value pair was found and removed; <c>false</c> otherwise.</returns>
-        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
-        {
-            if (item.Key == null)
-            {
-                throw new ArgumentException("Key is null", nameof(item));
-            }
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (map.TryGetValue(item.Key, out node) &&
-                EqualityComparer<TValue>.Default.Equals(item.Value, node.Value.Value))
-            {
-                map.Remove(item.Key);
-                node.List.Remove(node);
-                return true;
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        /// <summary>
-        /// Gets the number of elements contained in the map.
-        /// </summary>
-        public int Count { get { return list.Count; } }
-
-        /// <summary>
-        /// Gets a value indicating whether the map is read-only.
-        /// </summary>
-        public bool IsReadOnly { get { return false; } }
-
-        /// <summary>
-        /// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
-        /// </summary>
-        /// <param name="other">The <see cref="System.Object" /> to compare with this instance.</param>
-        /// <returns>
-        ///   <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
-        /// </returns>
-        public override bool Equals(object other)
-        {
-            return Equals(other as MapField<TKey, TValue>);
-        }
-
-        /// <summary>
-        /// Returns a hash code for this instance.
-        /// </summary>
-        /// <returns>
-        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
-        /// </returns>
-        public override int GetHashCode()
-        {
-            var keyComparer = KeyEqualityComparer;
-            var valueComparer = ValueEqualityComparer;
-            int hash = 0;
-            foreach (var pair in list)
-            {
-                hash ^= keyComparer.GetHashCode(pair.Key) * 31 + valueComparer.GetHashCode(pair.Value);
-            }
-            return hash;
-        }
-
-        /// <summary>
-        /// Compares this map with another for equality.
-        /// </summary>
-        /// <remarks>
-        /// The order of the key/value pairs in the maps is not deemed significant in this comparison.
-        /// </remarks>
-        /// <param name="other">The map to compare this with.</param>
-        /// <returns><c>true</c> if <paramref name="other"/> refers to an equal map; <c>false</c> otherwise.</returns>
-        public bool Equals(MapField<TKey, TValue> other)
-        {
-            if (other == null)
-            {
-                return false;
-            }
-            if (other == this)
-            {
-                return true;
-            }
-            if (other.Count != this.Count)
-            {
-                return false;
-            }
-            var valueComparer = ValueEqualityComparer;
-            foreach (var pair in this)
-            {
-                TValue value;
-                if (!other.TryGetValue(pair.Key, out value))
-                {
-                    return false;
-                }
-                if (!valueComparer.Equals(value, pair.Value))
-                {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        /// <summary>
-        /// Adds entries to the map from the given stream.
-        /// </summary>
-        /// <remarks>
-        /// It is assumed that the stream is initially positioned after the tag specified by the codec.
-        /// This method will continue reading entries from the stream until the end is reached, or
-        /// a different tag is encountered.
-        /// </remarks>
-        /// <param name="input">Stream to read from</param>
-        /// <param name="codec">Codec describing how the key/value pairs are encoded</param>
-        public void AddEntriesFrom(CodedInputStream input, Codec codec)
-        {
-            var adapter = new Codec.MessageAdapter(codec);
-            do
-            {
-                adapter.Reset();
-                input.ReadMessage(adapter);
-                this[adapter.Key] = adapter.Value;
-            } while (input.MaybeConsumeTag(codec.MapTag));
-        }
-
-        /// <summary>
-        /// Writes the contents of this map to the given coded output stream, using the specified codec
-        /// to encode each entry.
-        /// </summary>
-        /// <param name="output">The output stream to write to.</param>
-        /// <param name="codec">The codec to use for each entry.</param>
-        public void WriteTo(CodedOutputStream output, Codec codec)
-        {
-            var message = new Codec.MessageAdapter(codec);
-            foreach (var entry in list)
-            {
-                message.Key = entry.Key;
-                message.Value = entry.Value;
-                output.WriteTag(codec.MapTag);
-                output.WriteMessage(message);
-            }
-        }
-
-        /// <summary>
-        /// Calculates the size of this map based on the given entry codec.
-        /// </summary>
-        /// <param name="codec">The codec to use to encode each entry.</param>
-        /// <returns></returns>
-        public int CalculateSize(Codec codec)
-        {
-            if (Count == 0)
-            {
-                return 0;
-            }
-            var message = new Codec.MessageAdapter(codec);
-            int size = 0;
-            foreach (var entry in list)
-            {
-                message.Key = entry.Key;
-                message.Value = entry.Value;
-                size += CodedOutputStream.ComputeRawVarint32Size(codec.MapTag);
-                size += CodedOutputStream.ComputeMessageSize(message);
-            }
-            return size;
-        }
-
-        /// <summary>
-        /// Returns a string representation of this repeated field, in the same
-        /// way as it would be represented by the default JSON formatter.
-        /// </summary>
-        public override string ToString()
-        {
-            var writer = new StringWriter();
-            JsonFormatter.Default.WriteDictionary(writer, this);
-            return writer.ToString();
-        }
-
-        #region IDictionary explicit interface implementation
-        void IDictionary.Add(object key, object value)
-        {
-            Add((TKey)key, (TValue)value);
-        }
-
-        bool IDictionary.Contains(object key)
-        {
-            if (!(key is TKey))
-            {
-                return false;
-            }
-            return ContainsKey((TKey)key);
-        }
-
-        IDictionaryEnumerator IDictionary.GetEnumerator()
-        {
-            return new DictionaryEnumerator(GetEnumerator());
-        }
-
-        void IDictionary.Remove(object key)
-        {
-            ProtoPreconditions.CheckNotNull(key, nameof(key));
-            if (!(key is TKey))
-            {
-                return;
-            }
-            Remove((TKey)key);
-        }
-
-        void ICollection.CopyTo(Array array, int index)
-        {
-            // This is ugly and slow as heck, but with any luck it will never be used anyway.
-            ICollection temp = this.Select(pair => new DictionaryEntry(pair.Key, pair.Value)).ToList();
-            temp.CopyTo(array, index);
-        }
-
-        bool IDictionary.IsFixedSize { get { return false; } }
-
-        ICollection IDictionary.Keys { get { return (ICollection)Keys; } }
-
-        ICollection IDictionary.Values { get { return (ICollection)Values; } }
-
-        bool ICollection.IsSynchronized { get { return false; } }
-
-        object ICollection.SyncRoot { get { return this; } }
-
-        object IDictionary.this[object key]
-        {
-            get
-            {
-                ProtoPreconditions.CheckNotNull(key, nameof(key));
-                if (!(key is TKey))
-                {
-                    return null;
-                }
-                TValue value;
-                TryGetValue((TKey)key, out value);
-                return value;
-            }
-
-            set
-            {
-                this[(TKey)key] = (TValue)value;
-            }
-        }
-        #endregion
-
-        #region IReadOnlyDictionary explicit interface implementation
-#if !NET35
-        IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys;
-
-        IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
-#endif
-        #endregion
-
-        private class DictionaryEnumerator : IDictionaryEnumerator
-        {
-            private readonly IEnumerator<KeyValuePair<TKey, TValue>> enumerator;
-
-            internal DictionaryEnumerator(IEnumerator<KeyValuePair<TKey, TValue>> enumerator)
-            {
-                this.enumerator = enumerator;
-            }
-
-            public bool MoveNext()
-            {
-                return enumerator.MoveNext();
-            }
-
-            public void Reset()
-            {
-                enumerator.Reset();
-            }
-
-            public object Current { get { return Entry; } }
-            public DictionaryEntry Entry { get { return new DictionaryEntry(Key, Value); } }
-            public object Key { get { return enumerator.Current.Key; } }
-            public object Value { get { return enumerator.Current.Value; } }
-        }
-
-        /// <summary>
-        /// A codec for a specific map field. This contains all the information required to encode and
-        /// decode the nested messages.
-        /// </summary>
-        public sealed class Codec
-        {
-            private readonly FieldCodec<TKey> keyCodec;
-            private readonly FieldCodec<TValue> valueCodec;
-            private readonly uint mapTag;
-
-            /// <summary>
-            /// Creates a new entry codec based on a separate key codec and value codec,
-            /// and the tag to use for each map entry.
-            /// </summary>
-            /// <param name="keyCodec">The key codec.</param>
-            /// <param name="valueCodec">The value codec.</param>
-            /// <param name="mapTag">The map tag to use to introduce each map entry.</param>
-            public Codec(FieldCodec<TKey> keyCodec, FieldCodec<TValue> valueCodec, uint mapTag)
-            {
-                this.keyCodec = keyCodec;
-                this.valueCodec = valueCodec;
-                this.mapTag = mapTag;
-            }
-
-            /// <summary>
-            /// The tag used in the enclosing message to indicate map entries.
-            /// </summary>
-            internal uint MapTag { get { return mapTag; } }
-
-            /// <summary>
-            /// A mutable message class, used for parsing and serializing. This
-            /// delegates the work to a codec, but implements the <see cref="IMessage"/> interface
-            /// for interop with <see cref="CodedInputStream"/> and <see cref="CodedOutputStream"/>.
-            /// This is nested inside Codec as it's tightly coupled to the associated codec,
-            /// and it's simpler if it has direct access to all its fields.
-            /// </summary>
-            internal class MessageAdapter : IMessage
-            {
-                private static readonly byte[] ZeroLengthMessageStreamData = new byte[] { 0 };
-
-                private readonly Codec codec;
-                internal TKey Key { get; set; }
-                internal TValue Value { get; set; }
-
-                internal MessageAdapter(Codec codec)
-                {
-                    this.codec = codec;
-                }
-
-                internal void Reset()
-                {
-                    Key = codec.keyCodec.DefaultValue;
-                    Value = codec.valueCodec.DefaultValue;
-                }
-
-                public void MergeFrom(CodedInputStream input)
-                {
-                    uint tag;
-                    while ((tag = input.ReadTag()) != 0)
-                    {
-                        if (tag == codec.keyCodec.Tag)
-                        {
-                            Key = codec.keyCodec.Read(input);
-                        }
-                        else if (tag == codec.valueCodec.Tag)
-                        {
-                            Value = codec.valueCodec.Read(input);
-                        }
-                        else 
-                        {
-                            input.SkipLastField();
-                        }
-                    }
-
-                    // Corner case: a map entry with a key but no value, where the value type is a message.
-                    // Read it as if we'd seen an input stream with no data (i.e. create a "default" message).
-                    if (Value == null)
-                    {
-                        Value = codec.valueCodec.Read(new CodedInputStream(ZeroLengthMessageStreamData));
-                    }
-                }
-
-                public void WriteTo(CodedOutputStream output)
-                {
-                    codec.keyCodec.WriteTagAndValue(output, Key);
-                    codec.valueCodec.WriteTagAndValue(output, Value);
-                }
-
-                public int CalculateSize()
-                {
-                    return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value);
-                }
-
-                MessageDescriptor IMessage.Descriptor { get { return null; } }
-            }
-        }
-
-        private class MapView<T> : ICollection<T>, ICollection
-        {
-            private readonly MapField<TKey, TValue> parent;
-            private readonly Func<KeyValuePair<TKey, TValue>, T> projection;
-            private readonly Func<T, bool> containsCheck;
-
-            internal MapView(
-                MapField<TKey, TValue> parent,
-                Func<KeyValuePair<TKey, TValue>, T> projection,
-                Func<T, bool> containsCheck)
-            {
-                this.parent = parent;
-                this.projection = projection;
-                this.containsCheck = containsCheck;
-            }
-
-            public int Count { get { return parent.Count; } }
-
-            public bool IsReadOnly { get { return true; } }
-
-            public bool IsSynchronized { get { return false; } }
-
-            public object SyncRoot { get { return parent; } }
-
-            public void Add(T item)
-            {
-                throw new NotSupportedException();
-            }
-
-            public void Clear()
-            {
-                throw new NotSupportedException();
-            }
-
-            public bool Contains(T item)
-            {
-                return containsCheck(item);
-            }
-
-            public void CopyTo(T[] array, int arrayIndex)
-            {
-                if (arrayIndex < 0)
-                {
-                    throw new ArgumentOutOfRangeException(nameof(arrayIndex));
-                }
-                if (arrayIndex + Count > array.Length)
-                {
-                    throw new ArgumentException("Not enough space in the array", nameof(array));
-                }
-                foreach (var item in this)
-                {
-                    array[arrayIndex++] = item;
-                }
-            }
-
-            public IEnumerator<T> GetEnumerator()
-            {
-                return parent.list.Select(projection).GetEnumerator();
-            }
-
-            public bool Remove(T item)
-            {
-                throw new NotSupportedException();
-            }
-
-            IEnumerator IEnumerable.GetEnumerator()
-            {
-                return GetEnumerator();
-            }
-
-            public void CopyTo(Array array, int index)
-            {
-                if (index < 0)
-                {
-                    throw new ArgumentOutOfRangeException(nameof(index));
-                }
-                if (index + Count > array.Length)
-                {
-                    throw new ArgumentException("Not enough space in the array", nameof(array));
-                }
-                foreach (var item in this)
-                {
-                    array.SetValue(item, index++);
-                }
-            }
-        }
-    }
-}
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using Google.Protobuf.Compatibility;
+using Google.Protobuf.Reflection;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Google.Protobuf.Collections
+{
+    /// <summary>
+    /// Representation of a map field in a Protocol Buffer message.
+    /// </summary>
+    /// <typeparam name="TKey">Key type in the map. Must be a type supported by Protocol Buffer map keys.</typeparam>
+    /// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam>
+    /// <remarks>
+    /// <para>
+    /// For string keys, the equality comparison is provided by <see cref="StringComparer.Ordinal" />.
+    /// </para>
+    /// <para>
+    /// Null values are not permitted in the map, either for wrapper types or regular messages.
+    /// If a map is deserialized from a data stream and the value is missing from an entry, a default value
+    /// is created instead. For primitive types, that is the regular default value (0, the empty string and so
+    /// on); for message types, an empty instance of the message is created, as if the map entry contained a 0-length
+    /// encoded value for the field.
+    /// </para>
+    /// <para>
+    /// This implementation does not generally prohibit the use of key/value types which are not
+    /// supported by Protocol Buffers (e.g. using a key type of <code>byte</code>) but nor does it guarantee
+    /// that all operations will work in such cases.
+    /// </para>
+    /// <para>
+    /// The order in which entries are returned when iterating over this object is undefined, and may change
+    /// in future versions.
+    /// </para>
+    /// </remarks>
+    public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary
+#if !NET35
+        , IReadOnlyDictionary<TKey, TValue>
+#endif
+    {
+        private static readonly EqualityComparer<TValue> ValueEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TValue>();
+        private static readonly EqualityComparer<TKey> KeyEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TKey>();
+
+        // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
+        private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> map =
+            new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>(KeyEqualityComparer);
+        private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new LinkedList<KeyValuePair<TKey, TValue>>();
+
+        /// <summary>
+        /// Creates a deep clone of this object.
+        /// </summary>
+        /// <returns>
+        /// A deep clone of this object.
+        /// </returns>
+        public MapField<TKey, TValue> Clone()
+        {
+            var clone = new MapField<TKey, TValue>();
+            // Keys are never cloneable. Values might be.
+            if (typeof(IDeepCloneable<TValue>).IsAssignableFrom(typeof(TValue)))
+            {
+                foreach (var pair in list)
+                {
+                    clone.Add(pair.Key, ((IDeepCloneable<TValue>)pair.Value).Clone());
+                }
+            }
+            else
+            {
+                // Nothing is cloneable, so we don't need to worry.
+                clone.Add(this);
+            }
+            return clone;
+        }
+
+        /// <summary>
+        /// Adds the specified key/value pair to the map.
+        /// </summary>
+        /// <remarks>
+        /// This operation fails if the key already exists in the map. To replace an existing entry, use the indexer.
+        /// </remarks>
+        /// <param name="key">The key to add</param>
+        /// <param name="value">The value to add.</param>
+        /// <exception cref="System.ArgumentException">The given key already exists in map.</exception>
+        public void Add(TKey key, TValue value)
+        {
+            // Validation of arguments happens in ContainsKey and the indexer
+            if (ContainsKey(key))
+            {
+                throw new ArgumentException("Key already exists in map", nameof(key));
+            }
+            this[key] = value;
+        }
+
+        /// <summary>
+        /// Determines whether the specified key is present in the map.
+        /// </summary>
+        /// <param name="key">The key to check.</param>
+        /// <returns><c>true</c> if the map contains the given key; <c>false</c> otherwise.</returns>
+        public bool ContainsKey(TKey key)
+        {
+            ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
+            return map.ContainsKey(key);
+        }
+
+        private bool ContainsValue(TValue value) =>
+            list.Any(pair => ValueEqualityComparer.Equals(pair.Value, value));
+
+        /// <summary>
+        /// Removes the entry identified by the given key from the map.
+        /// </summary>
+        /// <param name="key">The key indicating the entry to remove from the map.</param>
+        /// <returns><c>true</c> if the map contained the given key before the entry was removed; <c>false</c> otherwise.</returns>
+        public bool Remove(TKey key)
+        {
+            ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
+            LinkedListNode<KeyValuePair<TKey, TValue>> node;
+            if (map.TryGetValue(key, out node))
+            {
+                map.Remove(key);
+                node.List.Remove(node);
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Gets the value associated with the specified key.
+        /// </summary>
+        /// <param name="key">The key whose value to get.</param>
+        /// <param name="value">When this method returns, the value associated with the specified key, if the key is found;
+        /// otherwise, the default value for the type of the <paramref name="value"/> parameter.
+        /// This parameter is passed uninitialized.</param>
+        /// <returns><c>true</c> if the map contains an element with the specified key; otherwise, <c>false</c>.</returns>
+        public bool TryGetValue(TKey key, out TValue value)
+        {
+            LinkedListNode<KeyValuePair<TKey, TValue>> node;
+            if (map.TryGetValue(key, out node))
+            {
+                value = node.Value.Value;
+                return true;
+            }
+            else
+            {
+                value = default(TValue);
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the value associated with the specified key.
+        /// </summary>
+        /// <param name="key">The key of the value to get or set.</param>
+        /// <exception cref="KeyNotFoundException">The property is retrieved and key does not exist in the collection.</exception>
+        /// <returns>The value associated with the specified key. If the specified key is not found,
+        /// a get operation throws a <see cref="KeyNotFoundException"/>, and a set operation creates a new element with the specified key.</returns>
+        public TValue this[TKey key]
+        {
+            get
+            {
+                ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
+                TValue value;
+                if (TryGetValue(key, out value))
+                {
+                    return value;
+                }
+                throw new KeyNotFoundException();
+            }
+            set
+            {
+                ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
+                // value == null check here is redundant, but avoids boxing.
+                if (value == null)
+                {
+                    ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
+                }
+                LinkedListNode<KeyValuePair<TKey, TValue>> node;
+                var pair = new KeyValuePair<TKey, TValue>(key, value);
+                if (map.TryGetValue(key, out node))
+                {
+                    node.Value = pair;
+                }
+                else
+                {
+                    node = list.AddLast(pair);
+                    map[key] = node;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets a collection containing the keys in the map.
+        /// </summary>
+        public ICollection<TKey> Keys { get { return new MapView<TKey>(this, pair => pair.Key, ContainsKey); } }
+
+        /// <summary>
+        /// Gets a collection containing the values in the map.
+        /// </summary>
+        public ICollection<TValue> Values { get { return new MapView<TValue>(this, pair => pair.Value, ContainsValue); } }
+
+        /// <summary>
+        /// Adds the specified entries to the map. The keys and values are not automatically cloned.
+        /// </summary>
+        /// <param name="entries">The entries to add to the map.</param>
+        public void Add(IDictionary<TKey, TValue> entries)
+        {
+            ProtoPreconditions.CheckNotNull(entries, nameof(entries));
+            foreach (var pair in entries)
+            {
+                Add(pair.Key, pair.Value);
+            }
+        }
+
+        /// <summary>
+        /// Returns an enumerator that iterates through the collection.
+        /// </summary>
+        /// <returns>
+        /// An enumerator that can be used to iterate through the collection.
+        /// </returns>
+        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+        {
+            return list.GetEnumerator();
+        }
+
+        /// <summary>
+        /// Returns an enumerator that iterates through a collection.
+        /// </summary>
+        /// <returns>
+        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
+        /// </returns>
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        /// <summary>
+        /// Adds the specified item to the map.
+        /// </summary>
+        /// <param name="item">The item to add to the map.</param>
+        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
+        {
+            Add(item.Key, item.Value);
+        }
+
+        /// <summary>
+        /// Removes all items from the map.
+        /// </summary>
+        public void Clear()
+        {
+            list.Clear();
+            map.Clear();
+        }
+
+        /// <summary>
+        /// Determines whether map contains an entry equivalent to the given key/value pair.
+        /// </summary>
+        /// <param name="item">The key/value pair to find.</param>
+        /// <returns></returns>
+        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
+        {
+            TValue value;
+            return TryGetValue(item.Key, out value) && ValueEqualityComparer.Equals(item.Value, value);
+        }
+
+        /// <summary>
+        /// Copies the key/value pairs in this map to an array.
+        /// </summary>
+        /// <param name="array">The array to copy the entries into.</param>
+        /// <param name="arrayIndex">The index of the array at which to start copying values.</param>
+        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+        {
+            list.CopyTo(array, arrayIndex);
+        }
+
+        /// <summary>
+        /// Removes the specified key/value pair from the map.
+        /// </summary>
+        /// <remarks>Both the key and the value must be found for the entry to be removed.</remarks>
+        /// <param name="item">The key/value pair to remove.</param>
+        /// <returns><c>true</c> if the key/value pair was found and removed; <c>false</c> otherwise.</returns>
+        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
+        {
+            if (item.Key == null)
+            {
+                throw new ArgumentException("Key is null", nameof(item));
+            }
+            LinkedListNode<KeyValuePair<TKey, TValue>> node;
+            if (map.TryGetValue(item.Key, out node) &&
+                EqualityComparer<TValue>.Default.Equals(item.Value, node.Value.Value))
+            {
+                map.Remove(item.Key);
+                node.List.Remove(node);
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Gets the number of elements contained in the map.
+        /// </summary>
+        public int Count { get { return list.Count; } }
+
+        /// <summary>
+        /// Gets a value indicating whether the map is read-only.
+        /// </summary>
+        public bool IsReadOnly { get { return false; } }
+
+        /// <summary>
+        /// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
+        /// </summary>
+        /// <param name="other">The <see cref="System.Object" /> to compare with this instance.</param>
+        /// <returns>
+        ///   <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
+        /// </returns>
+        public override bool Equals(object other)
+        {
+            return Equals(other as MapField<TKey, TValue>);
+        }
+
+        /// <summary>
+        /// Returns a hash code for this instance.
+        /// </summary>
+        /// <returns>
+        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
+        /// </returns>
+        public override int GetHashCode()
+        {
+            var keyComparer = KeyEqualityComparer;
+            var valueComparer = ValueEqualityComparer;
+            int hash = 0;
+            foreach (var pair in list)
+            {
+                hash ^= keyComparer.GetHashCode(pair.Key) * 31 + valueComparer.GetHashCode(pair.Value);
+            }
+            return hash;
+        }
+
+        /// <summary>
+        /// Compares this map with another for equality.
+        /// </summary>
+        /// <remarks>
+        /// The order of the key/value pairs in the maps is not deemed significant in this comparison.
+        /// </remarks>
+        /// <param name="other">The map to compare this with.</param>
+        /// <returns><c>true</c> if <paramref name="other"/> refers to an equal map; <c>false</c> otherwise.</returns>
+        public bool Equals(MapField<TKey, TValue> other)
+        {
+            if (other == null)
+            {
+                return false;
+            }
+            if (other == this)
+            {
+                return true;
+            }
+            if (other.Count != this.Count)
+            {
+                return false;
+            }
+            var valueComparer = ValueEqualityComparer;
+            foreach (var pair in this)
+            {
+                TValue value;
+                if (!other.TryGetValue(pair.Key, out value))
+                {
+                    return false;
+                }
+                if (!valueComparer.Equals(value, pair.Value))
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /// <summary>
+        /// Adds entries to the map from the given stream.
+        /// </summary>
+        /// <remarks>
+        /// It is assumed that the stream is initially positioned after the tag specified by the codec.
+        /// This method will continue reading entries from the stream until the end is reached, or
+        /// a different tag is encountered.
+        /// </remarks>
+        /// <param name="input">Stream to read from</param>
+        /// <param name="codec">Codec describing how the key/value pairs are encoded</param>
+        public void AddEntriesFrom(CodedInputStream input, Codec codec)
+        {
+            var adapter = new Codec.MessageAdapter(codec);
+            do
+            {
+                adapter.Reset();
+                input.ReadMessage(adapter);
+                this[adapter.Key] = adapter.Value;
+            } while (input.MaybeConsumeTag(codec.MapTag));
+        }
+
+        /// <summary>
+        /// Writes the contents of this map to the given coded output stream, using the specified codec
+        /// to encode each entry.
+        /// </summary>
+        /// <param name="output">The output stream to write to.</param>
+        /// <param name="codec">The codec to use for each entry.</param>
+        public void WriteTo(CodedOutputStream output, Codec codec)
+        {
+            var message = new Codec.MessageAdapter(codec);
+            foreach (var entry in list)
+            {
+                message.Key = entry.Key;
+                message.Value = entry.Value;
+                output.WriteTag(codec.MapTag);
+                output.WriteMessage(message);
+            }
+        }
+
+        /// <summary>
+        /// Calculates the size of this map based on the given entry codec.
+        /// </summary>
+        /// <param name="codec">The codec to use to encode each entry.</param>
+        /// <returns></returns>
+        public int CalculateSize(Codec codec)
+        {
+            if (Count == 0)
+            {
+                return 0;
+            }
+            var message = new Codec.MessageAdapter(codec);
+            int size = 0;
+            foreach (var entry in list)
+            {
+                message.Key = entry.Key;
+                message.Value = entry.Value;
+                size += CodedOutputStream.ComputeRawVarint32Size(codec.MapTag);
+                size += CodedOutputStream.ComputeMessageSize(message);
+            }
+            return size;
+        }
+
+        /// <summary>
+        /// Returns a string representation of this repeated field, in the same
+        /// way as it would be represented by the default JSON formatter.
+        /// </summary>
+        public override string ToString()
+        {
+            var writer = new StringWriter();
+            JsonFormatter.Default.WriteDictionary(writer, this);
+            return writer.ToString();
+        }
+
+        #region IDictionary explicit interface implementation
+        void IDictionary.Add(object key, object value)
+        {
+            Add((TKey)key, (TValue)value);
+        }
+
+        bool IDictionary.Contains(object key)
+        {
+            if (!(key is TKey))
+            {
+                return false;
+            }
+            return ContainsKey((TKey)key);
+        }
+
+        IDictionaryEnumerator IDictionary.GetEnumerator()
+        {
+            return new DictionaryEnumerator(GetEnumerator());
+        }
+
+        void IDictionary.Remove(object key)
+        {
+            ProtoPreconditions.CheckNotNull(key, nameof(key));
+            if (!(key is TKey))
+            {
+                return;
+            }
+            Remove((TKey)key);
+        }
+
+        void ICollection.CopyTo(Array array, int index)
+        {
+            // This is ugly and slow as heck, but with any luck it will never be used anyway.
+            ICollection temp = this.Select(pair => new DictionaryEntry(pair.Key, pair.Value)).ToList();
+            temp.CopyTo(array, index);
+        }
+
+        bool IDictionary.IsFixedSize { get { return false; } }
+
+        ICollection IDictionary.Keys { get { return (ICollection)Keys; } }
+
+        ICollection IDictionary.Values { get { return (ICollection)Values; } }
+
+        bool ICollection.IsSynchronized { get { return false; } }
+
+        object ICollection.SyncRoot { get { return this; } }
+
+        object IDictionary.this[object key]
+        {
+            get
+            {
+                ProtoPreconditions.CheckNotNull(key, nameof(key));
+                if (!(key is TKey))
+                {
+                    return null;
+                }
+                TValue value;
+                TryGetValue((TKey)key, out value);
+                return value;
+            }
+
+            set
+            {
+                this[(TKey)key] = (TValue)value;
+            }
+        }
+        #endregion
+
+        #region IReadOnlyDictionary explicit interface implementation
+#if !NET35
+        IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys;
+
+        IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
+#endif
+        #endregion
+
+        private class DictionaryEnumerator : IDictionaryEnumerator
+        {
+            private readonly IEnumerator<KeyValuePair<TKey, TValue>> enumerator;
+
+            internal DictionaryEnumerator(IEnumerator<KeyValuePair<TKey, TValue>> enumerator)
+            {
+                this.enumerator = enumerator;
+            }
+
+            public bool MoveNext()
+            {
+                return enumerator.MoveNext();
+            }
+
+            public void Reset()
+            {
+                enumerator.Reset();
+            }
+
+            public object Current { get { return Entry; } }
+            public DictionaryEntry Entry { get { return new DictionaryEntry(Key, Value); } }
+            public object Key { get { return enumerator.Current.Key; } }
+            public object Value { get { return enumerator.Current.Value; } }
+        }
+
+        /// <summary>
+        /// A codec for a specific map field. This contains all the information required to encode and
+        /// decode the nested messages.
+        /// </summary>
+        public sealed class Codec
+        {
+            private readonly FieldCodec<TKey> keyCodec;
+            private readonly FieldCodec<TValue> valueCodec;
+            private readonly uint mapTag;
+
+            /// <summary>
+            /// Creates a new entry codec based on a separate key codec and value codec,
+            /// and the tag to use for each map entry.
+            /// </summary>
+            /// <param name="keyCodec">The key codec.</param>
+            /// <param name="valueCodec">The value codec.</param>
+            /// <param name="mapTag">The map tag to use to introduce each map entry.</param>
+            public Codec(FieldCodec<TKey> keyCodec, FieldCodec<TValue> valueCodec, uint mapTag)
+            {
+                this.keyCodec = keyCodec;
+                this.valueCodec = valueCodec;
+                this.mapTag = mapTag;
+            }
+
+            /// <summary>
+            /// The tag used in the enclosing message to indicate map entries.
+            /// </summary>
+            internal uint MapTag { get { return mapTag; } }
+
+            /// <summary>
+            /// A mutable message class, used for parsing and serializing. This
+            /// delegates the work to a codec, but implements the <see cref="IMessage"/> interface
+            /// for interop with <see cref="CodedInputStream"/> and <see cref="CodedOutputStream"/>.
+            /// This is nested inside Codec as it's tightly coupled to the associated codec,
+            /// and it's simpler if it has direct access to all its fields.
+            /// </summary>
+            internal class MessageAdapter : IMessage
+            {
+                private static readonly byte[] ZeroLengthMessageStreamData = new byte[] { 0 };
+
+                private readonly Codec codec;
+                internal TKey Key { get; set; }
+                internal TValue Value { get; set; }
+
+                internal MessageAdapter(Codec codec)
+                {
+                    this.codec = codec;
+                }
+
+                internal void Reset()
+                {
+                    Key = codec.keyCodec.DefaultValue;
+                    Value = codec.valueCodec.DefaultValue;
+                }
+
+                public void MergeFrom(CodedInputStream input)
+                {
+                    uint tag;
+                    while ((tag = input.ReadTag()) != 0)
+                    {
+                        if (tag == codec.keyCodec.Tag)
+                        {
+                            Key = codec.keyCodec.Read(input);
+                        }
+                        else if (tag == codec.valueCodec.Tag)
+                        {
+                            Value = codec.valueCodec.Read(input);
+                        }
+                        else 
+                        {
+                            input.SkipLastField();
+                        }
+                    }
+
+                    // Corner case: a map entry with a key but no value, where the value type is a message.
+                    // Read it as if we'd seen an input stream with no data (i.e. create a "default" message).
+                    if (Value == null)
+                    {
+                        Value = codec.valueCodec.Read(new CodedInputStream(ZeroLengthMessageStreamData));
+                    }
+                }
+
+                public void WriteTo(CodedOutputStream output)
+                {
+                    codec.keyCodec.WriteTagAndValue(output, Key);
+                    codec.valueCodec.WriteTagAndValue(output, Value);
+                }
+
+                public int CalculateSize()
+                {
+                    return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value);
+                }
+
+                MessageDescriptor IMessage.Descriptor { get { return null; } }
+            }
+        }
+
+        private class MapView<T> : ICollection<T>, ICollection
+        {
+            private readonly MapField<TKey, TValue> parent;
+            private readonly Func<KeyValuePair<TKey, TValue>, T> projection;
+            private readonly Func<T, bool> containsCheck;
+
+            internal MapView(
+                MapField<TKey, TValue> parent,
+                Func<KeyValuePair<TKey, TValue>, T> projection,
+                Func<T, bool> containsCheck)
+            {
+                this.parent = parent;
+                this.projection = projection;
+                this.containsCheck = containsCheck;
+            }
+
+            public int Count { get { return parent.Count; } }
+
+            public bool IsReadOnly { get { return true; } }
+
+            public bool IsSynchronized { get { return false; } }
+
+            public object SyncRoot { get { return parent; } }
+
+            public void Add(T item)
+            {
+                throw new NotSupportedException();
+            }
+
+            public void Clear()
+            {
+                throw new NotSupportedException();
+            }
+
+            public bool Contains(T item)
+            {
+                return containsCheck(item);
+            }
+
+            public void CopyTo(T[] array, int arrayIndex)
+            {
+                if (arrayIndex < 0)
+                {
+                    throw new ArgumentOutOfRangeException(nameof(arrayIndex));
+                }
+                if (arrayIndex + Count > array.Length)
+                {
+                    throw new ArgumentException("Not enough space in the array", nameof(array));
+                }
+                foreach (var item in this)
+                {
+                    array[arrayIndex++] = item;
+                }
+            }
+
+            public IEnumerator<T> GetEnumerator()
+            {
+                return parent.list.Select(projection).GetEnumerator();
+            }
+
+            public bool Remove(T item)
+            {
+                throw new NotSupportedException();
+            }
+
+            IEnumerator IEnumerable.GetEnumerator()
+            {
+                return GetEnumerator();
+            }
+
+            public void CopyTo(Array array, int index)
+            {
+                if (index < 0)
+                {
+                    throw new ArgumentOutOfRangeException(nameof(index));
+                }
+                if (index + Count > array.Length)
+                {
+                    throw new ArgumentException("Not enough space in the array", nameof(array));
+                }
+                foreach (var item in this)
+                {
+                    array.SetValue(item, index++);
+                }
+            }
+        }
+    }
+}

+ 595 - 595
csharp/src/Google.Protobuf/Collections/RepeatedField.cs

@@ -1,595 +1,595 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2015 Google Inc.  All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#endregion
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.IO;
-
-namespace Google.Protobuf.Collections
-{
-    /// <summary>
-    /// The contents of a repeated field: essentially, a collection with some extra
-    /// restrictions (no null values) and capabilities (deep cloning).
-    /// </summary>
-    /// <remarks>
-    /// This implementation does not generally prohibit the use of types which are not
-    /// supported by Protocol Buffers but nor does it guarantee that all operations will work in such cases.
-    /// </remarks>
-    /// <typeparam name="T">The element type of the repeated field.</typeparam>
-    public sealed class RepeatedField<T> : IList<T>, IList, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>
-#if !NET35
-        , IReadOnlyList<T>
-#endif
-    {
-        private static readonly EqualityComparer<T> EqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<T>();
-        private static readonly T[] EmptyArray = new T[0];
-        private const int MinArraySize = 8;
-
-        private T[] array = EmptyArray;
-        private int count = 0;
-
-        /// <summary>
-        /// Creates a deep clone of this repeated field.
-        /// </summary>
-        /// <remarks>
-        /// If the field type is
-        /// a message type, each element is also cloned; otherwise, it is
-        /// assumed that the field type is primitive (including string and
-        /// bytes, both of which are immutable) and so a simple copy is
-        /// equivalent to a deep clone.
-        /// </remarks>
-        /// <returns>A deep clone of this repeated field.</returns>
-        public RepeatedField<T> Clone()
-        {
-            RepeatedField<T> clone = new RepeatedField<T>();
-            if (array != EmptyArray)
-            {
-                clone.array = (T[])array.Clone();
-                IDeepCloneable<T>[] cloneableArray = clone.array as IDeepCloneable<T>[];
-                if (cloneableArray != null)
-                {
-                    for (int i = 0; i < count; i++)
-                    {
-                        clone.array[i] = cloneableArray[i].Clone();
-                    }
-                }
-            }
-            clone.count = count;
-            return clone;
-        }
-
-        /// <summary>
-        /// Adds the entries from the given input stream, decoding them with the specified codec.
-        /// </summary>
-        /// <param name="input">The input stream to read from.</param>
-        /// <param name="codec">The codec to use in order to read each entry.</param>
-        public void AddEntriesFrom(CodedInputStream input, FieldCodec<T> codec)
-        {
-            // TODO: Inline some of the Add code, so we can avoid checking the size on every
-            // iteration.
-            uint tag = input.LastTag;
-            var reader = codec.ValueReader;
-            // Non-nullable value types can be packed or not.
-            if (FieldCodec<T>.IsPackedRepeatedField(tag))
-            {
-                int length = input.ReadLength();
-                if (length > 0)
-                {
-                    int oldLimit = input.PushLimit(length);
-                    while (!input.ReachedLimit)
-                    {
-                        Add(reader(input));
-                    }
-                    input.PopLimit(oldLimit);
-                }
-                // Empty packed field. Odd, but valid - just ignore.
-            }
-            else
-            {
-                // Not packed... (possibly not packable)
-                do
-                {
-                    Add(reader(input));
-                } while (input.MaybeConsumeTag(tag));
-            }
-        }
-
-        /// <summary>
-        /// Calculates the size of this collection based on the given codec.
-        /// </summary>
-        /// <param name="codec">The codec to use when encoding each field.</param>
-        /// <returns>The number of bytes that would be written to a <see cref="CodedOutputStream"/> by <see cref="WriteTo"/>,
-        /// using the same codec.</returns>
-        public int CalculateSize(FieldCodec<T> codec)
-        {
-            if (count == 0)
-            {
-                return 0;
-            }
-            uint tag = codec.Tag;
-            if (codec.PackedRepeatedField)
-            {
-                int dataSize = CalculatePackedDataSize(codec);
-                return CodedOutputStream.ComputeRawVarint32Size(tag) +
-                    CodedOutputStream.ComputeLengthSize(dataSize) +
-                    dataSize;
-            }
-            else
-            {
-                var sizeCalculator = codec.ValueSizeCalculator;
-                int size = count * CodedOutputStream.ComputeRawVarint32Size(tag);
-                for (int i = 0; i < count; i++)
-                {
-                    size += sizeCalculator(array[i]);
-                }
-                return size;
-            }
-        }
-
-        private int CalculatePackedDataSize(FieldCodec<T> codec)
-        {
-            int fixedSize = codec.FixedSize;
-            if (fixedSize == 0)
-            {
-                var calculator = codec.ValueSizeCalculator;
-                int tmp = 0;
-                for (int i = 0; i < count; i++)
-                {
-                    tmp += calculator(array[i]);
-                }
-                return tmp;
-            }
-            else
-            {
-                return fixedSize * Count;
-            }
-        }
-
-        /// <summary>
-        /// Writes the contents of this collection to the given <see cref="CodedOutputStream"/>,
-        /// encoding each value using the specified codec.
-        /// </summary>
-        /// <param name="output">The output stream to write to.</param>
-        /// <param name="codec">The codec to use when encoding each value.</param>
-        public void WriteTo(CodedOutputStream output, FieldCodec<T> codec)
-        {
-            if (count == 0)
-            {
-                return;
-            }
-            var writer = codec.ValueWriter;
-            var tag = codec.Tag;
-            if (codec.PackedRepeatedField)
-            {
-                // Packed primitive type
-                uint size = (uint)CalculatePackedDataSize(codec);
-                output.WriteTag(tag);
-                output.WriteRawVarint32(size);
-                for (int i = 0; i < count; i++)
-                {
-                    writer(output, array[i]);
-                }
-            }
-            else
-            {
-                // Not packed: a simple tag/value pair for each value.
-                // Can't use codec.WriteTagAndValue, as that omits default values.
-                for (int i = 0; i < count; i++)
-                {
-                    output.WriteTag(tag);
-                    writer(output, array[i]);
-                }
-            }
-        }
-
-        private void EnsureSize(int size)
-        {
-            if (array.Length < size)
-            {
-                size = Math.Max(size, MinArraySize);
-                int newSize = Math.Max(array.Length * 2, size);
-                var tmp = new T[newSize];
-                Array.Copy(array, 0, tmp, 0, array.Length);
-                array = tmp;
-            }
-        }
-
-        /// <summary>
-        /// Adds the specified item to the collection.
-        /// </summary>
-        /// <param name="item">The item to add.</param>
-        public void Add(T item)
-        {
-            ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
-            EnsureSize(count + 1);
-            array[count++] = item;
-        }
-
-        /// <summary>
-        /// Removes all items from the collection.
-        /// </summary>
-        public void Clear()
-        {
-            array = EmptyArray;
-            count = 0;
-        }
-
-        /// <summary>
-        /// Determines whether this collection contains the given item.
-        /// </summary>
-        /// <param name="item">The item to find.</param>
-        /// <returns><c>true</c> if this collection contains the given item; <c>false</c> otherwise.</returns>
-        public bool Contains(T item)
-        {
-            return IndexOf(item) != -1;
-        }
-
-        /// <summary>
-        /// Copies this collection to the given array.
-        /// </summary>
-        /// <param name="array">The array to copy to.</param>
-        /// <param name="arrayIndex">The first index of the array to copy to.</param>
-        public void CopyTo(T[] array, int arrayIndex)
-        {
-            Array.Copy(this.array, 0, array, arrayIndex, count);
-        }
-
-        /// <summary>
-        /// Removes the specified item from the collection
-        /// </summary>
-        /// <param name="item">The item to remove.</param>
-        /// <returns><c>true</c> if the item was found and removed; <c>false</c> otherwise.</returns>
-        public bool Remove(T item)
-        {
-            int index = IndexOf(item);
-            if (index == -1)
-            {
-                return false;
-            }            
-            Array.Copy(array, index + 1, array, index, count - index - 1);
-            count--;
-            array[count] = default(T);
-            return true;
-        }
-
-        /// <summary>
-        /// Gets the number of elements contained in the collection.
-        /// </summary>
-        public int Count => count;
-
-        /// <summary>
-        /// Gets a value indicating whether the collection is read-only.
-        /// </summary>
-        public bool IsReadOnly => false;
-
-        /// <summary>
-        /// Adds all of the specified values into this collection.
-        /// </summary>
-        /// <param name="values">The values to add to this collection.</param>
-        public void AddRange(IEnumerable<T> values)
-        {
-            ProtoPreconditions.CheckNotNull(values, nameof(values));
-
-            // Optimization 1: If the collection we're adding is already a RepeatedField<T>,
-            // we know the values are valid.
-            var otherRepeatedField = values as RepeatedField<T>;
-            if (otherRepeatedField != null)
-            {
-                EnsureSize(count + otherRepeatedField.count);
-                Array.Copy(otherRepeatedField.array, 0, array, count, otherRepeatedField.count);
-                count += otherRepeatedField.count;
-                return;
-            }
-
-            // Optimization 2: The collection is an ICollection, so we can expand
-            // just once and ask the collection to copy itself into the array.
-            var collection = values as ICollection;
-            if (collection != null)
-            {
-                var extraCount = collection.Count;
-                // For reference types and nullable value types, we need to check that there are no nulls
-                // present. (This isn't a thread-safe approach, but we don't advertise this is thread-safe.)
-                // We expect the JITter to optimize this test to true/false, so it's effectively conditional
-                // specialization.
-                if (default(T) == null)
-                {
-                    // TODO: Measure whether iterating once to check and then letting the collection copy
-                    // itself is faster or slower than iterating and adding as we go. For large
-                    // collections this will not be great in terms of cache usage... but the optimized
-                    // copy may be significantly faster than doing it one at a time.
-                    foreach (var item in collection)
-                    {
-                        if (item == null)
-                        {
-                            throw new ArgumentException("Sequence contained null element", nameof(values));
-                        }
-                    }
-                }
-                EnsureSize(count + extraCount);
-                collection.CopyTo(array, count);
-                count += extraCount;
-                return;
-            }
-
-            // We *could* check for ICollection<T> as well, but very very few collections implement
-            // ICollection<T> but not ICollection. (HashSet<T> does, for one...)
-
-            // Fall back to a slower path of adding items one at a time.
-            foreach (T item in values)
-            {
-                Add(item);
-            }
-        }
-
-        /// <summary>
-        /// Adds all of the specified values into this collection. This method is present to
-        /// allow repeated fields to be constructed from queries within collection initializers.
-        /// Within non-collection-initializer code, consider using the equivalent <see cref="AddRange"/>
-        /// method instead for clarity.
-        /// </summary>
-        /// <param name="values">The values to add to this collection.</param>
-        public void Add(IEnumerable<T> values)
-        {
-            AddRange(values);
-        }
-
-        /// <summary>
-        /// Returns an enumerator that iterates through the collection.
-        /// </summary>
-        /// <returns>
-        /// An enumerator that can be used to iterate through the collection.
-        /// </returns>
-        public IEnumerator<T> GetEnumerator()
-        {
-            for (int i = 0; i < count; i++)
-            {
-                yield return array[i];
-            }
-        }
-
-        /// <summary>
-        /// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
-        /// </summary>
-        /// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
-        /// <returns>
-        ///   <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
-        /// </returns>
-        public override bool Equals(object obj)
-        {
-            return Equals(obj as RepeatedField<T>);
-        }
-
-        /// <summary>
-        /// Returns an enumerator that iterates through a collection.
-        /// </summary>
-        /// <returns>
-        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
-        /// </returns>
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return GetEnumerator();
-        }
-
-        /// <summary>
-        /// Returns a hash code for this instance.
-        /// </summary>
-        /// <returns>
-        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
-        /// </returns>
-        public override int GetHashCode()
-        {
-            int hash = 0;
-            for (int i = 0; i < count; i++)
-            {
-                hash = hash * 31 + array[i].GetHashCode();
-            }
-            return hash;
-        }
-
-        /// <summary>
-        /// Compares this repeated field with another for equality.
-        /// </summary>
-        /// <param name="other">The repeated field to compare this with.</param>
-        /// <returns><c>true</c> if <paramref name="other"/> refers to an equal repeated field; <c>false</c> otherwise.</returns>
-        public bool Equals(RepeatedField<T> other)
-        {
-            if (ReferenceEquals(other, null))
-            {
-                return false;
-            }
-            if (ReferenceEquals(other, this))
-            {
-                return true;
-            }
-            if (other.Count != this.Count)
-            {
-                return false;
-            }
-            EqualityComparer<T> comparer = EqualityComparer;
-            for (int i = 0; i < count; i++)
-            {
-                if (!comparer.Equals(array[i], other.array[i]))
-                {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        /// <summary>
-        /// Returns the index of the given item within the collection, or -1 if the item is not
-        /// present.
-        /// </summary>
-        /// <param name="item">The item to find in the collection.</param>
-        /// <returns>The zero-based index of the item, or -1 if it is not found.</returns>
-        public int IndexOf(T item)
-        {
-            ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
-            EqualityComparer<T> comparer = EqualityComparer;
-            for (int i = 0; i < count; i++)
-            {
-                if (comparer.Equals(array[i], item))
-                {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        /// <summary>
-        /// Inserts the given item at the specified index.
-        /// </summary>
-        /// <param name="index">The index at which to insert the item.</param>
-        /// <param name="item">The item to insert.</param>
-        public void Insert(int index, T item)
-        {
-            ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
-            if (index < 0 || index > count)
-            {
-                throw new ArgumentOutOfRangeException(nameof(index));
-            }
-            EnsureSize(count + 1);
-            Array.Copy(array, index, array, index + 1, count - index);
-            array[index] = item;
-            count++;
-        }
-
-        /// <summary>
-        /// Removes the item at the given index.
-        /// </summary>
-        /// <param name="index">The zero-based index of the item to remove.</param>
-        public void RemoveAt(int index)
-        {
-            if (index < 0 || index >= count)
-            {
-                throw new ArgumentOutOfRangeException(nameof(index));
-            }
-            Array.Copy(array, index + 1, array, index, count - index - 1);
-            count--;
-            array[count] = default(T);
-        }
-
-        /// <summary>
-        /// Returns a string representation of this repeated field, in the same
-        /// way as it would be represented by the default JSON formatter.
-        /// </summary>
-        public override string ToString()
-        {
-            var writer = new StringWriter();
-            JsonFormatter.Default.WriteList(writer, this);
-            return writer.ToString();
-        }
-
-        /// <summary>
-        /// Gets or sets the item at the specified index.
-        /// </summary>
-        /// <value>
-        /// The element at the specified index.
-        /// </value>
-        /// <param name="index">The zero-based index of the element to get or set.</param>
-        /// <returns>The item at the specified index.</returns>
-        public T this[int index]
-        {
-            get
-            {
-                if (index < 0 || index >= count)
-                {
-                    throw new ArgumentOutOfRangeException(nameof(index));
-                }
-                return array[index];
-            }
-            set
-            {
-                if (index < 0 || index >= count)
-                {
-                    throw new ArgumentOutOfRangeException(nameof(index));
-                }
-                ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
-                array[index] = value;
-            }
-        }
-
-        #region Explicit interface implementation for IList and ICollection.
-        bool IList.IsFixedSize => false;
-
-        void ICollection.CopyTo(Array array, int index)
-        {
-            Array.Copy(this.array, 0, array, index, count);
-        }
-
-        bool ICollection.IsSynchronized => false;
-
-        object ICollection.SyncRoot => this;
-
-        object IList.this[int index]
-        {
-            get { return this[index]; }
-            set { this[index] = (T)value; }
-        }
-
-        int IList.Add(object value)
-        {
-            Add((T) value);
-            return count - 1;
-        }
-
-        bool IList.Contains(object value)
-        {
-            return (value is T && Contains((T)value));
-        }
-
-        int IList.IndexOf(object value)
-        {
-            if (!(value is T))
-            {
-                return -1;
-            }
-            return IndexOf((T)value);
-        }
-
-        void IList.Insert(int index, object value)
-        {
-            Insert(index, (T) value);
-        }
-
-        void IList.Remove(object value)
-        {
-            if (!(value is T))
-            {
-                return;
-            }
-            Remove((T)value);
-        }
-        #endregion        
-    }
-}
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Google.Protobuf.Collections
+{
+    /// <summary>
+    /// The contents of a repeated field: essentially, a collection with some extra
+    /// restrictions (no null values) and capabilities (deep cloning).
+    /// </summary>
+    /// <remarks>
+    /// This implementation does not generally prohibit the use of types which are not
+    /// supported by Protocol Buffers but nor does it guarantee that all operations will work in such cases.
+    /// </remarks>
+    /// <typeparam name="T">The element type of the repeated field.</typeparam>
+    public sealed class RepeatedField<T> : IList<T>, IList, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>
+#if !NET35
+        , IReadOnlyList<T>
+#endif
+    {
+        private static readonly EqualityComparer<T> EqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<T>();
+        private static readonly T[] EmptyArray = new T[0];
+        private const int MinArraySize = 8;
+
+        private T[] array = EmptyArray;
+        private int count = 0;
+
+        /// <summary>
+        /// Creates a deep clone of this repeated field.
+        /// </summary>
+        /// <remarks>
+        /// If the field type is
+        /// a message type, each element is also cloned; otherwise, it is
+        /// assumed that the field type is primitive (including string and
+        /// bytes, both of which are immutable) and so a simple copy is
+        /// equivalent to a deep clone.
+        /// </remarks>
+        /// <returns>A deep clone of this repeated field.</returns>
+        public RepeatedField<T> Clone()
+        {
+            RepeatedField<T> clone = new RepeatedField<T>();
+            if (array != EmptyArray)
+            {
+                clone.array = (T[])array.Clone();
+                IDeepCloneable<T>[] cloneableArray = clone.array as IDeepCloneable<T>[];
+                if (cloneableArray != null)
+                {
+                    for (int i = 0; i < count; i++)
+                    {
+                        clone.array[i] = cloneableArray[i].Clone();
+                    }
+                }
+            }
+            clone.count = count;
+            return clone;
+        }
+
+        /// <summary>
+        /// Adds the entries from the given input stream, decoding them with the specified codec.
+        /// </summary>
+        /// <param name="input">The input stream to read from.</param>
+        /// <param name="codec">The codec to use in order to read each entry.</param>
+        public void AddEntriesFrom(CodedInputStream input, FieldCodec<T> codec)
+        {
+            // TODO: Inline some of the Add code, so we can avoid checking the size on every
+            // iteration.
+            uint tag = input.LastTag;
+            var reader = codec.ValueReader;
+            // Non-nullable value types can be packed or not.
+            if (FieldCodec<T>.IsPackedRepeatedField(tag))
+            {
+                int length = input.ReadLength();
+                if (length > 0)
+                {
+                    int oldLimit = input.PushLimit(length);
+                    while (!input.ReachedLimit)
+                    {
+                        Add(reader(input));
+                    }
+                    input.PopLimit(oldLimit);
+                }
+                // Empty packed field. Odd, but valid - just ignore.
+            }
+            else
+            {
+                // Not packed... (possibly not packable)
+                do
+                {
+                    Add(reader(input));
+                } while (input.MaybeConsumeTag(tag));
+            }
+        }
+
+        /// <summary>
+        /// Calculates the size of this collection based on the given codec.
+        /// </summary>
+        /// <param name="codec">The codec to use when encoding each field.</param>
+        /// <returns>The number of bytes that would be written to a <see cref="CodedOutputStream"/> by <see cref="WriteTo"/>,
+        /// using the same codec.</returns>
+        public int CalculateSize(FieldCodec<T> codec)
+        {
+            if (count == 0)
+            {
+                return 0;
+            }
+            uint tag = codec.Tag;
+            if (codec.PackedRepeatedField)
+            {
+                int dataSize = CalculatePackedDataSize(codec);
+                return CodedOutputStream.ComputeRawVarint32Size(tag) +
+                    CodedOutputStream.ComputeLengthSize(dataSize) +
+                    dataSize;
+            }
+            else
+            {
+                var sizeCalculator = codec.ValueSizeCalculator;
+                int size = count * CodedOutputStream.ComputeRawVarint32Size(tag);
+                for (int i = 0; i < count; i++)
+                {
+                    size += sizeCalculator(array[i]);
+                }
+                return size;
+            }
+        }
+
+        private int CalculatePackedDataSize(FieldCodec<T> codec)
+        {
+            int fixedSize = codec.FixedSize;
+            if (fixedSize == 0)
+            {
+                var calculator = codec.ValueSizeCalculator;
+                int tmp = 0;
+                for (int i = 0; i < count; i++)
+                {
+                    tmp += calculator(array[i]);
+                }
+                return tmp;
+            }
+            else
+            {
+                return fixedSize * Count;
+            }
+        }
+
+        /// <summary>
+        /// Writes the contents of this collection to the given <see cref="CodedOutputStream"/>,
+        /// encoding each value using the specified codec.
+        /// </summary>
+        /// <param name="output">The output stream to write to.</param>
+        /// <param name="codec">The codec to use when encoding each value.</param>
+        public void WriteTo(CodedOutputStream output, FieldCodec<T> codec)
+        {
+            if (count == 0)
+            {
+                return;
+            }
+            var writer = codec.ValueWriter;
+            var tag = codec.Tag;
+            if (codec.PackedRepeatedField)
+            {
+                // Packed primitive type
+                uint size = (uint)CalculatePackedDataSize(codec);
+                output.WriteTag(tag);
+                output.WriteRawVarint32(size);
+                for (int i = 0; i < count; i++)
+                {
+                    writer(output, array[i]);
+                }
+            }
+            else
+            {
+                // Not packed: a simple tag/value pair for each value.
+                // Can't use codec.WriteTagAndValue, as that omits default values.
+                for (int i = 0; i < count; i++)
+                {
+                    output.WriteTag(tag);
+                    writer(output, array[i]);
+                }
+            }
+        }
+
+        private void EnsureSize(int size)
+        {
+            if (array.Length < size)
+            {
+                size = Math.Max(size, MinArraySize);
+                int newSize = Math.Max(array.Length * 2, size);
+                var tmp = new T[newSize];
+                Array.Copy(array, 0, tmp, 0, array.Length);
+                array = tmp;
+            }
+        }
+
+        /// <summary>
+        /// Adds the specified item to the collection.
+        /// </summary>
+        /// <param name="item">The item to add.</param>
+        public void Add(T item)
+        {
+            ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
+            EnsureSize(count + 1);
+            array[count++] = item;
+        }
+
+        /// <summary>
+        /// Removes all items from the collection.
+        /// </summary>
+        public void Clear()
+        {
+            array = EmptyArray;
+            count = 0;
+        }
+
+        /// <summary>
+        /// Determines whether this collection contains the given item.
+        /// </summary>
+        /// <param name="item">The item to find.</param>
+        /// <returns><c>true</c> if this collection contains the given item; <c>false</c> otherwise.</returns>
+        public bool Contains(T item)
+        {
+            return IndexOf(item) != -1;
+        }
+
+        /// <summary>
+        /// Copies this collection to the given array.
+        /// </summary>
+        /// <param name="array">The array to copy to.</param>
+        /// <param name="arrayIndex">The first index of the array to copy to.</param>
+        public void CopyTo(T[] array, int arrayIndex)
+        {
+            Array.Copy(this.array, 0, array, arrayIndex, count);
+        }
+
+        /// <summary>
+        /// Removes the specified item from the collection
+        /// </summary>
+        /// <param name="item">The item to remove.</param>
+        /// <returns><c>true</c> if the item was found and removed; <c>false</c> otherwise.</returns>
+        public bool Remove(T item)
+        {
+            int index = IndexOf(item);
+            if (index == -1)
+            {
+                return false;
+            }            
+            Array.Copy(array, index + 1, array, index, count - index - 1);
+            count--;
+            array[count] = default(T);
+            return true;
+        }
+
+        /// <summary>
+        /// Gets the number of elements contained in the collection.
+        /// </summary>
+        public int Count => count;
+
+        /// <summary>
+        /// Gets a value indicating whether the collection is read-only.
+        /// </summary>
+        public bool IsReadOnly => false;
+
+        /// <summary>
+        /// Adds all of the specified values into this collection.
+        /// </summary>
+        /// <param name="values">The values to add to this collection.</param>
+        public void AddRange(IEnumerable<T> values)
+        {
+            ProtoPreconditions.CheckNotNull(values, nameof(values));
+
+            // Optimization 1: If the collection we're adding is already a RepeatedField<T>,
+            // we know the values are valid.
+            var otherRepeatedField = values as RepeatedField<T>;
+            if (otherRepeatedField != null)
+            {
+                EnsureSize(count + otherRepeatedField.count);
+                Array.Copy(otherRepeatedField.array, 0, array, count, otherRepeatedField.count);
+                count += otherRepeatedField.count;
+                return;
+            }
+
+            // Optimization 2: The collection is an ICollection, so we can expand
+            // just once and ask the collection to copy itself into the array.
+            var collection = values as ICollection;
+            if (collection != null)
+            {
+                var extraCount = collection.Count;
+                // For reference types and nullable value types, we need to check that there are no nulls
+                // present. (This isn't a thread-safe approach, but we don't advertise this is thread-safe.)
+                // We expect the JITter to optimize this test to true/false, so it's effectively conditional
+                // specialization.
+                if (default(T) == null)
+                {
+                    // TODO: Measure whether iterating once to check and then letting the collection copy
+                    // itself is faster or slower than iterating and adding as we go. For large
+                    // collections this will not be great in terms of cache usage... but the optimized
+                    // copy may be significantly faster than doing it one at a time.
+                    foreach (var item in collection)
+                    {
+                        if (item == null)
+                        {
+                            throw new ArgumentException("Sequence contained null element", nameof(values));
+                        }
+                    }
+                }
+                EnsureSize(count + extraCount);
+                collection.CopyTo(array, count);
+                count += extraCount;
+                return;
+            }
+
+            // We *could* check for ICollection<T> as well, but very very few collections implement
+            // ICollection<T> but not ICollection. (HashSet<T> does, for one...)
+
+            // Fall back to a slower path of adding items one at a time.
+            foreach (T item in values)
+            {
+                Add(item);
+            }
+        }
+
+        /// <summary>
+        /// Adds all of the specified values into this collection. This method is present to
+        /// allow repeated fields to be constructed from queries within collection initializers.
+        /// Within non-collection-initializer code, consider using the equivalent <see cref="AddRange"/>
+        /// method instead for clarity.
+        /// </summary>
+        /// <param name="values">The values to add to this collection.</param>
+        public void Add(IEnumerable<T> values)
+        {
+            AddRange(values);
+        }
+
+        /// <summary>
+        /// Returns an enumerator that iterates through the collection.
+        /// </summary>
+        /// <returns>
+        /// An enumerator that can be used to iterate through the collection.
+        /// </returns>
+        public IEnumerator<T> GetEnumerator()
+        {
+            for (int i = 0; i < count; i++)
+            {
+                yield return array[i];
+            }
+        }
+
+        /// <summary>
+        /// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
+        /// </summary>
+        /// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
+        /// <returns>
+        ///   <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
+        /// </returns>
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as RepeatedField<T>);
+        }
+
+        /// <summary>
+        /// Returns an enumerator that iterates through a collection.
+        /// </summary>
+        /// <returns>
+        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
+        /// </returns>
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        /// <summary>
+        /// Returns a hash code for this instance.
+        /// </summary>
+        /// <returns>
+        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
+        /// </returns>
+        public override int GetHashCode()
+        {
+            int hash = 0;
+            for (int i = 0; i < count; i++)
+            {
+                hash = hash * 31 + array[i].GetHashCode();
+            }
+            return hash;
+        }
+
+        /// <summary>
+        /// Compares this repeated field with another for equality.
+        /// </summary>
+        /// <param name="other">The repeated field to compare this with.</param>
+        /// <returns><c>true</c> if <paramref name="other"/> refers to an equal repeated field; <c>false</c> otherwise.</returns>
+        public bool Equals(RepeatedField<T> other)
+        {
+            if (ReferenceEquals(other, null))
+            {
+                return false;
+            }
+            if (ReferenceEquals(other, this))
+            {
+                return true;
+            }
+            if (other.Count != this.Count)
+            {
+                return false;
+            }
+            EqualityComparer<T> comparer = EqualityComparer;
+            for (int i = 0; i < count; i++)
+            {
+                if (!comparer.Equals(array[i], other.array[i]))
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /// <summary>
+        /// Returns the index of the given item within the collection, or -1 if the item is not
+        /// present.
+        /// </summary>
+        /// <param name="item">The item to find in the collection.</param>
+        /// <returns>The zero-based index of the item, or -1 if it is not found.</returns>
+        public int IndexOf(T item)
+        {
+            ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
+            EqualityComparer<T> comparer = EqualityComparer;
+            for (int i = 0; i < count; i++)
+            {
+                if (comparer.Equals(array[i], item))
+                {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        /// <summary>
+        /// Inserts the given item at the specified index.
+        /// </summary>
+        /// <param name="index">The index at which to insert the item.</param>
+        /// <param name="item">The item to insert.</param>
+        public void Insert(int index, T item)
+        {
+            ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
+            if (index < 0 || index > count)
+            {
+                throw new ArgumentOutOfRangeException(nameof(index));
+            }
+            EnsureSize(count + 1);
+            Array.Copy(array, index, array, index + 1, count - index);
+            array[index] = item;
+            count++;
+        }
+
+        /// <summary>
+        /// Removes the item at the given index.
+        /// </summary>
+        /// <param name="index">The zero-based index of the item to remove.</param>
+        public void RemoveAt(int index)
+        {
+            if (index < 0 || index >= count)
+            {
+                throw new ArgumentOutOfRangeException(nameof(index));
+            }
+            Array.Copy(array, index + 1, array, index, count - index - 1);
+            count--;
+            array[count] = default(T);
+        }
+
+        /// <summary>
+        /// Returns a string representation of this repeated field, in the same
+        /// way as it would be represented by the default JSON formatter.
+        /// </summary>
+        public override string ToString()
+        {
+            var writer = new StringWriter();
+            JsonFormatter.Default.WriteList(writer, this);
+            return writer.ToString();
+        }
+
+        /// <summary>
+        /// Gets or sets the item at the specified index.
+        /// </summary>
+        /// <value>
+        /// The element at the specified index.
+        /// </value>
+        /// <param name="index">The zero-based index of the element to get or set.</param>
+        /// <returns>The item at the specified index.</returns>
+        public T this[int index]
+        {
+            get
+            {
+                if (index < 0 || index >= count)
+                {
+                    throw new ArgumentOutOfRangeException(nameof(index));
+                }
+                return array[index];
+            }
+            set
+            {
+                if (index < 0 || index >= count)
+                {
+                    throw new ArgumentOutOfRangeException(nameof(index));
+                }
+                ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
+                array[index] = value;
+            }
+        }
+
+        #region Explicit interface implementation for IList and ICollection.
+        bool IList.IsFixedSize => false;
+
+        void ICollection.CopyTo(Array array, int index)
+        {
+            Array.Copy(this.array, 0, array, index, count);
+        }
+
+        bool ICollection.IsSynchronized => false;
+
+        object ICollection.SyncRoot => this;
+
+        object IList.this[int index]
+        {
+            get { return this[index]; }
+            set { this[index] = (T)value; }
+        }
+
+        int IList.Add(object value)
+        {
+            Add((T) value);
+            return count - 1;
+        }
+
+        bool IList.Contains(object value)
+        {
+            return (value is T && Contains((T)value));
+        }
+
+        int IList.IndexOf(object value)
+        {
+            if (!(value is T))
+            {
+                return -1;
+            }
+            return IndexOf((T)value);
+        }
+
+        void IList.Insert(int index, object value)
+        {
+            Insert(index, (T) value);
+        }
+
+        void IList.Remove(object value)
+        {
+            if (!(value is T))
+            {
+                return;
+            }
+            Remove((T)value);
+        }
+        #endregion        
+    }
+}

+ 6 - 1
csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs

@@ -125,5 +125,10 @@ namespace Google.Protobuf
             return new InvalidProtocolBufferException(
                 "Stream of protocol messages had invalid tag. Expected tag is length-delimited field 1.");
         }
-    }
+
+        internal static InvalidProtocolBufferException MissingFields()
+        {
+            return new InvalidProtocolBufferException("Message was missing required fields");
+        }
+}
 }

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

@@ -30,7 +30,10 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
+using Google.Protobuf.Reflection;
+using System.Collections;
 using System.IO;
+using System.Linq;
 
 namespace Google.Protobuf
 {
@@ -140,6 +143,53 @@ namespace Google.Protobuf
             return ByteString.AttachBytes(message.ToByteArray());
         }
 
+        /// <summary>
+        /// Checks if all required fields in a message have values set. For proto3 messages, this returns true
+        /// </summary>
+        public static bool IsInitialized(this IMessage message)
+        {
+            if (message.Descriptor.File.Proto.Syntax != "proto2")
+            {
+                return true;
+            }
+
+            return message.Descriptor
+                .Fields
+                .InDeclarationOrder()
+                .All(f =>
+                {
+                    if (f.IsMap)
+                    {
+                        var map = (IDictionary)f.Accessor.GetValue(message);
+                        return map.Values.OfType<IMessage>().All(IsInitialized);
+                    }
+                    else if (f.IsRepeated && f.MessageType != null)
+                    {
+                        var enumerable = (IEnumerable)f.Accessor.GetValue(message);
+                        return enumerable.Cast<IMessage>().All(IsInitialized);
+                    }
+                    else if (f.MessageType != null)
+                    {
+                        if (f.Accessor.HasValue(message))
+                        {
+                            return ((IMessage)f.Accessor.GetValue(message)).IsInitialized();
+                        }
+                        else
+                        {
+                            return !f.IsRequired;
+                        }
+                    }
+                    else if (f.IsRequired)
+                    {
+                        return f.Accessor.HasValue(message);
+                    }
+                    else 
+                    {
+                        return true;
+                    }
+                });
+        }
+
         // Implementations allowing unknown fields to be discarded.
         internal static void MergeFrom(this IMessage message, byte[] data, bool discardUnknownFields)
         {

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

@@ -69,6 +69,7 @@ namespace Google.Protobuf
         {
             IMessage message = factory();
             message.MergeFrom(data, DiscardUnknownFields);
+            CheckMergedRequiredFields(message);
             return message;
         }
 
@@ -83,6 +84,7 @@ namespace Google.Protobuf
         {
             IMessage message = factory();
             message.MergeFrom(data, offset, length, DiscardUnknownFields);
+            CheckMergedRequiredFields(message);
             return message;
         }
 
@@ -95,6 +97,7 @@ namespace Google.Protobuf
         {
             IMessage message = factory();
             message.MergeFrom(data, DiscardUnknownFields);
+            CheckMergedRequiredFields(message);
             return message;
         }
 
@@ -107,6 +110,7 @@ namespace Google.Protobuf
         {
             IMessage message = factory();
             message.MergeFrom(input, DiscardUnknownFields);
+            CheckMergedRequiredFields(message);
             return message;
         }
 
@@ -123,6 +127,7 @@ namespace Google.Protobuf
         {
             IMessage message = factory();
             message.MergeDelimitedFrom(input, DiscardUnknownFields);
+            CheckMergedRequiredFields(message);
             return message;
         }
 
@@ -135,6 +140,7 @@ namespace Google.Protobuf
         {
             IMessage message = factory();
             MergeFrom(message, input);
+            CheckMergedRequiredFields(message);
             return message;
         }
 
@@ -167,6 +173,12 @@ namespace Google.Protobuf
             }
         }
 
+        internal static void CheckMergedRequiredFields(IMessage message)
+        {
+            if (!message.IsInitialized())
+                throw new InvalidOperationException("Parsed message does not contain all required fields");
+        }
+
         /// <summary>
         /// Creates a new message parser which optionally discards unknown fields when parsing.
         /// </summary>

+ 78 - 78
csharp/src/Google.Protobuf/ProtoPreconditions.cs

@@ -1,79 +1,79 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc.  All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#endregion
-
-using System;
-
-namespace Google.Protobuf
-{
-    /// <summary>
-    /// Helper methods for throwing exceptions when preconditions are not met.
-    /// </summary>
-    /// <remarks>
-    /// This class is used internally and by generated code; it is not particularly
-    /// expected to be used from application code, although nothing prevents it
-    /// from being used that way.
-    /// </remarks>
-    public static class ProtoPreconditions
-    {
-        /// <summary>
-        /// Throws an ArgumentNullException if the given value is null, otherwise
-        /// return the value to the caller.
-        /// </summary>
-        public static T CheckNotNull<T>(T value, string name) where T : class
-        {
-            if (value == null)
-            {
-                throw new ArgumentNullException(name);
-            }
-            return value;
-        }
-
-        /// <summary>
-        /// Throws an ArgumentNullException if the given value is null, otherwise
-        /// return the value to the caller.
-        /// </summary>
-        /// <remarks>
-        /// This is equivalent to <see cref="CheckNotNull{T}(T, string)"/> but without the type parameter
-        /// constraint. In most cases, the constraint is useful to prevent you from calling CheckNotNull
-        /// with a value type - but it gets in the way if either you want to use it with a nullable
-        /// value type, or you want to use it with an unconstrained type parameter.
-        /// </remarks>
-        internal static T CheckNotNullUnconstrained<T>(T value, string name)
-        {
-            if (value == null)
-            {
-                throw new ArgumentNullException(name);
-            }
-            return value;
-        }
-    }
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Helper methods for throwing exceptions when preconditions are not met.
+    /// </summary>
+    /// <remarks>
+    /// This class is used internally and by generated code; it is not particularly
+    /// expected to be used from application code, although nothing prevents it
+    /// from being used that way.
+    /// </remarks>
+    public static class ProtoPreconditions
+    {
+        /// <summary>
+        /// Throws an ArgumentNullException if the given value is null, otherwise
+        /// return the value to the caller.
+        /// </summary>
+        public static T CheckNotNull<T>(T value, string name) where T : class
+        {
+            if (value == null)
+            {
+                throw new ArgumentNullException(name);
+            }
+            return value;
+        }
+
+        /// <summary>
+        /// Throws an ArgumentNullException if the given value is null, otherwise
+        /// return the value to the caller.
+        /// </summary>
+        /// <remarks>
+        /// This is equivalent to <see cref="CheckNotNull{T}(T, string)"/> but without the type parameter
+        /// constraint. In most cases, the constraint is useful to prevent you from calling CheckNotNull
+        /// with a value type - but it gets in the way if either you want to use it with a nullable
+        /// value type, or you want to use it with an unconstrained type parameter.
+        /// </remarks>
+        internal static T CheckNotNullUnconstrained<T>(T value, string name)
+        {
+            if (value == null)
+            {
+                throw new ArgumentNullException(name);
+            }
+            return value;
+        }
+    }
 }

File diff suppressed because it is too large
+ 359 - 101
csharp/src/Google.Protobuf/Reflection/Descriptor.cs


+ 1 - 0
csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs

@@ -57,6 +57,7 @@ namespace Google.Protobuf.Reflection
             return getValueDelegate(message);
         }
 
+        public abstract bool HasValue(IMessage message);
         public abstract void Clear(IMessage message);
         public abstract void SetValue(IMessage message, object value);
     }

+ 15 - 16
csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs

@@ -79,8 +79,7 @@ namespace Google.Protobuf.Reflection
                 throw new DescriptorValidationException(this, "Field numbers must be positive integers.");
             }
             ContainingType = parent;
-            // OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnConstruction.
-            if (proto.OneofIndex != -1)
+            if (proto.HasOneofIndex)
             {
                 if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.OneofDecl.Count)
                 {
@@ -184,6 +183,11 @@ namespace Google.Protobuf.Reflection
         /// </summary>
         public bool IsRepeated => Proto.Label == FieldDescriptorProto.Types.Label.Repeated;
 
+        /// <summary>
+        /// Returns <c>true</c> if this field is a required field; <c>false</c> otherwise.
+        /// </summary>
+        public bool IsRequired => Proto.Label == FieldDescriptorProto.Types.Label.Required;
+
         /// <summary>
         /// Returns <c>true</c> if this field is a map field; <c>false</c> otherwise.
         /// </summary>
@@ -192,13 +196,8 @@ namespace Google.Protobuf.Reflection
         /// <summary>
         /// Returns <c>true</c> if this field is a packed, repeated field; <c>false</c> otherwise.
         /// </summary>
-        public bool IsPacked => 
-            // Note the || rather than && here - we're effectively defaulting to packed, because that *is*
-            // the default in proto3, which is all we support. We may give the wrong result for the protos
-            // within descriptor.proto, but that's okay, as they're never exposed and we don't use IsPacked
-            // within the runtime.
-            Proto.Options == null || Proto.Options.Packed;
-        
+        public bool IsPacked => File.Proto.Syntax == "proto2" ? Proto.Options?.Packed ?? false : !Proto.Options.HasPacked || Proto.Options.Packed;
+
         /// <summary>
         /// Returns the type of the field.
         /// </summary>
@@ -247,9 +246,9 @@ namespace Google.Protobuf.Reflection
         {
             get
             {
-                if (fieldType != FieldType.Message)
+                if (fieldType != FieldType.Message && fieldType != FieldType.Group)
                 {
-                    throw new InvalidOperationException("MessageType is only valid for message fields.");
+                    throw new InvalidOperationException("MessageType is only valid for message or group fields.");
                 }
                 return messageType;
             }
@@ -265,12 +264,12 @@ namespace Google.Protobuf.Reflection
         /// </summary>
         internal void CrossLink()
         {
-            if (Proto.TypeName != "")
+            if (Proto.HasTypeName)
             {
                 IDescriptor typeDescriptor =
                     File.DescriptorPool.LookupSymbol(Proto.TypeName, this);
 
-                if (Proto.Type != 0)
+                if (Proto.HasType)
                 {
                     // Choose field type based on symbol.
                     if (typeDescriptor is MessageDescriptor)
@@ -287,7 +286,7 @@ namespace Google.Protobuf.Reflection
                     }
                 }
 
-                if (fieldType == FieldType.Message)
+                if (fieldType == FieldType.Message || fieldType == FieldType.Group)
                 {
                     if (!(typeDescriptor is MessageDescriptor))
                     {
@@ -295,7 +294,7 @@ namespace Google.Protobuf.Reflection
                     }
                     messageType = (MessageDescriptor) typeDescriptor;
 
-                    if (Proto.DefaultValue != "")
+                    if (Proto.HasDefaultValue)
                     {
                         throw new DescriptorValidationException(this, "Messages can't have default values.");
                     }
@@ -325,7 +324,7 @@ namespace Google.Protobuf.Reflection
 
             File.DescriptorPool.AddFieldByNumber(this);
 
-            if (ContainingType != null && ContainingType.Proto.Options != null && ContainingType.Proto.Options.MessageSetWireFormat)
+            if (ContainingType != null && ContainingType.Proto.HasOptions && ContainingType.Proto.Options.MessageSetWireFormat)
             {
                 throw new DescriptorValidationException(this, "MessageSet format is not supported.");
             }

+ 1 - 1
csharp/src/Google.Protobuf/Reflection/FieldType.cs

@@ -74,7 +74,7 @@ namespace Google.Protobuf.Reflection
         /// </summary>
         String,
         /// <summary>
-        /// The field type used for groups (not supported in this implementation).
+        /// The field type used for groups.
         /// </summary>
         Group,
         /// <summary>

+ 5 - 0
csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs

@@ -51,6 +51,11 @@ namespace Google.Protobuf.Reflection
         /// </summary>
         void Clear(IMessage message);
 
+        /// <summary>
+        /// Indicates whether the field in the specified message is set. For proto3 fields, this throws an <see cref="InvalidOperationException"/>
+        /// </summary>
+        bool HasValue(IMessage message);
+
         /// <summary>
         /// Fetches the field value. For repeated values, this will be an
         /// <see cref="IList"/> implementation. For map values, this will be an

+ 5 - 0
csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs

@@ -51,6 +51,11 @@ namespace Google.Protobuf.Reflection
             list.Clear();
         }
 
+        public override bool HasValue(IMessage message)
+        {
+            throw new InvalidOperationException("HasValue is not implemented for map fields");
+        }
+
         public override void SetValue(IMessage message, object value)
         {
             throw new InvalidOperationException("SetValue is not implemented for map fields");

+ 0 - 59
csharp/src/Google.Protobuf/Reflection/PartialClasses.cs

@@ -1,59 +0,0 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc.  All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#endregion
-
-// This file just contains partial classes for any autogenerated classes that need additional support.
-namespace Google.Protobuf.Reflection
-{
-    internal partial class FieldDescriptorProto
-    {
-        // We can't tell the difference between "explicitly set to 0" and "not set"
-        // in proto3, but we need to tell the difference for OneofIndex. descriptor.proto
-        // is really a proto2 file, but the runtime doesn't know about proto2 semantics...
-        // We fake it by defaulting to -1.
-        partial void OnConstruction()
-        {
-            OneofIndex = -1;
-        }
-    }
-
-    internal partial class FieldOptions
-    {
-        // We can't tell the difference between "explicitly set to false" and "not set"
-        // in proto3, but we need to tell the difference for FieldDescriptor.IsPacked.
-        // This won't work if we ever need to support proto2, but at that point we'll be
-        // able to remove this hack and use field presence instead. 
-        partial void OnConstruction()
-        {
-            Packed = true;
-        }
-    }
-}

+ 10 - 0
csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs

@@ -112,6 +112,9 @@ namespace Google.Protobuf.Reflection
         internal static Action<IMessage> CreateActionIMessage(MethodInfo method) =>
             GetReflectionHelper(method.DeclaringType, typeof(object)).CreateActionIMessage(method);
 
+        internal static Func<IMessage, bool> CreateFuncIMessageBool(MethodInfo method) =>
+            GetReflectionHelper(method.DeclaringType, method.ReturnType).CreateFuncIMessageBool(method);
+
         /// <summary>
         /// Creates a reflection helper for the given type arguments. Currently these are created on demand
         /// rather than cached; this will be "busy" when initially loading a message's descriptor, but after that
@@ -129,6 +132,7 @@ namespace Google.Protobuf.Reflection
             Action<IMessage> CreateActionIMessage(MethodInfo method);
             Func<IMessage, object> CreateFuncIMessageObject(MethodInfo method);
             Action<IMessage, object> CreateActionIMessageObject(MethodInfo method);
+            Func<IMessage, bool> CreateFuncIMessageBool(MethodInfo method);
         }
 
         private class ReflectionHelper<T1, T2> : IReflectionHelper
@@ -170,6 +174,12 @@ namespace Google.Protobuf.Reflection
                 var del = (Action<T1, T2>) method.CreateDelegate(typeof(Action<T1, T2>));
                 return (message, arg) => del((T1) message, (T2) arg);
             }
+
+            public Func<IMessage, bool> CreateFuncIMessageBool(MethodInfo method)
+            {
+                var del = (Func<T1, bool>)method.CreateDelegate(typeof(Func<T1, bool>));
+                return message => del((T1)message);
+            }
         }
 
         // Runtime compatibility checking code - see ReflectionHelper<T1, T2>.CreateFuncIMessageInt32 for

+ 5 - 0
csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs

@@ -51,6 +51,11 @@ namespace Google.Protobuf.Reflection
             list.Clear();
         }
 
+        public override bool HasValue(IMessage message)
+        {
+            throw new InvalidOperationException("HasValue is not implemented for repeated fields");
+        }
+
         public override void SetValue(IMessage message, object value)
         {
             throw new InvalidOperationException("SetValue is not implemented for repeated fields");

+ 24 - 9
csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs

@@ -48,6 +48,7 @@ namespace Google.Protobuf.Reflection
 
         private readonly Action<IMessage, object> setValueDelegate;
         private readonly Action<IMessage> clearDelegate;
+        private readonly Func<IMessage, bool> hasDelegate;
 
         internal SingleFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor)
         {
@@ -56,16 +57,25 @@ namespace Google.Protobuf.Reflection
                 throw new ArgumentException("Not all required properties/methods available");
             }
             setValueDelegate = ReflectionUtil.CreateActionIMessageObject(property.GetSetMethod());
+            if (descriptor.File.Proto.Syntax == "proto2")
+            {
+                MethodInfo hasMethod = property.DeclaringType.GetRuntimeProperty("Has" + property.Name).GetMethod;
+                hasDelegate = ReflectionUtil.CreateFuncIMessageBool(hasMethod ?? throw new ArgumentException("Not all required properties/methods are available"));
+                MethodInfo clearMethod = property.DeclaringType.GetRuntimeMethod("Clear" + property.Name, ReflectionUtil.EmptyTypes);
+                clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod ?? throw new ArgumentException("Not all required properties/methods are available"));
+            }
+            else
+            {
+                hasDelegate = (_) => throw new InvalidOperationException("HasValue is not implemented for proto3 fields"); var clrType = property.PropertyType;
 
-            var clrType = property.PropertyType;
-            
-            // TODO: Validate that this is a reasonable single field? (Should be a value type, a message type, or string/ByteString.)
-            object defaultValue =
-                descriptor.FieldType == FieldType.Message ? null
-                : clrType == typeof(string) ? ""
-                : clrType == typeof(ByteString) ? ByteString.Empty
-                : Activator.CreateInstance(clrType);
-            clearDelegate = message => SetValue(message, defaultValue);
+                // TODO: Validate that this is a reasonable single field? (Should be a value type, a message type, or string/ByteString.) 
+                object defaultValue =
+                    descriptor.FieldType == FieldType.Message ? null
+                    : clrType == typeof(string) ? ""
+                    : clrType == typeof(ByteString) ? ByteString.Empty
+                    : Activator.CreateInstance(clrType);
+                clearDelegate = message => SetValue(message, defaultValue);
+            }
         }
 
         public override void Clear(IMessage message)
@@ -73,6 +83,11 @@ namespace Google.Protobuf.Reflection
             clearDelegate(message);
         }
 
+        public override bool HasValue(IMessage message)
+        {
+            return hasDelegate(message);
+        }
+
         public override void SetValue(IMessage message, object value)
         {
             setValueDelegate(message, value);

+ 5 - 5
csharp/src/Google.Protobuf/WellKnownTypes/Api.cs

@@ -328,7 +328,7 @@ namespace Google.Protobuf.WellKnownTypes {
       }
       if (other.sourceContext_ != null) {
         if (sourceContext_ == null) {
-          sourceContext_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
+          SourceContext = new global::Google.Protobuf.WellKnownTypes.SourceContext();
         }
         SourceContext.MergeFrom(other.SourceContext);
       }
@@ -365,9 +365,9 @@ namespace Google.Protobuf.WellKnownTypes {
           }
           case 42: {
             if (sourceContext_ == null) {
-              sourceContext_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
+              SourceContext = new global::Google.Protobuf.WellKnownTypes.SourceContext();
             }
-            input.ReadMessage(sourceContext_);
+            input.ReadMessage(SourceContext);
             break;
           }
           case 50: {
@@ -375,7 +375,7 @@ namespace Google.Protobuf.WellKnownTypes {
             break;
           }
           case 56: {
-            syntax_ = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
+            Syntax = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
             break;
           }
         }
@@ -688,7 +688,7 @@ namespace Google.Protobuf.WellKnownTypes {
             break;
           }
           case 56: {
-            syntax_ = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
+            Syntax = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
             break;
           }
         }

+ 13 - 13
csharp/src/Google.Protobuf/WellKnownTypes/Type.cs

@@ -312,7 +312,7 @@ namespace Google.Protobuf.WellKnownTypes {
       options_.Add(other.options_);
       if (other.sourceContext_ != null) {
         if (sourceContext_ == null) {
-          sourceContext_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
+          SourceContext = new global::Google.Protobuf.WellKnownTypes.SourceContext();
         }
         SourceContext.MergeFrom(other.SourceContext);
       }
@@ -348,13 +348,13 @@ namespace Google.Protobuf.WellKnownTypes {
           }
           case 42: {
             if (sourceContext_ == null) {
-              sourceContext_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
+              SourceContext = new global::Google.Protobuf.WellKnownTypes.SourceContext();
             }
-            input.ReadMessage(sourceContext_);
+            input.ReadMessage(SourceContext);
             break;
           }
           case 48: {
-            syntax_ = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
+            Syntax = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
             break;
           }
         }
@@ -726,11 +726,11 @@ namespace Google.Protobuf.WellKnownTypes {
             _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
             break;
           case 8: {
-            kind_ = (global::Google.Protobuf.WellKnownTypes.Field.Types.Kind) input.ReadEnum();
+            Kind = (global::Google.Protobuf.WellKnownTypes.Field.Types.Kind) input.ReadEnum();
             break;
           }
           case 16: {
-            cardinality_ = (global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality) input.ReadEnum();
+            Cardinality = (global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality) input.ReadEnum();
             break;
           }
           case 24: {
@@ -1084,7 +1084,7 @@ namespace Google.Protobuf.WellKnownTypes {
       options_.Add(other.options_);
       if (other.sourceContext_ != null) {
         if (sourceContext_ == null) {
-          sourceContext_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
+          SourceContext = new global::Google.Protobuf.WellKnownTypes.SourceContext();
         }
         SourceContext.MergeFrom(other.SourceContext);
       }
@@ -1116,13 +1116,13 @@ namespace Google.Protobuf.WellKnownTypes {
           }
           case 34: {
             if (sourceContext_ == null) {
-              sourceContext_ = new global::Google.Protobuf.WellKnownTypes.SourceContext();
+              SourceContext = new global::Google.Protobuf.WellKnownTypes.SourceContext();
             }
-            input.ReadMessage(sourceContext_);
+            input.ReadMessage(SourceContext);
             break;
           }
           case 40: {
-            syntax_ = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
+            Syntax = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
             break;
           }
         }
@@ -1467,7 +1467,7 @@ namespace Google.Protobuf.WellKnownTypes {
       }
       if (other.value_ != null) {
         if (value_ == null) {
-          value_ = new global::Google.Protobuf.WellKnownTypes.Any();
+          Value = new global::Google.Protobuf.WellKnownTypes.Any();
         }
         Value.MergeFrom(other.Value);
       }
@@ -1488,9 +1488,9 @@ namespace Google.Protobuf.WellKnownTypes {
           }
           case 18: {
             if (value_ == null) {
-              value_ = new global::Google.Protobuf.WellKnownTypes.Any();
+              Value = new global::Google.Protobuf.WellKnownTypes.Any();
             }
-            input.ReadMessage(value_);
+            input.ReadMessage(Value);
             break;
           }
         }

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

@@ -61,11 +61,11 @@ namespace Google.Protobuf
             /// </summary>
             LengthDelimited = 2,
             /// <summary>
-            /// A "start group" value - not supported by this implementation.
+            /// A "start group" value
             /// </summary>
             StartGroup = 3,
             /// <summary>
-            /// An "end group" value - not supported by this implementation.
+            /// An "end group" value
             /// </summary>
             EndGroup = 4,
             /// <summary>

+ 5 - 5
src/google/protobuf/compiler/csharp/csharp_enum_field.cc

@@ -47,8 +47,8 @@ namespace compiler {
 namespace csharp {
 
 EnumFieldGenerator::EnumFieldGenerator(const FieldDescriptor* descriptor,
-                                       int fieldOrdinal, const Options *options)
-    : PrimitiveFieldGenerator(descriptor, fieldOrdinal, options) {
+                                       int presenceIndex, const Options *options)
+    : PrimitiveFieldGenerator(descriptor, presenceIndex, options) {
 }
 
 EnumFieldGenerator::~EnumFieldGenerator() {
@@ -56,7 +56,7 @@ EnumFieldGenerator::~EnumFieldGenerator() {
 
 void EnumFieldGenerator::GenerateParsingCode(io::Printer* printer) {
   printer->Print(variables_,
-    "$name$_ = ($type_name$) input.ReadEnum();\n");
+    "$property_name$ = ($type_name$) input.ReadEnum();\n");
 }
 
 void EnumFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
@@ -82,8 +82,8 @@ void EnumFieldGenerator::GenerateCodecCode(io::Printer* printer) {
 }
 
 EnumOneofFieldGenerator::EnumOneofFieldGenerator(
-    const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options)
-  : PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal, options) {
+    const FieldDescriptor* descriptor, int presenceIndex, const Options *options)
+  : PrimitiveOneofFieldGenerator(descriptor, presenceIndex, options) {
 }
 
 EnumOneofFieldGenerator::~EnumOneofFieldGenerator() {

+ 2 - 2
src/google/protobuf/compiler/csharp/csharp_enum_field.h

@@ -44,7 +44,7 @@ namespace csharp {
 class EnumFieldGenerator : public PrimitiveFieldGenerator {
  public:
   EnumFieldGenerator(const FieldDescriptor* descriptor,
-                     int fieldOrdinal,
+                     int presenceIndex,
                      const Options *options);
   ~EnumFieldGenerator();
 
@@ -60,7 +60,7 @@ class EnumFieldGenerator : public PrimitiveFieldGenerator {
 class EnumOneofFieldGenerator : public PrimitiveOneofFieldGenerator {
  public:
   EnumOneofFieldGenerator(const FieldDescriptor* descriptor,
-                          int fieldOrdinal,
+                          int presenceIndex,
                           const Options *options);
   ~EnumOneofFieldGenerator();
 

+ 52 - 53
src/google/protobuf/compiler/csharp/csharp_field_base.cc

@@ -57,6 +57,9 @@ void FieldGeneratorBase::SetCommonFieldVariables(
   // repeated fields varies by wire format. The wire format is encoded in the bottom 3 bits, which
   // never effects the tag size.
   int tag_size = internal::WireFormat::TagSize(descriptor_->number(), descriptor_->type());
+  if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) {
+    tag_size /= 2;
+  }
   uint tag = internal::WireFormat::MakeTag(descriptor_);
   uint8 tag_array[5];
   io::CodedOutputStream::WriteTagToArray(tag, tag_array);
@@ -75,34 +78,52 @@ void FieldGeneratorBase::SetCommonFieldVariables(
   (*variables)["name"] = name();
   (*variables)["descriptor_name"] = descriptor_->name();
   (*variables)["default_value"] = default_value();
-  if (has_default_value()) {
+  (*variables)["capitalized_type_name"] = capitalized_type_name();
+  (*variables)["number"] = number();
+  if (has_default_value() && !IsProto2(descriptor_->file())) {
     (*variables)["name_def_message"] =
       (*variables)["name"] + "_ = " + (*variables)["default_value"];
   } else {
     (*variables)["name_def_message"] = (*variables)["name"] + "_";
   }
-  (*variables)["capitalized_type_name"] = capitalized_type_name();
-  (*variables)["number"] = number();
-  (*variables)["has_property_check"] =
-    (*variables)["property_name"] + " != " + (*variables)["default_value"];
-  (*variables)["other_has_property_check"] = "other." +
-    (*variables)["property_name"] + " != " + (*variables)["default_value"];
+  if (IsProto2(descriptor_->file())) {
+    (*variables)["has_property_check"] = "Has" + (*variables)["property_name"];
+    (*variables)["other_has_property_check"] = "other.Has" + (*variables)["property_name"];
+    (*variables)["has_not_property_check"] = "!" + (*variables)["has_property_check"];
+    (*variables)["other_has_not_property_check"] = "!" + (*variables)["other_has_property_check"];
+    if (presenceIndex_ != -1) {
+      string hasBitsNumber = SimpleItoa(presenceIndex_ / 32);
+      string hasBitsMask = SimpleItoa(1 << (presenceIndex_ % 32));
+      (*variables)["has_field_check"] = "(_hasBits" + hasBitsNumber + " & " + hasBitsMask + ") != 0";
+      (*variables)["set_has_field"] = "_hasBits" + hasBitsNumber + " |= " + hasBitsMask;
+      (*variables)["clear_has_field"] = "_hasBits" + hasBitsNumber + " &= ~" + hasBitsMask;
+    }
+  } else {
+    (*variables)["has_property_check"] =
+      (*variables)["property_name"] + " != " + (*variables)["default_value"];
+    (*variables)["other_has_property_check"] = "other." +
+      (*variables)["property_name"] + " != " + (*variables)["default_value"];
+  }
 }
 
 void FieldGeneratorBase::SetCommonOneofFieldVariables(
     std::map<string, string>* variables) {
   (*variables)["oneof_name"] = oneof_name();
-  (*variables)["has_property_check"] =
-    oneof_name() + "Case_ == " + oneof_property_name() +
-    "OneofCase." + property_name();
+  if (IsProto2(descriptor_->file())) {
+    (*variables)["has_property_check"] = "Has" + property_name();
+  } else {
+    (*variables)["has_property_check"] =
+      oneof_name() + "Case_ == " + oneof_property_name() +
+      "OneofCase." + property_name();
+  }
   (*variables)["oneof_property_name"] = oneof_property_name();
 }
 
 FieldGeneratorBase::FieldGeneratorBase(const FieldDescriptor* descriptor,
-                                       int fieldOrdinal, const Options* options)
+                                       int presenceIndex, const Options* options)
     : SourceGeneratorBase(descriptor->file(), options),
       descriptor_(descriptor),
-      fieldOrdinal_(fieldOrdinal) {
+      presenceIndex_(presenceIndex) {
   SetCommonFieldVariables(&variables_);
 }
 
@@ -251,36 +272,6 @@ bool FieldGeneratorBase::has_default_value() {
   }
 }
 
-bool FieldGeneratorBase::is_nullable_type() {
-  switch (descriptor_->type()) {
-    case FieldDescriptor::TYPE_ENUM:
-    case FieldDescriptor::TYPE_DOUBLE:
-    case FieldDescriptor::TYPE_FLOAT:
-    case FieldDescriptor::TYPE_INT64:
-    case FieldDescriptor::TYPE_UINT64:
-    case FieldDescriptor::TYPE_INT32:
-    case FieldDescriptor::TYPE_FIXED64:
-    case FieldDescriptor::TYPE_FIXED32:
-    case FieldDescriptor::TYPE_BOOL:
-    case FieldDescriptor::TYPE_UINT32:
-    case FieldDescriptor::TYPE_SFIXED32:
-    case FieldDescriptor::TYPE_SFIXED64:
-    case FieldDescriptor::TYPE_SINT32:
-    case FieldDescriptor::TYPE_SINT64:
-      return false;
-
-    case FieldDescriptor::TYPE_MESSAGE:
-    case FieldDescriptor::TYPE_GROUP:
-    case FieldDescriptor::TYPE_STRING:
-    case FieldDescriptor::TYPE_BYTES:
-      return true;
-
-    default:
-      GOOGLE_LOG(FATAL)<< "Unknown field type.";
-      return true;
-  }
-}
-
 bool AllPrintableAscii(const std::string& text) {
   for(int i = 0; i < text.size(); i++) {
     if (text[i] < 0x20 || text[i] > 0x7e) {
@@ -290,14 +281,18 @@ bool AllPrintableAscii(const std::string& text) {
   return true;
 }
 
-std::string FieldGeneratorBase::GetStringDefaultValueInternal() {
-  // No other default values needed for proto3...
-  return "\"\"";
+std::string FieldGeneratorBase::GetStringDefaultValueInternal(const FieldDescriptor* descriptor) {
+    if (descriptor->default_value_string().empty())
+        return "\"\"";
+    else
+        return "global::System.Encoding.UTF8.GetString(global::System.Convert.FromBase64String(\" +" + StringToBase64(descriptor->default_value_string()) + " +\"))";
 }
 
-std::string FieldGeneratorBase::GetBytesDefaultValueInternal() {
-  // No other default values needed for proto3...
-  return "pb::ByteString.Empty";
+std::string FieldGeneratorBase::GetBytesDefaultValueInternal(const FieldDescriptor* descriptor) {
+    if (descriptor->default_value_string().empty())
+        return "pb::ByteString.Empty";
+    else
+        return "pb::ByteString.FromBase64(\"" + StringToBase64(descriptor->default_value_string()) + "\")";
 }
 
 std::string FieldGeneratorBase::default_value() {
@@ -307,9 +302,13 @@ std::string FieldGeneratorBase::default_value() {
 std::string FieldGeneratorBase::default_value(const FieldDescriptor* descriptor) {
   switch (descriptor->type()) {
     case FieldDescriptor::TYPE_ENUM:
-      // All proto3 enums have a default value of 0, and there's an implicit conversion from the constant 0 to
-      // any C# enum. This means we don't need to work out what we actually mapped the enum value name to.
-     return "0";
+      if (IsProto2(descriptor_->file())) {
+        return GetClassName(descriptor->default_value_enum()->type()) + "." + 
+          GetEnumValueName(descriptor->default_value_enum()->type()->name(), descriptor->default_value_enum()->name());
+      }
+      else {
+        return "0";
+      }
     case FieldDescriptor::TYPE_MESSAGE:
     case FieldDescriptor::TYPE_GROUP:
       if (IsWrapperType(descriptor)) {
@@ -357,9 +356,9 @@ std::string FieldGeneratorBase::default_value(const FieldDescriptor* descriptor)
         return "false";
       }
     case FieldDescriptor::TYPE_STRING:
-      return GetStringDefaultValueInternal();
+      return GetStringDefaultValueInternal(descriptor);
     case FieldDescriptor::TYPE_BYTES:
-      return GetBytesDefaultValueInternal();
+      return GetBytesDefaultValueInternal(descriptor);
     case FieldDescriptor::TYPE_UINT32:
       return SimpleItoa(descriptor->default_value_uint32());
     case FieldDescriptor::TYPE_SFIXED32:

+ 4 - 5
src/google/protobuf/compiler/csharp/csharp_field_base.h

@@ -47,7 +47,7 @@ namespace csharp {
 class FieldGeneratorBase : public SourceGeneratorBase {
  public:
   FieldGeneratorBase(const FieldDescriptor* descriptor,
-                     int fieldOrdinal,
+                     int presenceIndex,
                      const Options* options);
   ~FieldGeneratorBase();
 
@@ -67,7 +67,7 @@ class FieldGeneratorBase : public SourceGeneratorBase {
 
  protected:
   const FieldDescriptor* descriptor_;
-  const int fieldOrdinal_;
+  const int presenceIndex_;
   std::map<string, string> variables_;
 
   void AddDeprecatedFlag(io::Printer* printer);
@@ -84,7 +84,6 @@ class FieldGeneratorBase : public SourceGeneratorBase {
   std::string type_name();
   std::string type_name(const FieldDescriptor* descriptor);
   bool has_default_value();
-  bool is_nullable_type();
   std::string default_value();
   std::string default_value(const FieldDescriptor* descriptor);
   std::string number();
@@ -92,8 +91,8 @@ class FieldGeneratorBase : public SourceGeneratorBase {
 
  private:
   void SetCommonFieldVariables(std::map<string, string>* variables);
-  std::string GetStringDefaultValueInternal();
-  std::string GetBytesDefaultValueInternal();
+  std::string GetStringDefaultValueInternal(const FieldDescriptor* descriptor);
+  std::string GetBytesDefaultValueInternal(const FieldDescriptor* descriptor);
 
   GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorBase);
 };

+ 3 - 3
src/google/protobuf/compiler/csharp/csharp_generator.cc

@@ -65,11 +65,11 @@ bool Generator::Generate(
   std::vector<std::pair<string, string> > options;
   ParseGeneratorParameter(parameter, &options);
 
-  // We only support proto3 - but we make an exception for descriptor.proto.
+  // We only support proto3 - but we make an exception for descriptor.proto. 
   if (file->syntax() != FileDescriptor::SYNTAX_PROTO3 && !IsDescriptorProto(file)) {
-    *error = "C# code generation only supports proto3 syntax";
+    *error = "C# code generation only supports proto3 syntax"; 
     return false;
-  }
+  } 
 
   struct Options cli_options;
 

+ 48 - 13
src/google/protobuf/compiler/csharp/csharp_helpers.cc

@@ -36,6 +36,7 @@
 #include <google/protobuf/stubs/hash.h>
 #include <limits>
 #include <vector>
+#include <sstream>
 
 #include <google/protobuf/compiler/csharp/csharp_helpers.h>
 #include <google/protobuf/compiler/csharp/csharp_names.h>
@@ -452,55 +453,89 @@ std::string FileDescriptorToBase64(const FileDescriptor* descriptor) {
 }
 
 FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
-                                         int fieldOrdinal,
+                                         int presenceIndex,
                                          const Options* options) {
   switch (descriptor->type()) {
     case FieldDescriptor::TYPE_GROUP:
     case FieldDescriptor::TYPE_MESSAGE:
       if (descriptor->is_repeated()) {
         if (descriptor->is_map()) {
-          return new MapFieldGenerator(descriptor, fieldOrdinal, options);
+          return new MapFieldGenerator(descriptor, presenceIndex, options);
         } else {
-          return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal, options);
+          return new RepeatedMessageFieldGenerator(descriptor, presenceIndex, options);
         }
       } else {
         if (IsWrapperType(descriptor)) {
           if (descriptor->containing_oneof()) {
-            return new WrapperOneofFieldGenerator(descriptor, fieldOrdinal, options);
+            return new WrapperOneofFieldGenerator(descriptor, presenceIndex, options);
           } else {
-            return new WrapperFieldGenerator(descriptor, fieldOrdinal, options);
+            return new WrapperFieldGenerator(descriptor, presenceIndex, options);
           }
         } else {
           if (descriptor->containing_oneof()) {
-            return new MessageOneofFieldGenerator(descriptor, fieldOrdinal, options);
+            return new MessageOneofFieldGenerator(descriptor, presenceIndex, options);
           } else {
-            return new MessageFieldGenerator(descriptor, fieldOrdinal, options);
+            return new MessageFieldGenerator(descriptor, presenceIndex, options);
           }
         }
       }
     case FieldDescriptor::TYPE_ENUM:
       if (descriptor->is_repeated()) {
-        return new RepeatedEnumFieldGenerator(descriptor, fieldOrdinal, options);
+        return new RepeatedEnumFieldGenerator(descriptor, presenceIndex, options);
       } else {
         if (descriptor->containing_oneof()) {
-          return new EnumOneofFieldGenerator(descriptor, fieldOrdinal, options);
+          return new EnumOneofFieldGenerator(descriptor, presenceIndex, options);
         } else {
-          return new EnumFieldGenerator(descriptor, fieldOrdinal, options);
+          return new EnumFieldGenerator(descriptor, presenceIndex, options);
         }
       }
     default:
       if (descriptor->is_repeated()) {
-        return new RepeatedPrimitiveFieldGenerator(descriptor, fieldOrdinal, options);
+        return new RepeatedPrimitiveFieldGenerator(descriptor, presenceIndex, options);
       } else {
         if (descriptor->containing_oneof()) {
-          return new PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal, options);
+          return new PrimitiveOneofFieldGenerator(descriptor, presenceIndex, options);
         } else {
-          return new PrimitiveFieldGenerator(descriptor, fieldOrdinal, options);
+          return new PrimitiveFieldGenerator(descriptor, presenceIndex, options);
         }
       }
   }
 }
 
+bool IsNullable(const FieldDescriptor* descriptor) {
+  if (descriptor->is_repeated()) {
+    return true;
+  }
+
+  switch (descriptor->type()) {
+    case FieldDescriptor::TYPE_ENUM:
+    case FieldDescriptor::TYPE_DOUBLE:
+    case FieldDescriptor::TYPE_FLOAT:
+    case FieldDescriptor::TYPE_INT64:
+    case FieldDescriptor::TYPE_UINT64:
+    case FieldDescriptor::TYPE_INT32:
+    case FieldDescriptor::TYPE_FIXED64:
+    case FieldDescriptor::TYPE_FIXED32:
+    case FieldDescriptor::TYPE_BOOL:
+    case FieldDescriptor::TYPE_UINT32:
+    case FieldDescriptor::TYPE_SFIXED32:
+    case FieldDescriptor::TYPE_SFIXED64:
+    case FieldDescriptor::TYPE_SINT32:
+    case FieldDescriptor::TYPE_SINT64:
+      return false;
+
+    case FieldDescriptor::TYPE_MESSAGE:
+    case FieldDescriptor::TYPE_GROUP:
+    case FieldDescriptor::TYPE_STRING:
+    case FieldDescriptor::TYPE_BYTES:
+      return true;
+
+    default:
+      GOOGLE_LOG(FATAL) << "Unknown field type.";
+      return true;
+  }
+}
+
 }  // namespace csharp
 }  // namespace compiler
 }  // namespace protobuf

+ 7 - 1
src/google/protobuf/compiler/csharp/csharp_helpers.h

@@ -107,9 +107,11 @@ std::string StringToBase64(const std::string& input);
 std::string FileDescriptorToBase64(const FileDescriptor* descriptor);
 
 FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
-                                         int fieldOrdinal,
+                                         int presenceIndex,
                                          const Options* options);
 
+bool IsNullable(const FieldDescriptor* descriptor);
+
 // Determines whether the given message is a map entry message,
 // i.e. one implicitly created by protoc due to a map<key, value> field.
 inline bool IsMapEntryMessage(const Descriptor* descriptor) {
@@ -144,6 +146,10 @@ inline bool IsWrapperType(const FieldDescriptor* descriptor) {
       descriptor->message_type()->file()->name() == "google/protobuf/wrappers.proto";
 }
 
+inline bool IsProto2(const FileDescriptor* descriptor) {
+  return descriptor->syntax() == FileDescriptor::SYNTAX_PROTO2;
+}
+
 }  // namespace csharp
 }  // namespace compiler
 }  // namespace protobuf

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

@@ -48,9 +48,9 @@ namespace compiler {
 namespace csharp {
 
 MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor,
-                                     int fieldOrdinal,
+                                     int presenceIndex,
                                      const Options* options)
-    : FieldGeneratorBase(descriptor, fieldOrdinal, options) {
+    : FieldGeneratorBase(descriptor, presenceIndex, options) {
 }
 
 MapFieldGenerator::~MapFieldGenerator() {

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

@@ -44,7 +44,7 @@ namespace csharp {
 class MapFieldGenerator : public FieldGeneratorBase {
  public:
   MapFieldGenerator(const FieldDescriptor* descriptor,
-                    int fieldOrdinal,
+                    int presenceIndex,
                     const Options* options);
   ~MapFieldGenerator();
 

+ 40 - 18
src/google/protobuf/compiler/csharp/csharp_message.cc

@@ -61,20 +61,27 @@ bool CompareFieldNumbers(const FieldDescriptor* d1, const FieldDescriptor* d2) {
 MessageGenerator::MessageGenerator(const Descriptor* descriptor,
                                    const Options* options)
     : SourceGeneratorBase(descriptor->file(), options),
-      descriptor_(descriptor) {
-
-  // sorted field names
-  for (int i = 0; i < descriptor_->field_count(); i++) {
-    field_names_.push_back(descriptor_->field(i)->name());
-  }
-  std::sort(field_names_.begin(), field_names_.end());
-
+      descriptor_(descriptor),
+      has_bit_field_count_(0) {
   // fields by number
   for (int i = 0; i < descriptor_->field_count(); i++) {
     fields_by_number_.push_back(descriptor_->field(i));
   }
   std::sort(fields_by_number_.begin(), fields_by_number_.end(),
             CompareFieldNumbers);
+
+  if (IsProto2(descriptor_->file())) {
+    int primitiveCount = 0;
+    for (int i = 0; i < descriptor_->field_count(); i++) {
+      const FieldDescriptor* field = descriptor_->field(i);
+      if (!IsNullable(field)) {
+        primitiveCount++;
+        if (has_bit_field_count_ == 0 || (primitiveCount % 32) == 0) {
+          has_bit_field_count_++;
+        }
+      }
+    }
+  }
 }
 
 MessageGenerator::~MessageGenerator() {
@@ -88,10 +95,6 @@ std::string MessageGenerator::full_class_name() {
   return GetClassName(descriptor_);
 }
 
-const std::vector<std::string>& MessageGenerator::field_names() {
-  return field_names_;
-}
-
 const std::vector<const FieldDescriptor*>& MessageGenerator::fields_by_number() {
   return fields_by_number_;
 }
@@ -123,6 +126,12 @@ void MessageGenerator::Generate(io::Printer* printer) {
   printer->Print(
       "private pb::UnknownFieldSet _unknownFields;\n");
 
+  for (int i = 0; i < has_bit_field_count_; i++) {
+    // don't use arrays since all arrays are heap allocated, saving allocations
+    // use ints instead of bytes since bytes lack bitwise operators, saving casts
+    printer->Print("private int _hasBits$i$;\n", "i", SimpleItoa(i));
+  }
+
   WriteGeneratedCodeAttributes(printer);
 
   printer->Print(
@@ -288,6 +297,9 @@ void MessageGenerator::GenerateCloningCode(io::Printer* printer) {
     vars,
     "public $class_name$($class_name$ other) : this() {\n");
   printer->Indent();
+  for (int i = 0; i < has_bit_field_count_; i++) {
+    printer->Print("_hasBits$i$ = other._hasBits$i$;\n", "i", SimpleItoa(i));
+  }
   // Clone non-oneof fields first
   for (int i = 0; i < descriptor_->field_count(); i++) {
     if (!descriptor_->field(i)->containing_oneof()) {
@@ -559,19 +571,29 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
   printer->Print("}\n\n"); // method
 }
 
-int MessageGenerator::GetFieldOrdinal(const FieldDescriptor* descriptor) {
-  for (int i = 0; i < field_names().size(); i++) {
-    if (field_names()[i] == descriptor->name()) {
-      return i;
+// it's a waste of space to track presence for all values, so we only track them if they're not nullable
+int MessageGenerator::GetPresenceIndex(const FieldDescriptor* descriptor) {
+  if (IsNullable(descriptor) || !IsProto2(descriptor_->file())) {
+    return -1;
+  }
+
+  int index = 0;
+  for (int i = 0; i < fields_by_number().size(); i++) {
+    const FieldDescriptor* field = fields_by_number()[i];
+    if (field == descriptor) {
+      return index;
+    }
+    if (!IsNullable(field)) {
+      index++;
     }
   }
-  GOOGLE_LOG(DFATAL)<< "Could not find ordinal for field " << descriptor->name();
+  GOOGLE_LOG(DFATAL)<< "Could not find presence index for field " << descriptor->name();
   return -1;
 }
 
 FieldGeneratorBase* MessageGenerator::CreateFieldGeneratorInternal(
     const FieldDescriptor* descriptor) {
-  return CreateFieldGenerator(descriptor, GetFieldOrdinal(descriptor), this->options());
+  return CreateFieldGenerator(descriptor, GetPresenceIndex(descriptor), this->options());
 }
 
 }  // namespace csharp

+ 2 - 5
src/google/protobuf/compiler/csharp/csharp_message.h

@@ -57,13 +57,13 @@ class MessageGenerator : public SourceGeneratorBase {
 
  private:
   const Descriptor* descriptor_;
-  std::vector<std::string> field_names_;
   std::vector<const FieldDescriptor*> fields_by_number_;
+  int has_bit_field_count_;
 
   void GenerateMessageSerializationMethods(io::Printer* printer);
   void GenerateMergingMethods(io::Printer* printer);
 
-  int GetFieldOrdinal(const FieldDescriptor* descriptor);
+  int GetPresenceIndex(const FieldDescriptor* descriptor);
   FieldGeneratorBase* CreateFieldGeneratorInternal(
       const FieldDescriptor* descriptor);
 
@@ -74,9 +74,6 @@ class MessageGenerator : public SourceGeneratorBase {
   std::string class_name();
   std::string full_class_name();
 
-  // field names sorted alphabetically
-  const std::vector<std::string>& field_names();
-
   // field descriptors sorted by number
   const std::vector<const FieldDescriptor*>& fields_by_number();
 

+ 54 - 12
src/google/protobuf/compiler/csharp/csharp_message_field.cc

@@ -49,11 +49,13 @@ namespace compiler {
 namespace csharp {
 
 MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor,
-                                             int fieldOrdinal,
+                                             int presenceIndex,
                                              const Options *options)
-    : FieldGeneratorBase(descriptor, fieldOrdinal, options) {
-  variables_["has_property_check"] = name() + "_ != null";
-  variables_["has_not_property_check"] = name() + "_ == null";
+    : FieldGeneratorBase(descriptor, presenceIndex, options) {
+  if (!IsProto2(descriptor_->file())) {
+    variables_["has_property_check"] = name() + "_ != null";
+    variables_["has_not_property_check"] = name() + "_ == null";
+  }
 }
 
 MessageFieldGenerator::~MessageFieldGenerator() {
@@ -74,6 +76,26 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) {
     "    $name$_ = value;\n"
     "  }\n"
     "}\n");
+  if (IsProto2(descriptor_->file())) {
+    printer->Print(
+      variables_,
+      "/// <summary>Gets whether the $descriptor_name$ field is set</summary>\n");
+    AddPublicMemberAttributes(printer);
+    printer->Print(
+      variables_,
+      "$access_level$ bool Has$property_name$ {\n"
+      "  get { return $name$_ != null; }\n"
+      "}\n");
+    printer->Print( 
+      variables_, 
+      "/// <summary>Clears the value of the $descriptor_name$ field</summary>\n");
+    AddPublicMemberAttributes(printer);
+    printer->Print(
+      variables_,
+      "$access_level$ void Clear$property_name$() {\n"
+      "  $name$_ = null;\n"
+      "}\n");
+  }
 }
 
 void MessageFieldGenerator::GenerateMergingCode(io::Printer* printer) {
@@ -81,7 +103,7 @@ void MessageFieldGenerator::GenerateMergingCode(io::Printer* printer) {
     variables_,
     "if (other.$has_property_check$) {\n"
     "  if ($has_not_property_check$) {\n"
-    "    $name$_ = new $type_name$();\n"
+    "    $property_name$ = new $type_name$();\n"
     "  }\n"
     "  $property_name$.MergeFrom(other.$property_name$);\n"
     "}\n");
@@ -91,10 +113,9 @@ void MessageFieldGenerator::GenerateParsingCode(io::Printer* printer) {
   printer->Print(
     variables_,
     "if ($has_not_property_check$) {\n"
-    "  $name$_ = new $type_name$();\n"
+    "  $property_name$ = new $type_name$();\n"
     "}\n"
-    // TODO(jonskeet): Do we really need merging behaviour like this?
-    "input.ReadMessage($name$_);\n"); // No need to support TYPE_GROUP...
+    "input.ReadMessage($property_name$);\n");
 }
 
 void MessageFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
@@ -130,7 +151,6 @@ void MessageFieldGenerator::WriteToString(io::Printer* printer) {
     variables_,
     "PrintField(\"$field_name$\", has$property_name$, $name$_, writer);\n");
 }
-
 void MessageFieldGenerator::GenerateCloningCode(io::Printer* printer) {
   printer->Print(variables_,
     "$name$_ = other.$has_property_check$ ? other.$name$_.Clone() : null;\n");
@@ -147,9 +167,9 @@ void MessageFieldGenerator::GenerateCodecCode(io::Printer* printer) {
 
 MessageOneofFieldGenerator::MessageOneofFieldGenerator(
     const FieldDescriptor* descriptor,
-	  int fieldOrdinal,
+	  int presenceIndex,
     const Options *options)
-    : MessageFieldGenerator(descriptor, fieldOrdinal, options) {
+    : MessageFieldGenerator(descriptor, presenceIndex, options) {
   SetCommonOneofFieldVariables(&variables_);
 }
 
@@ -169,6 +189,28 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
     "    $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n"
     "  }\n"
     "}\n");
+  if (IsProto2(descriptor_->file())) {
+    printer->Print(
+      variables_,
+      "/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\n");
+    AddPublicMemberAttributes(printer);
+    printer->Print(
+      variables_,
+      "$access_level$ bool Has$property_name$ {\n"
+      "  get { return $oneof_name$Case_ == $oneof_property_name$OneofCase.$property_name$; }\n"
+      "}\n");
+    printer->Print(
+      variables_,
+      "/// <summary> Clears the value of the oneof if it's currently set to \"$descriptor_name$\" </summary>\n");
+    AddPublicMemberAttributes(printer);
+    printer->Print(
+      variables_,
+      "$access_level$ void Clear$property_name$() {\n"
+      "  if ($has_property_check$) {\n"
+      "    Clear$oneof_property_name$();\n"
+      "  }\n"
+      "}\n");
+  }
 }
 
 void MessageOneofFieldGenerator::GenerateMergingCode(io::Printer* printer) {
@@ -187,7 +229,7 @@ void MessageOneofFieldGenerator::GenerateParsingCode(io::Printer* printer) {
     "if ($has_property_check$) {\n"
     "  subBuilder.MergeFrom($property_name$);\n"
     "}\n"
-    "input.ReadMessage(subBuilder);\n" // No support of TYPE_GROUP
+    "input.ReadMessage(subBuilder);\n"
     "$property_name$ = subBuilder;\n");
 }
 

+ 2 - 2
src/google/protobuf/compiler/csharp/csharp_message_field.h

@@ -44,7 +44,7 @@ namespace csharp {
 class MessageFieldGenerator : public FieldGeneratorBase {
  public:
   MessageFieldGenerator(const FieldDescriptor* descriptor,
-                        int fieldOrdinal,
+                        int presenceIndex,
                         const Options *options);
   ~MessageFieldGenerator();
 
@@ -68,7 +68,7 @@ class MessageFieldGenerator : public FieldGeneratorBase {
 class MessageOneofFieldGenerator : public MessageFieldGenerator {
  public:
   MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
-                             int fieldOrdinal,
+                             int presenceIndex,
                              const Options *options);
   ~MessageOneofFieldGenerator();
 

+ 100 - 20
src/google/protobuf/compiler/csharp/csharp_primitive_field.cc

@@ -49,12 +49,12 @@ namespace compiler {
 namespace csharp {
 
 PrimitiveFieldGenerator::PrimitiveFieldGenerator(
-    const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options)
-    : FieldGeneratorBase(descriptor, fieldOrdinal, options) {
+    const FieldDescriptor* descriptor, int presenceIndex, const Options *options)
+    : FieldGeneratorBase(descriptor, presenceIndex, options) {
   // TODO(jonskeet): Make this cleaner...
   is_value_type = descriptor->type() != FieldDescriptor::TYPE_STRING
       && descriptor->type() != FieldDescriptor::TYPE_BYTES;
-  if (!is_value_type) {
+  if (!is_value_type && !IsProto2(descriptor_->file())) {
     variables_["has_property_check"] = variables_["property_name"] + ".Length != 0";
     variables_["other_has_property_check"] = "other." + variables_["property_name"] + ".Length != 0";
   }
@@ -67,16 +67,44 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
   // TODO(jonskeet): Work out whether we want to prevent the fields from ever being
   // null, or whether we just handle it, in the cases of bytes and string.
   // (Basically, should null-handling code be in the getter or the setter?)
+  if (IsProto2(descriptor_->file())) {
+    printer->Print(
+      variables_, 
+      "private readonly static $type_name$ $property_name$DefaultValue = $default_value$;\n\n");
+  }
+
   printer->Print(
     variables_,
     "private $type_name$ $name_def_message$;\n");
+
   WritePropertyDocComment(printer, descriptor_);
   AddPublicMemberAttributes(printer);
-  printer->Print(
-    variables_,
-    "$access_level$ $type_name$ $property_name$ {\n"
-    "  get { return $name$_; }\n"
-    "  set {\n");
+  if (IsProto2(descriptor_->file())) {
+    if (presenceIndex_ == -1) {
+      printer->Print(
+        variables_,
+        "$access_level$ $type_name$ $property_name$ {\n"
+        "  get { return $name$_ ?? $property_name$DefaultValue; }\n"
+        "  set {\n");
+    } else {
+      printer->Print(
+        variables_,
+        "$access_level$ $type_name$ $property_name$ {\n"
+        "  get { if ($has_field_check$) { return $name$_; } else { return $property_name$DefaultValue; } }\n"
+        "  set {\n");
+    }
+  } else {
+    printer->Print(
+      variables_,
+      "$access_level$ $type_name$ $property_name$ {\n"
+      "  get { return $name$_; }\n"
+      "  set {\n");
+  }
+  if (presenceIndex_ != -1) {
+    printer->Print(
+      variables_,
+      "    $set_has_field$;\n");
+  }
   if (is_value_type) {
     printer->Print(
       variables_,
@@ -89,6 +117,36 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
   printer->Print(
     "  }\n"
     "}\n");
+  if (IsProto2(descriptor_->file())) {
+    printer->Print(variables_, "/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\n");
+    AddPublicMemberAttributes(printer);
+    printer->Print(
+      variables_, 
+      "$access_level$ bool Has$property_name$ {\n"
+      "  get { return ");
+    if (IsNullable(descriptor_)) {
+      printer->Print(
+        variables_,
+        "$name$_ != null; }\n}\n");
+    } else {
+      printer->Print(
+        variables_,
+        "$has_field_check$; }\n}\n");
+    }
+  }
+  if (IsProto2(descriptor_->file())) {
+    printer->Print(variables_, "/// <summary>Clears the value of the \"$descriptor_name$\" field</summary>\n");
+    AddPublicMemberAttributes(printer);
+    printer->Print(
+      variables_,
+      "$access_level$ void Clear$property_name$() {\n");
+    if (IsNullable(descriptor_)) {
+      printer->Print(variables_, "  $name$_ = null;\n");
+    } else {
+      printer->Print(variables_, "  $clear_has_field$;\n");
+    }
+    printer->Print("}\n");
+  }
 }
 
 void PrimitiveFieldGenerator::GenerateMergingCode(io::Printer* printer) {
@@ -172,8 +230,8 @@ void PrimitiveFieldGenerator::GenerateCodecCode(io::Printer* printer) {
 }
 
 PrimitiveOneofFieldGenerator::PrimitiveOneofFieldGenerator(
-    const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options)
-    : PrimitiveFieldGenerator(descriptor, fieldOrdinal, options) {
+    const FieldDescriptor* descriptor, int presenceIndex, const Options *options)
+    : PrimitiveFieldGenerator(descriptor, presenceIndex, options) {
   SetCommonOneofFieldVariables(&variables_);
 }
 
@@ -188,20 +246,42 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
     "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n"
     "  set {\n");
-    if (is_value_type) {
-      printer->Print(
-        variables_,
-        "    $oneof_name$_ = value;\n");
-    } else {
-      printer->Print(
-        variables_,
-        "    $oneof_name$_ = pb::ProtoPreconditions.CheckNotNull(value, \"value\");\n");
-    }
+  if (is_value_type) {
     printer->Print(
       variables_,
-      "    $oneof_name$Case_ = $oneof_property_name$OneofCase.$property_name$;\n"
+      "    $oneof_name$_ = value;\n");
+  } else {
+    printer->Print(
+      variables_,
+      "    $oneof_name$_ = pb::ProtoPreconditions.CheckNotNull(value, \"value\");\n");
+  }
+  printer->Print(
+    variables_,
+    "    $oneof_name$Case_ = $oneof_property_name$OneofCase.$property_name$;\n"
+    "  }\n"
+    "}\n");
+  if (IsProto2(descriptor_->file())) {
+    printer->Print(
+      variables_,
+      "/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\n");
+    AddPublicMemberAttributes(printer);
+    printer->Print(
+      variables_,
+      "$access_level$ bool Has$property_name$ {\n"
+      "  get { return $oneof_name$Case_ == $oneof_property_name$OneofCase.$property_name$; }\n"
+      "}\n");
+    printer->Print(
+      variables_,
+      "/// <summary> Clears the value of the oneof if it's currently set to \"$descriptor_name$\" </summary>\n");
+    AddPublicMemberAttributes(printer);
+    printer->Print(
+      variables_,
+      "$access_level$ void Clear$property_name$() {\n"
+      "  if ($has_property_check$) {\n"
+      "    Clear$oneof_property_name$();\n"
       "  }\n"
       "}\n");
+  }
 }
 
 void PrimitiveOneofFieldGenerator::GenerateMergingCode(io::Printer* printer) {

+ 2 - 2
src/google/protobuf/compiler/csharp/csharp_primitive_field.h

@@ -46,7 +46,7 @@ struct Options;
 class PrimitiveFieldGenerator : public FieldGeneratorBase {
  public:
   PrimitiveFieldGenerator(const FieldDescriptor* descriptor,
-                          int fieldOrdinal,
+                          int presenceIndex,
                           const Options *options);
   ~PrimitiveFieldGenerator();
 
@@ -72,7 +72,7 @@ class PrimitiveFieldGenerator : public FieldGeneratorBase {
 class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator {
  public:
   PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor,
-                               int fieldOrdinal,
+                               int presenceIndex,
                                const Options *options);
   ~PrimitiveOneofFieldGenerator();
 

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

@@ -48,8 +48,8 @@ namespace compiler {
 namespace csharp {
 
 RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator(
-    const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options)
-    : FieldGeneratorBase(descriptor, fieldOrdinal, options) {
+    const FieldDescriptor* descriptor, int presenceIndex, const Options *options)
+    : FieldGeneratorBase(descriptor, presenceIndex, options) {
 }
 
 RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {

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

@@ -46,7 +46,7 @@ namespace csharp {
 class RepeatedEnumFieldGenerator : public FieldGeneratorBase {
  public:
   RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor,
-                             int fieldOrdinal,
+                             int presenceIndex,
                              const Options *options);
   ~RepeatedEnumFieldGenerator();
 

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

@@ -49,8 +49,8 @@ namespace compiler {
 namespace csharp {
 
 RepeatedMessageFieldGenerator::RepeatedMessageFieldGenerator(
-    const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options)
-    : FieldGeneratorBase(descriptor, fieldOrdinal, options) {
+    const FieldDescriptor* descriptor, int presenceIndex, const Options *options)
+    : FieldGeneratorBase(descriptor, presenceIndex, options) {
 }
 
 RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {
@@ -67,11 +67,11 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) {
   // function, but it doesn't seem worth it for just this.
   if (IsWrapperType(descriptor_)) {
     std::unique_ptr<FieldGeneratorBase> single_generator(
-      new WrapperFieldGenerator(descriptor_, fieldOrdinal_, this->options()));
+      new WrapperFieldGenerator(descriptor_, presenceIndex_, this->options()));
     single_generator->GenerateCodecCode(printer);
   } else {
     std::unique_ptr<FieldGeneratorBase> single_generator(
-      new MessageFieldGenerator(descriptor_, fieldOrdinal_, this->options()));
+      new MessageFieldGenerator(descriptor_, presenceIndex_, this->options()));
     single_generator->GenerateCodecCode(printer);
   }
   printer->Print(";\n");

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

@@ -46,7 +46,7 @@ struct Options;
 class RepeatedMessageFieldGenerator : public FieldGeneratorBase {
  public:
   RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor,
-                                int fieldOrdinal,
+                                int presenceIndex,
                                 const Options *options);
   ~RepeatedMessageFieldGenerator();
 

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

@@ -48,8 +48,8 @@ namespace compiler {
 namespace csharp {
 
 RepeatedPrimitiveFieldGenerator::RepeatedPrimitiveFieldGenerator(
-    const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options)
-    : FieldGeneratorBase(descriptor, fieldOrdinal, options) {
+    const FieldDescriptor* descriptor, int presenceIndex, const Options *options)
+    : FieldGeneratorBase(descriptor, presenceIndex, options) {
 }
 
 RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {

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

@@ -43,7 +43,7 @@ namespace csharp {
 
 class RepeatedPrimitiveFieldGenerator : public FieldGeneratorBase {
  public:
-  RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options);
+  RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, int presenceIndex, const Options *options);
   ~RepeatedPrimitiveFieldGenerator();
 
   virtual void GenerateCloningCode(io::Printer* printer);

+ 47 - 5
src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc

@@ -48,8 +48,8 @@ namespace compiler {
 namespace csharp {
 
 WrapperFieldGenerator::WrapperFieldGenerator(const FieldDescriptor* descriptor,
-                                       int fieldOrdinal, const Options *options)
-    : FieldGeneratorBase(descriptor, fieldOrdinal, options) {
+                                       int presenceIndex, const Options *options)
+    : FieldGeneratorBase(descriptor, presenceIndex, options) {
   variables_["has_property_check"] = name() + "_ != null";
   variables_["has_not_property_check"] = name() + "_ == null";
   const FieldDescriptor* wrapped_field = descriptor->message_type()->field(0);
@@ -81,7 +81,27 @@ void WrapperFieldGenerator::GenerateMembers(io::Printer* printer) {
     "  set {\n"
     "    $name$_ = value;\n"
     "  }\n"
-    "}\n");
+    "}\n\n");
+  if (IsProto2(descriptor_->file())) {
+    printer->Print(
+      variables_,
+      "/// <summary>Gets whether the $descriptor_name$ field is set</summary>\n");
+    AddPublicMemberAttributes(printer);
+    printer->Print(
+      variables_,
+      "$access_level$ bool Has$property_name$ {\n"
+      "  get { return $name$_ != null; }\n"
+      "}\n\n");
+    printer->Print(
+      variables_,
+      "/// <summary>Clears the value of the $descriptor_name$ field</summary>\n");
+    AddPublicMemberAttributes(printer);
+    printer->Print(
+      variables_,
+      "$access_level$ void Clear$property_name$() {\n"
+      "  $name$_ = null;\n"
+      "}\n");
+  }
 }
 
 void WrapperFieldGenerator::GenerateMergingCode(io::Printer* printer) {
@@ -163,8 +183,8 @@ void WrapperFieldGenerator::GenerateCodecCode(io::Printer* printer) {
 }
 
 WrapperOneofFieldGenerator::WrapperOneofFieldGenerator(
-    const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options)
-    : WrapperFieldGenerator(descriptor, fieldOrdinal, options) {
+    const FieldDescriptor* descriptor, int presenceIndex, const Options *options)
+    : WrapperFieldGenerator(descriptor, presenceIndex, options) {
     SetCommonOneofFieldVariables(&variables_);
 }
 
@@ -189,6 +209,28 @@ void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
     "    $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n"
     "  }\n"
     "}\n");
+  if (IsProto2(descriptor_->file())) {
+    printer->Print(
+      variables_,
+      "/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\n");
+    AddPublicMemberAttributes(printer);
+    printer->Print(
+      variables_,
+      "$access_level$ bool Has$property_name$ {\n"
+      "  get { return $oneof_name$Case_ == $oneof_property_name$OneofCase.$property_name$; }\n"
+      "}\n");
+    printer->Print(
+      variables_,
+      "/// <summary> Clears the value of the oneof if it's currently set to \"$descriptor_name$\" </summary>\n");
+    AddPublicMemberAttributes(printer);
+    printer->Print(
+      variables_,
+      "$access_level$ void Clear$property_name$() {\n"
+      "  if ($has_property_check$) {\n"
+      "    Clear$oneof_property_name$();\n"
+      "  }\n"
+      "}\n");
+  }
 }
 
 void WrapperOneofFieldGenerator::GenerateMergingCode(io::Printer* printer) {

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

@@ -46,7 +46,7 @@ struct Options;
 class WrapperFieldGenerator : public FieldGeneratorBase {
  public:
   WrapperFieldGenerator(const FieldDescriptor* descriptor,
-                        int fieldOrdinal,
+                        int presenceIndex,
                         const Options *options);
   ~WrapperFieldGenerator();
 
@@ -70,7 +70,7 @@ class WrapperFieldGenerator : public FieldGeneratorBase {
 class WrapperOneofFieldGenerator : public WrapperFieldGenerator {
  public:
   WrapperOneofFieldGenerator(const FieldDescriptor* descriptor,
-                             int fieldOrdinal,
+                             int presenceIndex,
                              const Options *options);
   ~WrapperOneofFieldGenerator();
 

Some files were not shown because too many files changed in this diff