浏览代码

Merge pull request #560 from jskeet/csharp-repeated

Large changes to repeated field handling
Jon Skeet 10 年之前
父节点
当前提交
4ae9b6c0d1
共有 25 个文件被更改,包括 1444 次插入2109 次删除
  1. 10 20
      csharp/src/AddressBook/Addressbook.cs
  2. 54 0
      csharp/src/ProtocolBuffers.Test/CodedInputStreamExtensions.cs
  3. 16 118
      csharp/src/ProtocolBuffers.Test/CodedInputStreamTest.cs
  4. 11 80
      csharp/src/ProtocolBuffers.Test/CodedOutputStreamTest.cs
  5. 290 1
      csharp/src/ProtocolBuffers.Test/Collections/RepeatedFieldTest.cs
  6. 46 17
      csharp/src/ProtocolBuffers.Test/FieldCodecTest.cs
  7. 133 0
      csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs
  8. 2 0
      csharp/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj
  9. 42 0
      csharp/src/ProtocolBuffers.Test/SampleEnum.cs
  10. 25 64
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestIssues.cs
  11. 196 487
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestProto3.cs
  12. 148 436
      csharp/src/ProtocolBuffers/CodedInputStream.cs
  13. 30 21
      csharp/src/ProtocolBuffers/CodedOutputStream.ComputeSize.cs
  14. 80 336
      csharp/src/ProtocolBuffers/CodedOutputStream.cs
  15. 7 3
      csharp/src/ProtocolBuffers/Collections/MapField.cs
  16. 1 1
      csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs
  17. 110 75
      csharp/src/ProtocolBuffers/Collections/RepeatedField.cs
  18. 147 313
      csharp/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs
  19. 52 7
      csharp/src/ProtocolBuffers/FieldCodec.cs
  20. 1 1
      csharp/src/ProtocolBuffers/MessageExtensions.cs
  21. 7 0
      csharp/src/ProtocolBuffers/Properties/AssemblyInfo.cs
  22. 9 27
      csharp/src/ProtocolBuffers/WireFormat.cs
  23. 8 39
      src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc
  24. 7 12
      src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc
  25. 12 51
      src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc

+ 10 - 20
csharp/src/AddressBook/Addressbook.cs

@@ -131,6 +131,8 @@ namespace Google.Protobuf.Examples.AddressBook {
     }
     }
 
 
     public const int PhoneFieldNumber = 4;
     public const int PhoneFieldNumber = 4;
+    private static readonly pb::FieldCodec<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> _repeated_phone_codec
+        = pb::FieldCodec.ForMessage(34, global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser);
     private readonly pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> phone_ = new pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber>();
     private readonly pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> phone_ = new pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber>();
     public pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> Phone {
     public pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> Phone {
       get { return phone_; }
       get { return phone_; }
@@ -176,9 +178,7 @@ namespace Google.Protobuf.Examples.AddressBook {
         output.WriteRawTag(26);
         output.WriteRawTag(26);
         output.WriteString(Email);
         output.WriteString(Email);
       }
       }
-      if (phone_.Count > 0) {
-        output.WriteMessageArray(4, phone_);
-      }
+      phone_.WriteTo(output, _repeated_phone_codec);
     }
     }
 
 
     public int CalculateSize() {
     public int CalculateSize() {
@@ -192,12 +192,7 @@ namespace Google.Protobuf.Examples.AddressBook {
       if (Email.Length != 0) {
       if (Email.Length != 0) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(Email);
         size += 1 + pb::CodedOutputStream.ComputeStringSize(Email);
       }
       }
-      if (phone_.Count > 0) {
-        foreach (global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber element in phone_) {
-          size += pb::CodedOutputStream.ComputeMessageSize(element);
-        }
-        size += 1 * phone_.Count;
-      }
+      size += phone_.CalculateSize(_repeated_phone_codec);
       return size;
       return size;
     }
     }
 
 
@@ -241,7 +236,7 @@ namespace Google.Protobuf.Examples.AddressBook {
             break;
             break;
           }
           }
           case 34: {
           case 34: {
-            input.ReadMessageArray(phone_, global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser);
+            phone_.AddEntriesFrom(input, _repeated_phone_codec);
             break;
             break;
           }
           }
         }
         }
@@ -437,6 +432,8 @@ namespace Google.Protobuf.Examples.AddressBook {
     }
     }
 
 
     public const int PersonFieldNumber = 1;
     public const int PersonFieldNumber = 1;
+    private static readonly pb::FieldCodec<global::Google.Protobuf.Examples.AddressBook.Person> _repeated_person_codec
+        = pb::FieldCodec.ForMessage(10, global::Google.Protobuf.Examples.AddressBook.Person.Parser);
     private readonly pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person> person_ = new pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person>();
     private readonly pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person> person_ = new pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person>();
     public pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person> Person {
     public pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person> Person {
       get { return person_; }
       get { return person_; }
@@ -464,19 +461,12 @@ namespace Google.Protobuf.Examples.AddressBook {
     }
     }
 
 
     public void WriteTo(pb::CodedOutputStream output) {
     public void WriteTo(pb::CodedOutputStream output) {
-      if (person_.Count > 0) {
-        output.WriteMessageArray(1, person_);
-      }
+      person_.WriteTo(output, _repeated_person_codec);
     }
     }
 
 
     public int CalculateSize() {
     public int CalculateSize() {
       int size = 0;
       int size = 0;
-      if (person_.Count > 0) {
-        foreach (global::Google.Protobuf.Examples.AddressBook.Person element in person_) {
-          size += pb::CodedOutputStream.ComputeMessageSize(element);
-        }
-        size += 1 * person_.Count;
-      }
+      size += person_.CalculateSize(_repeated_person_codec);
       return size;
       return size;
     }
     }
 
 
@@ -499,7 +489,7 @@ namespace Google.Protobuf.Examples.AddressBook {
             }
             }
             break;
             break;
           case 10: {
           case 10: {
-            input.ReadMessageArray(person_, global::Google.Protobuf.Examples.AddressBook.Person.Parser);
+            person_.AddEntriesFrom(input, _repeated_person_codec);
             break;
             break;
           }
           }
         }
         }

+ 54 - 0
csharp/src/ProtocolBuffers.Test/CodedInputStreamExtensions.cs

@@ -0,0 +1,54 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+    internal static class CodedInputStreamExtensions
+    {
+        public static void AssertNextTag(this CodedInputStream input, uint expectedTag)
+        {
+            uint tag;
+            Assert.IsTrue(input.ReadTag(out tag));
+            Assert.AreEqual(expectedTag, tag);
+        }
+
+        public static T ReadMessage<T>(this CodedInputStream stream, MessageParser<T> parser)
+            where T : IMessage<T>
+        {
+            var message = parser.CreateTemplate();
+            stream.ReadMessage(message);
+            return message;
+        }
+    }
+}

+ 16 - 118
csharp/src/ProtocolBuffers.Test/CodedInputStreamTest.cs

@@ -35,10 +35,8 @@
 #endregion
 #endregion
 
 
 using System;
 using System;
-using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using Google.Protobuf.Collections;
 using Google.Protobuf.Collections;
-using Google.Protobuf.Descriptors;
 using Google.Protobuf.TestProtos;
 using Google.Protobuf.TestProtos;
 using NUnit.Framework;
 using NUnit.Framework;
 
 
@@ -62,7 +60,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and
+        /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64()
         /// </summary>
         /// </summary>
         private static void AssertReadVarint(byte[] data, ulong value)
         private static void AssertReadVarint(byte[] data, ulong value)
         {
         {
@@ -232,66 +230,26 @@ namespace Google.Protobuf
             Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
             Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
             Assert.AreEqual(unchecked((long) 0x8000000000000000L), CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
             Assert.AreEqual(unchecked((long) 0x8000000000000000L), CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
         }
         }
-        /*
+        
         [Test]
         [Test]
-        public void ReadWholeMessage()
+        public void ReadWholeMessage_VaryingBlockSizes()
         {
         {
-            TestAllTypes message = TestUtil.GetAllSet();
+            TestAllTypes message = GeneratedMessageTest.GetSampleMessage();
 
 
             byte[] rawBytes = message.ToByteArray();
             byte[] rawBytes = message.ToByteArray();
-            Assert.AreEqual(rawBytes.Length, message.SerializedSize);
-            TestAllTypes message2 = TestAllTypes.ParseFrom(rawBytes);
-            TestUtil.AssertAllFieldsSet(message2);
+            Assert.AreEqual(rawBytes.Length, message.CalculateSize());
+            TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(rawBytes);
+            Assert.AreEqual(message, message2);
 
 
             // Try different block sizes.
             // Try different block sizes.
             for (int blockSize = 1; blockSize < 256; blockSize *= 2)
             for (int blockSize = 1; blockSize < 256; blockSize *= 2)
             {
             {
-                message2 = TestAllTypes.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));
-                TestUtil.AssertAllFieldsSet(message2);
+                message2 = TestAllTypes.Parser.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));
+                Assert.AreEqual(message, message2);
             }
             }
         }
         }
-
+                
         [Test]
         [Test]
-        public void SkipWholeMessage()
-        {
-            TestAllTypes message = TestUtil.GetAllSet();
-            byte[] rawBytes = message.ToByteArray();
-
-            // Create two parallel inputs.  Parse one as unknown fields while using
-            // skipField() to skip each field on the other.  Expect the same tags.
-            CodedInputStream input1 = CodedInputStream.CreateInstance(rawBytes);
-            CodedInputStream input2 = CodedInputStream.CreateInstance(rawBytes);
-            UnknownFieldSet.Builder unknownFields = UnknownFieldSet.CreateBuilder();
-
-            uint tag;
-            string name;
-            while (input1.ReadTag(out tag, out name))
-            {
-                uint tag2;
-                Assert.IsTrue(input2.ReadTag(out tag2, out name));
-                Assert.AreEqual(tag, tag2);
-
-                unknownFields.MergeFieldFrom(tag, input1);
-                input2.SkipField();
-            }
-        }*/
-
-        /// <summary>
-        /// Test that a bug in SkipRawBytes has been fixed: if the skip
-        /// skips exactly up to a limit, this should bnot break things
-        /// </summary>
-        [Test]
-        public void SkipRawBytesBug()
-        {
-            byte[] rawBytes = new byte[] {1, 2};
-            CodedInputStream input = CodedInputStream.CreateInstance(rawBytes);
-
-            int limit = input.PushLimit(1);
-            input.SkipRawBytes(1);
-            input.PopLimit(limit);
-            Assert.AreEqual(2, input.ReadRawByte());
-        }
-        /*
         public void ReadHugeBlob()
         public void ReadHugeBlob()
         {
         {
             // Allocate and initialize a 1MB blob.
             // Allocate and initialize a 1MB blob.
@@ -302,24 +260,15 @@ namespace Google.Protobuf
             }
             }
 
 
             // Make a message containing it.
             // Make a message containing it.
-            TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
-            TestUtil.SetAllFields(builder);
-            builder.SetOptionalBytes(ByteString.CopyFrom(blob));
-            TestAllTypes message = builder.Build();
+            var message = new TestAllTypes { SingleBytes = ByteString.CopyFrom(blob) };
 
 
             // Serialize and parse it.  Make sure to parse from an InputStream, not
             // Serialize and parse it.  Make sure to parse from an InputStream, not
             // directly from a ByteString, so that CodedInputStream uses buffered
             // directly from a ByteString, so that CodedInputStream uses buffered
             // reading.
             // reading.
-            TestAllTypes message2 = TestAllTypes.ParseFrom(message.ToByteString().CreateCodedInput());
+            TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(message.ToByteString());
 
 
-            Assert.AreEqual(message.OptionalBytes, message2.OptionalBytes);
-
-            // Make sure all the other fields were parsed correctly.
-            TestAllTypes message3 = TestAllTypes.CreateBuilder(message2)
-                .SetOptionalBytes(TestUtil.GetAllSet().OptionalBytes)
-                .Build();
-            TestUtil.AssertAllFieldsSet(message3);
-        }*/
+            Assert.AreEqual(message, message2);
+        }
 
 
         [Test]
         [Test]
         public void ReadMaliciouslyLargeBlob()
         public void ReadMaliciouslyLargeBlob()
@@ -461,66 +410,15 @@ namespace Google.Protobuf
             }
             }
         }
         }
 
 
-        enum TestNegEnum { None = 0, Value = -2 }
-
         [Test]
         [Test]
         public void TestNegativeEnum()
         public void TestNegativeEnum()
         {
         {
-            byte[] bytes = new byte[10] { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };
+            byte[] bytes = { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };
             CodedInputStream input = CodedInputStream.CreateInstance(bytes);
             CodedInputStream input = CodedInputStream.CreateInstance(bytes);
-            Assert.AreEqual((int)TestNegEnum.Value, input.ReadEnum());
+            Assert.AreEqual((int)SampleEnum.NegativeValue, input.ReadEnum());
             Assert.IsTrue(input.IsAtEnd);
             Assert.IsTrue(input.IsAtEnd);
         }
         }
 
 
-        [Test]
-        public void TestNegativeEnumPackedArray()
-        {
-            int arraySize = 1 + (10 * 5);
-            int msgSize = 1 + 1 + arraySize;
-            byte[] bytes = new byte[msgSize];
-            CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
-            output.WriteTag(8, WireFormat.WireType.LengthDelimited);
-            output.WritePackedInt32Array(new RepeatedField<int> { 0, -1, -2, -3, -4, -5 });
-
-            Assert.AreEqual(0, output.SpaceLeft);
-
-            CodedInputStream input = CodedInputStream.CreateInstance(bytes);
-            uint tag;
-            Assert.IsTrue(input.ReadTag(out tag));
-
-            RepeatedField<TestNegEnum> values = new RepeatedField<TestNegEnum>();
-            input.ReadEnumArray(values);
-
-            Assert.AreEqual(6, values.Count);
-            Assert.AreEqual(TestNegEnum.None, values[0]);
-            Assert.AreEqual(TestNegEnum.Value, values[2]);
-            // TODO(jonskeet): Test unknown value preservation
-        }
-
-        [Test]
-        public void TestNegativeEnumArray()
-        {
-            int arraySize = 1 + 1 + (11 * 5);
-            int msgSize = arraySize;
-            byte[] bytes = new byte[msgSize];
-            CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
-            output.WriteInt32Array(8, new RepeatedField<int> { 0, -1, -2, -3, -4, -5 });
-
-            Assert.AreEqual(0, output.SpaceLeft);
-
-            CodedInputStream input = CodedInputStream.CreateInstance(bytes);
-            uint tag;
-            Assert.IsTrue(input.ReadTag(out tag));
-
-            RepeatedField<TestNegEnum> values = new RepeatedField<TestNegEnum>();
-            input.ReadEnumArray(values);
-
-            Assert.AreEqual(6, values.Count);
-            Assert.AreEqual(TestNegEnum.None, values[0]);
-            Assert.AreEqual(TestNegEnum.Value, values[2]);
-            // TODO(jonskeet): Test unknown value preservation
-        }
-
         //Issue 71:	CodedInputStream.ReadBytes go to slow path unnecessarily
         //Issue 71:	CodedInputStream.ReadBytes go to slow path unnecessarily
         [Test]
         [Test]
         public void TestSlowPathAvoidance()
         public void TestSlowPathAvoidance()

+ 11 - 80
csharp/src/ProtocolBuffers.Test/CodedOutputStreamTest.cs

@@ -35,9 +35,8 @@
 #endregion
 #endregion
 
 
 using System;
 using System;
-using System.Collections.Generic;
 using System.IO;
 using System.IO;
-using Google.Protobuf.Collections;
+using Google.Protobuf.TestProtos;
 using NUnit.Framework;
 using NUnit.Framework;
 
 
 namespace Google.Protobuf
 namespace Google.Protobuf
@@ -195,42 +194,24 @@ namespace Google.Protobuf
                 0x9abcdef012345678UL);
                 0x9abcdef012345678UL);
         }
         }
 
 
-        /*
         [Test]
         [Test]
-        public void WriteWholeMessage()
+        public void WriteWholeMessage_VaryingBlockSizes()
         {
         {
-            TestAllTypes message = TestUtil.GetAllSet();
+            TestAllTypes message = GeneratedMessageTest.GetSampleMessage();
 
 
             byte[] rawBytes = message.ToByteArray();
             byte[] rawBytes = message.ToByteArray();
-            TestUtil.AssertEqualBytes(TestUtil.GoldenMessage.ToByteArray(), rawBytes);
 
 
             // Try different block sizes.
             // Try different block sizes.
             for (int blockSize = 1; blockSize < 256; blockSize *= 2)
             for (int blockSize = 1; blockSize < 256; blockSize *= 2)
             {
             {
                 MemoryStream rawOutput = new MemoryStream();
                 MemoryStream rawOutput = new MemoryStream();
-                CodedOutputStream output =
-                    CodedOutputStream.CreateInstance(rawOutput, blockSize);
+                CodedOutputStream output = CodedOutputStream.CreateInstance(rawOutput, blockSize);
                 message.WriteTo(output);
                 message.WriteTo(output);
                 output.Flush();
                 output.Flush();
-                TestUtil.AssertEqualBytes(rawBytes, rawOutput.ToArray());
+                Assert.AreEqual(rawBytes, rawOutput.ToArray());
             }
             }
         }
         }
-
-        /// <summary>
-        /// Tests writing a whole message with every packed field type. Ensures the
-        /// wire format of packed fields is compatible with C++.
-        /// </summary>
-        [Test]
-        public void WriteWholePackedFieldsMessage()
-        {
-            TestPackedTypes message = TestUtil.GetPackedSet();
-
-            byte[] rawBytes = message.ToByteArray();
-            TestUtil.AssertEqualBytes(TestUtil.GetGoldenPackedFieldsMessage().ToByteArray(),
-                                      rawBytes);
-        }
-        */
-
+        
         [Test]
         [Test]
         public void EncodeZigZag32()
         public void EncodeZigZag32()
         {
         {
@@ -296,66 +277,16 @@ namespace Google.Protobuf
         public void TestNegativeEnumNoTag()
         public void TestNegativeEnumNoTag()
         {
         {
             Assert.AreEqual(10, CodedOutputStream.ComputeInt32Size(-2));
             Assert.AreEqual(10, CodedOutputStream.ComputeInt32Size(-2));
-            Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) TestNegEnum.Value));
+            Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) SampleEnum.NegativeValue));
 
 
             byte[] bytes = new byte[10];
             byte[] bytes = new byte[10];
             CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
             CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
-            output.WriteEnum((int) TestNegEnum.Value);
+            output.WriteEnum((int) SampleEnum.NegativeValue);
 
 
             Assert.AreEqual(0, output.SpaceLeft);
             Assert.AreEqual(0, output.SpaceLeft);
             Assert.AreEqual("FE-FF-FF-FF-FF-FF-FF-FF-FF-01", BitConverter.ToString(bytes));
             Assert.AreEqual("FE-FF-FF-FF-FF-FF-FF-FF-FF-01", BitConverter.ToString(bytes));
         }
         }
 
 
-        enum TestNegEnum { None = 0, Value = -2 }
-
-        [Test]
-        public void TestNegativeEnumArrayPacked()
-        {
-            int arraySize = 1 + (10 * 5);
-            int msgSize = 1 + 1 + arraySize;
-            byte[] bytes = new byte[msgSize];
-            CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
-            output.WriteTag(8, WireFormat.WireType.LengthDelimited);
-            output.WritePackedEnumArray(new RepeatedField<TestNegEnum> {
-                0, (TestNegEnum) (-1), TestNegEnum.Value, (TestNegEnum) (-3), (TestNegEnum) (-4), (TestNegEnum) (-5) });
-
-            Assert.AreEqual(0, output.SpaceLeft);
-
-            CodedInputStream input = CodedInputStream.CreateInstance(bytes);
-            uint tag;
-            Assert.IsTrue(input.ReadTag(out tag));
-
-            List<int> values = new List<int>();
-            input.ReadInt32Array(values);
-
-            Assert.AreEqual(6, values.Count);
-            for (int i = 0; i > -6; i--)
-                Assert.AreEqual(i, values[Math.Abs(i)]);
-        }
-
-        [Test]
-        public void TestNegativeEnumArray()
-        {
-            int arraySize = 1 + 1 + (11 * 5);
-            int msgSize = arraySize;
-            byte[] bytes = new byte[msgSize];
-            CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
-            output.WriteEnumArray(8, new RepeatedField<TestNegEnum> {
-                0, (TestNegEnum) (-1), TestNegEnum.Value, (TestNegEnum) (-3), (TestNegEnum) (-4), (TestNegEnum) (-5) });
-            Assert.AreEqual(0, output.SpaceLeft);
-
-            CodedInputStream input = CodedInputStream.CreateInstance(bytes);
-            uint tag;
-            Assert.IsTrue(input.ReadTag(out tag));
-
-            List<int> values = new List<int>();
-            input.ReadInt32Array(values);
-
-            Assert.AreEqual(6, values.Count);
-            for (int i = 0; i > -6; i--)
-                Assert.AreEqual(i, values[Math.Abs(i)]);
-        }
-
         [Test]
         [Test]
         public void TestCodedInputOutputPosition()
         public void TestCodedInputOutputPosition()
         {
         {
@@ -405,7 +336,7 @@ namespace Google.Protobuf
                 Assert.AreEqual(130, cout.Position);
                 Assert.AreEqual(130, cout.Position);
                 cout.Flush();
                 cout.Flush();
             }
             }
-            //Now test Input stream:
+            // Now test Input stream:
             {
             {
                 CodedInputStream cin = CodedInputStream.CreateInstance(new MemoryStream(bytes), new byte[50]);
                 CodedInputStream cin = CodedInputStream.CreateInstance(new MemoryStream(bytes), new byte[50]);
                 uint tag;
                 uint tag;
@@ -418,8 +349,8 @@ namespace Google.Protobuf
                 //Field 2:
                 //Field 2:
                 Assert.IsTrue(cin.ReadTag(out tag) && tag >> 3 == 2);
                 Assert.IsTrue(cin.ReadTag(out tag) && tag >> 3 == 2);
                 Assert.AreEqual(4, cin.Position);
                 Assert.AreEqual(4, cin.Position);
-                uint childlen = cin.ReadRawVarint32();
-                Assert.AreEqual(120u, childlen);
+                int childlen = cin.ReadLength();
+                Assert.AreEqual(120, childlen);
                 Assert.AreEqual(5, cin.Position);
                 Assert.AreEqual(5, cin.Position);
                 int oldlimit = cin.PushLimit((int)childlen);
                 int oldlimit = cin.PushLimit((int)childlen);
                 Assert.AreEqual(5, cin.Position);
                 Assert.AreEqual(5, cin.Position);

+ 290 - 1
csharp/src/ProtocolBuffers.Test/Collections/RepeatedFieldTest.cs

@@ -1,5 +1,39 @@
-using System;
+#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.Generic;
 using System.Collections.Generic;
+using System.IO;
+using System.Linq;
 using Google.Protobuf.TestProtos;
 using Google.Protobuf.TestProtos;
 using NUnit.Framework;
 using NUnit.Framework;
 
 
@@ -89,5 +123,260 @@ namespace Google.Protobuf.Collections
             var clone = list.Clone();
             var clone = list.Clone();
             clone[0] = 1;
             clone[0] = 1;
         }
         }
+
+        [Test]
+        public void AddEntriesFrom_PackedInt32()
+        {
+            uint packedTag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
+            var stream = new MemoryStream();
+            var output = CodedOutputStream.CreateInstance(stream);
+            var length = CodedOutputStream.ComputeInt32Size(10)
+                + CodedOutputStream.ComputeInt32Size(999)
+                + CodedOutputStream.ComputeInt32Size(-1000);
+            output.WriteTag(packedTag);
+            output.WriteRawVarint32((uint) length);
+            output.WriteInt32(10);
+            output.WriteInt32(999);
+            output.WriteInt32(-1000);
+            output.Flush();
+            stream.Position = 0;
+
+            // Deliberately "expecting" a non-packed tag, but we detect that the data is
+            // actually packed.
+            uint nonPackedTag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
+            var field = new RepeatedField<int>();
+            var input = CodedInputStream.CreateInstance(stream);
+            input.AssertNextTag(packedTag);
+            field.AddEntriesFrom(input, FieldCodec.ForInt32(nonPackedTag));
+            CollectionAssert.AreEqual(new[] { 10, 999, -1000 }, field);
+            Assert.IsTrue(input.IsAtEnd);
+        }
+
+        [Test]
+        public void AddEntriesFrom_NonPackedInt32()
+        {
+            uint nonPackedTag = WireFormat.MakeTag(10, WireFormat.WireType.Varint);
+            var stream = new MemoryStream();
+            var output = CodedOutputStream.CreateInstance(stream);
+            output.WriteTag(nonPackedTag);
+            output.WriteInt32(10);
+            output.WriteTag(nonPackedTag);
+            output.WriteInt32(999);
+            output.WriteTag(nonPackedTag);
+            output.WriteInt32(-1000); // Just for variety...
+            output.Flush();
+            stream.Position = 0;
+
+            // Deliberately "expecting" a packed tag, but we detect that the data is
+            // actually not packed.
+            uint packedTag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
+            var field = new RepeatedField<int>();
+            var input = CodedInputStream.CreateInstance(stream);
+            input.AssertNextTag(nonPackedTag);
+            field.AddEntriesFrom(input, FieldCodec.ForInt32(packedTag));
+            CollectionAssert.AreEqual(new[] { 10, 999, -1000 }, field);
+            Assert.IsTrue(input.IsAtEnd);
+        }
+
+        [Test]
+        public void AddEntriesFrom_String()
+        {
+            uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
+            var stream = new MemoryStream();
+            var output = CodedOutputStream.CreateInstance(stream);
+            output.WriteTag(tag);
+            output.WriteString("Foo");
+            output.WriteTag(tag);
+            output.WriteString("");
+            output.WriteTag(tag);
+            output.WriteString("Bar");
+            output.Flush();
+            stream.Position = 0;
+
+            var field = new RepeatedField<string>();
+            var input = CodedInputStream.CreateInstance(stream);
+            input.AssertNextTag(tag);
+            field.AddEntriesFrom(input, FieldCodec.ForString(tag));
+            CollectionAssert.AreEqual(new[] { "Foo", "", "Bar" }, field);
+            Assert.IsTrue(input.IsAtEnd);
+        }
+
+        [Test]
+        public void AddEntriesFrom_Message()
+        {
+            var message1 = new ForeignMessage { C = 2000 };
+            var message2 = new ForeignMessage { C = -250 };
+
+            uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
+            var stream = new MemoryStream();
+            var output = CodedOutputStream.CreateInstance(stream);
+            output.WriteTag(tag);
+            output.WriteMessage(message1);
+            output.WriteTag(tag);
+            output.WriteMessage(message2);
+            output.Flush();
+            stream.Position = 0;
+
+            var field = new RepeatedField<ForeignMessage>();
+            var input = CodedInputStream.CreateInstance(stream);
+            input.AssertNextTag(tag);
+            field.AddEntriesFrom(input, FieldCodec.ForMessage(tag, ForeignMessage.Parser));
+            CollectionAssert.AreEqual(new[] { message1, message2}, field);
+            Assert.IsTrue(input.IsAtEnd);
+        }
+
+        [Test]
+        public void WriteTo_PackedInt32()
+        {
+            uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
+            var field = new RepeatedField<int> { 10, 1000, 1000000 };
+            var stream = new MemoryStream();
+            var output = CodedOutputStream.CreateInstance(stream);
+            field.WriteTo(output, FieldCodec.ForInt32(tag));
+            output.Flush();
+            stream.Position = 0;
+
+            var input = CodedInputStream.CreateInstance(stream);
+            input.AssertNextTag(tag);
+            var length = input.ReadLength();
+            Assert.AreEqual(10, input.ReadInt32());
+            Assert.AreEqual(1000, input.ReadInt32());
+            Assert.AreEqual(1000000, input.ReadInt32());
+            Assert.IsTrue(input.IsAtEnd);
+            Assert.AreEqual(1 + CodedOutputStream.ComputeLengthSize(length) + length, stream.Length);
+        }
+
+        [Test]
+        public void WriteTo_NonPackedInt32()
+        {
+            uint tag = WireFormat.MakeTag(10, WireFormat.WireType.Varint);
+            var field = new RepeatedField<int> { 10, 1000, 1000000};
+            var stream = new MemoryStream();
+            var output = CodedOutputStream.CreateInstance(stream);
+            field.WriteTo(output, FieldCodec.ForInt32(tag));
+            output.Flush();
+            stream.Position = 0;
+
+            var input = CodedInputStream.CreateInstance(stream);
+            input.AssertNextTag(tag);
+            Assert.AreEqual(10, input.ReadInt32());
+            input.AssertNextTag(tag);
+            Assert.AreEqual(1000, input.ReadInt32());
+            input.AssertNextTag(tag);
+            Assert.AreEqual(1000000, input.ReadInt32());
+            Assert.IsTrue(input.IsAtEnd);
+        }
+
+        [Test]
+        public void WriteTo_String()
+        {
+            uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
+            var field = new RepeatedField<string> { "Foo", "", "Bar" };
+            var stream = new MemoryStream();
+            var output = CodedOutputStream.CreateInstance(stream);
+            field.WriteTo(output, FieldCodec.ForString(tag));
+            output.Flush();
+            stream.Position = 0;
+
+            var input = CodedInputStream.CreateInstance(stream);
+            input.AssertNextTag(tag);
+            Assert.AreEqual("Foo", input.ReadString());
+            input.AssertNextTag(tag);
+            Assert.AreEqual("", input.ReadString());
+            input.AssertNextTag(tag);
+            Assert.AreEqual("Bar", input.ReadString());
+            Assert.IsTrue(input.IsAtEnd);
+        }
+
+        [Test]
+        public void WriteTo_Message()
+        {
+            var message1 = new ForeignMessage { C = 20 };
+            var message2 = new ForeignMessage { C = 25 };
+            uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
+            var field = new RepeatedField<ForeignMessage> { message1, message2 };
+            var stream = new MemoryStream();
+            var output = CodedOutputStream.CreateInstance(stream);
+            field.WriteTo(output, FieldCodec.ForMessage(tag, ForeignMessage.Parser));
+            output.Flush();
+            stream.Position = 0;
+
+            var input = CodedInputStream.CreateInstance(stream);
+            input.AssertNextTag(tag);
+            Assert.AreEqual(message1, input.ReadMessage(ForeignMessage.Parser));
+            input.AssertNextTag(tag);
+            Assert.AreEqual(message2, input.ReadMessage(ForeignMessage.Parser));
+            Assert.IsTrue(input.IsAtEnd);
+        }
+
+
+        [Test]
+        public void TestNegativeEnumArray()
+        {
+            int arraySize = 1 + 1 + (11 * 5);
+            int msgSize = arraySize;
+            byte[] bytes = new byte[msgSize];
+            CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
+            uint tag = WireFormat.MakeTag(8, WireFormat.WireType.Varint);
+            for (int i = 0; i >= -5; i--)
+            {
+                output.WriteTag(tag);
+                output.WriteEnum(i);
+            }
+
+            Assert.AreEqual(0, output.SpaceLeft);
+
+            CodedInputStream input = CodedInputStream.CreateInstance(bytes);
+            Assert.IsTrue(input.ReadTag(out tag));
+
+            RepeatedField<SampleEnum> values = new RepeatedField<SampleEnum>();
+            values.AddEntriesFrom(input, FieldCodec.ForEnum(tag, x => (int)x, x => (SampleEnum)x));
+
+            Assert.AreEqual(6, values.Count);
+            Assert.AreEqual(SampleEnum.None, values[0]);
+            Assert.AreEqual(((SampleEnum)(-1)), values[1]);
+            Assert.AreEqual(SampleEnum.NegativeValue, values[2]);
+            Assert.AreEqual(((SampleEnum)(-3)), values[3]);
+            Assert.AreEqual(((SampleEnum)(-4)), values[4]);
+            Assert.AreEqual(((SampleEnum)(-5)), values[5]);
+        }
+
+
+        [Test]
+        public void TestNegativeEnumPackedArray()
+        {
+            int arraySize = 1 + (10 * 5);
+            int msgSize = 1 + 1 + arraySize;
+            byte[] bytes = new byte[msgSize];
+            CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
+            // Length-delimited to show we want the packed representation
+            uint tag = WireFormat.MakeTag(8, WireFormat.WireType.LengthDelimited);
+            output.WriteTag(tag);
+            int size = 0;
+            for (int i = 0; i >= -5; i--)
+            {
+                size += CodedOutputStream.ComputeEnumSize(i);
+            }
+            output.WriteRawVarint32((uint)size);
+            for (int i = 0; i >= -5; i--)
+            {
+                output.WriteEnum(i);
+            }
+            Assert.AreEqual(0, output.SpaceLeft);
+
+            CodedInputStream input = CodedInputStream.CreateInstance(bytes);
+            Assert.IsTrue(input.ReadTag(out tag));
+
+            RepeatedField<SampleEnum> values = new RepeatedField<SampleEnum>();
+            values.AddEntriesFrom(input, FieldCodec.ForEnum(tag, x => (int)x, x => (SampleEnum)x));
+
+            Assert.AreEqual(6, values.Count);
+            Assert.AreEqual(SampleEnum.None, values[0]);
+            Assert.AreEqual(((SampleEnum)(-1)), values[1]);
+            Assert.AreEqual(SampleEnum.NegativeValue, values[2]);
+            Assert.AreEqual(((SampleEnum)(-3)), values[3]);
+            Assert.AreEqual(((SampleEnum)(-4)), values[4]);
+            Assert.AreEqual(((SampleEnum)(-5)), values[5]);
+        }
     }
     }
 }
 }

+ 46 - 17
csharp/src/ProtocolBuffers.Test/FieldCodecTest.cs

@@ -63,15 +63,21 @@ namespace Google.Protobuf
         };
         };
 
 
         [Test, TestCaseSource("Codecs")]
         [Test, TestCaseSource("Codecs")]
-        public void RoundTrip(ICodecTestData codec)
+        public void RoundTripWithTag(ICodecTestData codec)
         {
         {
-            codec.TestRoundTrip();
+            codec.TestRoundTripWithTag();
+        }
+
+        [Test, TestCaseSource("Codecs")]
+        public void RoundTripRaw(ICodecTestData codec)
+        {
+            codec.TestRoundTripRaw();
         }
         }
 
 
         [Test, TestCaseSource("Codecs")]
         [Test, TestCaseSource("Codecs")]
         public void CalculateSize(ICodecTestData codec)
         public void CalculateSize(ICodecTestData codec)
         {
         {
-            codec.TestCalculateSize();
+            codec.TestCalculateSizeWithTag();
         }
         }
 
 
         [Test, TestCaseSource("Codecs")]
         [Test, TestCaseSource("Codecs")]
@@ -82,8 +88,9 @@ namespace Google.Protobuf
 
 
         public interface ICodecTestData
         public interface ICodecTestData
         {
         {
-            void TestRoundTrip();
-            void TestCalculateSize();
+            void TestRoundTripRaw();
+            void TestRoundTripWithTag();
+            void TestCalculateSizeWithTag();
             void TestDefaultValue();
             void TestDefaultValue();
         }
         }
 
 
@@ -100,45 +107,67 @@ namespace Google.Protobuf
                 this.name = name;
                 this.name = name;
             }
             }
 
 
-            public void TestRoundTrip()
+            public void TestRoundTripRaw()
             {
             {
                 var stream = new MemoryStream();
                 var stream = new MemoryStream();
                 var codedOutput = CodedOutputStream.CreateInstance(stream);
                 var codedOutput = CodedOutputStream.CreateInstance(stream);
-                codec.Write(codedOutput, sampleValue);
+                codec.ValueWriter(codedOutput, sampleValue);
                 codedOutput.Flush();
                 codedOutput.Flush();
                 stream.Position = 0;
                 stream.Position = 0;
                 var codedInput = CodedInputStream.CreateInstance(stream);
                 var codedInput = CodedInputStream.CreateInstance(stream);
-                uint tag;
-                Assert.IsTrue(codedInput.ReadTag(out tag));
-                Assert.AreEqual(codec.Tag, tag);
+                Assert.AreEqual(sampleValue, codec.ValueReader(codedInput));
+                Assert.IsTrue(codedInput.IsAtEnd);
+            }
+
+            public void TestRoundTripWithTag()
+            {
+                var stream = new MemoryStream();
+                var codedOutput = CodedOutputStream.CreateInstance(stream);
+                codec.WriteTagAndValue(codedOutput, sampleValue);
+                codedOutput.Flush();
+                stream.Position = 0;
+                var codedInput = CodedInputStream.CreateInstance(stream);
+                codedInput.AssertNextTag(codec.Tag);
                 Assert.AreEqual(sampleValue, codec.Read(codedInput));
                 Assert.AreEqual(sampleValue, codec.Read(codedInput));
                 Assert.IsTrue(codedInput.IsAtEnd);
                 Assert.IsTrue(codedInput.IsAtEnd);
             }
             }
 
 
-            public void TestCalculateSize()
+            public void TestCalculateSizeWithTag()
             {
             {
                 var stream = new MemoryStream();
                 var stream = new MemoryStream();
                 var codedOutput = CodedOutputStream.CreateInstance(stream);
                 var codedOutput = CodedOutputStream.CreateInstance(stream);
-                codec.Write(codedOutput, sampleValue);
+                codec.WriteTagAndValue(codedOutput, sampleValue);
                 codedOutput.Flush();
                 codedOutput.Flush();
-                Assert.AreEqual(stream.Position, codec.CalculateSize(sampleValue));
+                Assert.AreEqual(stream.Position, codec.CalculateSizeWithTag(sampleValue));
             }
             }
 
 
             public void TestDefaultValue()
             public void TestDefaultValue()
             {
             {
+                // WriteTagAndValue ignores default values
                 var stream = new MemoryStream();
                 var stream = new MemoryStream();
                 var codedOutput = CodedOutputStream.CreateInstance(stream);
                 var codedOutput = CodedOutputStream.CreateInstance(stream);
-                codec.Write(codedOutput, codec.DefaultValue);
+                codec.WriteTagAndValue(codedOutput, codec.DefaultValue);
                 codedOutput.Flush();
                 codedOutput.Flush();
                 Assert.AreEqual(0, stream.Position);
                 Assert.AreEqual(0, stream.Position);
-                Assert.AreEqual(0, codec.CalculateSize(codec.DefaultValue));
+                Assert.AreEqual(0, codec.CalculateSizeWithTag(codec.DefaultValue));
                 if (typeof(T).IsValueType)
                 if (typeof(T).IsValueType)
                 {
                 {
                     Assert.AreEqual(default(T), codec.DefaultValue);
                     Assert.AreEqual(default(T), codec.DefaultValue);
                 }
                 }
-            }
 
 
-            public string Description { get { return name; } }
+                // The plain ValueWriter/ValueReader delegates don't.
+                if (codec.DefaultValue != null) // This part isn't appropriate for message types.
+                {
+                    codedOutput = CodedOutputStream.CreateInstance(stream);
+                    codec.ValueWriter(codedOutput, codec.DefaultValue);
+                    codedOutput.Flush();
+                    Assert.AreNotEqual(0, stream.Position);
+                    Assert.AreEqual(stream.Position, codec.ValueSizeCalculator(codec.DefaultValue));
+                    stream.Position = 0;
+                    var codedInput = CodedInputStream.CreateInstance(stream);
+                    Assert.AreEqual(codec.DefaultValue, codec.ValueReader(codedInput));
+                }
+            }
 
 
             public override string ToString()
             public override string ToString()
             {
             {

+ 133 - 0
csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs

@@ -10,6 +10,61 @@ namespace Google.Protobuf
     /// </summary>
     /// </summary>
     public class GeneratedMessageTest
     public class GeneratedMessageTest
     {
     {
+        /// <summary>
+        /// Returns a sample TestAllTypes with all fields populated
+        /// </summary>
+        public static TestAllTypes GetSampleMessage()
+        {
+            return new TestAllTypes
+            {
+                SingleBool = true,
+                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
+                SingleDouble = 23.5,
+                SingleFixed32 = 23,
+                SingleFixed64 = 1234567890123,
+                SingleFloat = 12.25f,
+                SingleForeignEnum = ForeignEnum.FOREIGN_BAR,
+                SingleForeignMessage = new ForeignMessage { C = 10 },
+                SingleImportEnum = ImportEnum.IMPORT_BAZ,
+                SingleImportMessage = new ImportMessage { D = 20 },
+                SingleInt32 = 100,
+                SingleInt64 = 3210987654321,
+                SingleNestedEnum = TestAllTypes.Types.NestedEnum.FOO,
+                SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 },
+                SinglePublicImportMessage = new PublicImportMessage { E = 54 },
+                SingleSfixed32 = -123,
+                SingleSfixed64 = -12345678901234,
+                SingleSint32 = -456,
+                SingleSint64 = -12345678901235,
+                SingleString = "test",
+                SingleUint32 = uint.MaxValue,
+                SingleUint64 = ulong.MaxValue,
+                RepeatedBool = { true, false },
+                RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6), ByteString.CopyFrom(new byte[1000]) },
+                RepeatedDouble = { -12.25, 23.5 },
+                RepeatedFixed32 = { uint.MaxValue, 23 },
+                RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
+                RepeatedFloat = { 100f, 12.25f },
+                RepeatedForeignEnum = { ForeignEnum.FOREIGN_FOO, ForeignEnum.FOREIGN_BAR },
+                RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } },
+                RepeatedImportEnum = { ImportEnum.IMPORT_BAZ, ImportEnum.IMPORT_ENUM_UNSPECIFIED },
+                RepeatedImportMessage = { new ImportMessage { D = 20 }, new ImportMessage { D = 25 } },
+                RepeatedInt32 = { 100, 200 },
+                RepeatedInt64 = { 3210987654321, long.MaxValue },
+                RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.FOO, TestAllTypes.Types.NestedEnum.NEG },
+                RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 35 }, new TestAllTypes.Types.NestedMessage { Bb = 10 } },
+                RepeatedPublicImportMessage = { new PublicImportMessage { E = 54 }, new PublicImportMessage { E = -1 } },
+                RepeatedSfixed32 = { -123, 123 },
+                RepeatedSfixed64 = { -12345678901234, 12345678901234 },
+                RepeatedSint32 = { -456, 100 },
+                RepeatedSint64 = { -12345678901235, 123 },
+                RepeatedString = { "foo", "bar" },
+                RepeatedUint32 = { uint.MaxValue, uint.MinValue },
+                RepeatedUint64 = { ulong.MaxValue, uint.MinValue },
+                OneofString = "Oneof string"                
+            };
+        }
+
         [Test]
         [Test]
         public void EmptyMessageFieldDistinctFromMissingMessageField()
         public void EmptyMessageFieldDistinctFromMissingMessageField()
         {
         {
@@ -485,5 +540,83 @@ namespace Google.Protobuf
             Assert.Throws<InvalidOperationException>(() => frozen.RepeatedDouble.Add(0.0));
             Assert.Throws<InvalidOperationException>(() => frozen.RepeatedDouble.Add(0.0));
             Assert.Throws<InvalidOperationException>(() => frozen.RepeatedNestedMessage.Add(new TestAllTypes.Types.NestedMessage()));
             Assert.Throws<InvalidOperationException>(() => frozen.RepeatedNestedMessage.Add(new TestAllTypes.Types.NestedMessage()));
         }
         }
+
+        [Test]
+        public void OneofProperties()
+        {
+            // Switch the oneof case between each of the different options, and check everything behaves
+            // as expected in each case.
+            var message = new TestAllTypes();
+            Assert.AreEqual("", message.OneofString);
+            Assert.AreEqual(0, message.OneofUint32);
+            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+            Assert.IsNull(message.OneofNestedMessage);
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
+
+            message.OneofString = "sample";
+            Assert.AreEqual("sample", message.OneofString);
+            Assert.AreEqual(0, message.OneofUint32);
+            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+            Assert.IsNull(message.OneofNestedMessage);
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message.OneofFieldCase);
+
+            var bytes = ByteString.CopyFrom(1, 2, 3);
+            message.OneofBytes = bytes;
+            Assert.AreEqual("", message.OneofString);
+            Assert.AreEqual(0, message.OneofUint32);
+            Assert.AreEqual(bytes, message.OneofBytes);
+            Assert.IsNull(message.OneofNestedMessage);
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofBytes, message.OneofFieldCase);
+
+            message.OneofUint32 = 20;
+            Assert.AreEqual("", message.OneofString);
+            Assert.AreEqual(20, message.OneofUint32);
+            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+            Assert.IsNull(message.OneofNestedMessage);
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message.OneofFieldCase);
+
+            var nestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 25 };
+            message.OneofNestedMessage = nestedMessage;
+            Assert.AreEqual("", message.OneofString);
+            Assert.AreEqual(0, message.OneofUint32);
+            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+            Assert.AreEqual(nestedMessage, message.OneofNestedMessage);
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofNestedMessage, message.OneofFieldCase);
+
+            message.ClearOneofField();
+            Assert.AreEqual("", message.OneofString);
+            Assert.AreEqual(0, message.OneofUint32);
+            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+            Assert.IsNull(message.OneofNestedMessage);
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
+        }
+
+        [Test]
+        public void OneofSerialization_NonDefaultValue()
+        {
+            var message = new TestAllTypes();
+            message.OneofString = "this would take a bit of space";
+            message.OneofUint32 = 10;
+            var bytes = message.ToByteArray();
+            Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - no string!
+
+            var message2 = TestAllTypes.Parser.ParseFrom(bytes);
+            Assert.AreEqual(message, message2);
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
+        }
+
+        [Test]
+        public void OneofSerialization_DefaultValue()
+        {
+            var message = new TestAllTypes();
+            message.OneofString = "this would take a bit of space";
+            message.OneofUint32 = 0; // This is the default value for UInt32; normally wouldn't be serialized
+            var bytes = message.ToByteArray();
+            Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - it's still serialized
+
+            var message2 = TestAllTypes.Parser.ParseFrom(bytes);
+            Assert.AreEqual(message, message2);
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
+        }
     }
     }
 }
 }

+ 2 - 0
csharp/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj

@@ -72,6 +72,7 @@
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <Compile Include="ByteStringTest.cs" />
     <Compile Include="ByteStringTest.cs" />
+    <Compile Include="CodedInputStreamExtensions.cs" />
     <Compile Include="CodedInputStreamTest.cs" />
     <Compile Include="CodedInputStreamTest.cs" />
     <Compile Include="CodedOutputStreamTest.cs" />
     <Compile Include="CodedOutputStreamTest.cs" />
     <Compile Include="EqualityTester.cs" />
     <Compile Include="EqualityTester.cs" />
@@ -79,6 +80,7 @@
     <Compile Include="GeneratedMessageTest.cs" />
     <Compile Include="GeneratedMessageTest.cs" />
     <Compile Include="Collections\MapFieldTest.cs" />
     <Compile Include="Collections\MapFieldTest.cs" />
     <Compile Include="Collections\RepeatedFieldTest.cs" />
     <Compile Include="Collections\RepeatedFieldTest.cs" />
+    <Compile Include="SampleEnum.cs" />
     <Compile Include="TestProtos\MapUnittestProto3.cs" />
     <Compile Include="TestProtos\MapUnittestProto3.cs" />
     <Compile Include="TestProtos\UnittestImportProto3.cs" />
     <Compile Include="TestProtos\UnittestImportProto3.cs" />
     <Compile Include="TestProtos\UnittestImportPublicProto3.cs" />
     <Compile Include="TestProtos\UnittestImportPublicProto3.cs" />

+ 42 - 0
csharp/src/ProtocolBuffers.Test/SampleEnum.cs

@@ -0,0 +1,42 @@
+#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
+    
+namespace Google.Protobuf
+{
+    // Just a sample enum with positive and negative values to be used in tests.
+    internal enum SampleEnum
+    {
+        NegativeValue = -2,
+        None = 0,
+        PositiveValue = 3
+    }
+}

+ 25 - 64
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestIssues.cs

@@ -422,13 +422,15 @@ namespace UnitTest.Issues.TestProtos {
     }
     }
 
 
     public const int ValuesFieldNumber = 2;
     public const int ValuesFieldNumber = 2;
-    private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> values_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
+    private static readonly pb::FieldCodec<global::UnitTest.Issues.TestProtos.NegativeEnum> _repeated_values_codec
+        = pb::FieldCodec.ForEnum(16, x => (int) x, x => (global::UnitTest.Issues.TestProtos.NegativeEnum) x);private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> values_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
     public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> Values {
     public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> Values {
       get { return values_; }
       get { return values_; }
     }
     }
 
 
     public const int PackedValuesFieldNumber = 3;
     public const int PackedValuesFieldNumber = 3;
-    private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> packedValues_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
+    private static readonly pb::FieldCodec<global::UnitTest.Issues.TestProtos.NegativeEnum> _repeated_packedValues_codec
+        = pb::FieldCodec.ForEnum(26, x => (int) x, x => (global::UnitTest.Issues.TestProtos.NegativeEnum) x);private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> packedValues_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
     public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> PackedValues {
     public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> PackedValues {
       get { return packedValues_; }
       get { return packedValues_; }
     }
     }
@@ -463,13 +465,8 @@ namespace UnitTest.Issues.TestProtos {
         output.WriteRawTag(8);
         output.WriteRawTag(8);
         output.WriteEnum((int) Value);
         output.WriteEnum((int) Value);
       }
       }
-      if (values_.Count > 0) {
-        output.WriteEnumArray(2, values_);
-      }
-      if (packedValues_.Count > 0) {
-        output.WriteRawTag(26);
-        output.WritePackedEnumArray(packedValues_);
-      }
+      values_.WriteTo(output, _repeated_values_codec);
+      packedValues_.WriteTo(output, _repeated_packedValues_codec);
     }
     }
 
 
     public int CalculateSize() {
     public int CalculateSize() {
@@ -477,22 +474,8 @@ namespace UnitTest.Issues.TestProtos {
       if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO) {
       if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Value);
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Value);
       }
       }
-      if (values_.Count > 0) {
-        int dataSize = 0;
-        foreach (global::UnitTest.Issues.TestProtos.NegativeEnum element in values_) {
-          dataSize += pb::CodedOutputStream.ComputeEnumSize((int) element);
-        }
-        size += dataSize;
-        size += 1 * values_.Count;
-      }
-      if (packedValues_.Count > 0) {
-        int dataSize = 0;
-        foreach (global::UnitTest.Issues.TestProtos.NegativeEnum element in packedValues_) {
-          dataSize += pb::CodedOutputStream.ComputeEnumSize((int) element);
-        }
-        size += dataSize;
-        size += 1 + pb::CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);
-      }
+      size += values_.CalculateSize(_repeated_values_codec);
+      size += packedValues_.CalculateSize(_repeated_packedValues_codec);
       return size;
       return size;
     }
     }
 
 
@@ -524,12 +507,12 @@ namespace UnitTest.Issues.TestProtos {
           }
           }
           case 18:
           case 18:
           case 16: {
           case 16: {
-            input.ReadEnumArray<global::UnitTest.Issues.TestProtos.NegativeEnum>(values_);
+            values_.AddEntriesFrom(input, _repeated_values_codec);
             break;
             break;
           }
           }
           case 26:
           case 26:
           case 24: {
           case 24: {
-            input.ReadEnumArray<global::UnitTest.Issues.TestProtos.NegativeEnum>(packedValues_);
+            packedValues_.AddEntriesFrom(input, _repeated_packedValues_codec);
             break;
             break;
           }
           }
         }
         }
@@ -678,6 +661,8 @@ namespace UnitTest.Issues.TestProtos {
     }
     }
 
 
     public const int PrimitiveArrayFieldNumber = 2;
     public const int PrimitiveArrayFieldNumber = 2;
+    private static readonly pb::FieldCodec<int> _repeated_primitiveArray_codec
+        = pb::FieldCodec.ForInt32(18);
     private readonly pbc::RepeatedField<int> primitiveArray_ = new pbc::RepeatedField<int>();
     private readonly pbc::RepeatedField<int> primitiveArray_ = new pbc::RepeatedField<int>();
     [global::System.ObsoleteAttribute()]
     [global::System.ObsoleteAttribute()]
     public pbc::RepeatedField<int> PrimitiveArray {
     public pbc::RepeatedField<int> PrimitiveArray {
@@ -696,6 +681,8 @@ namespace UnitTest.Issues.TestProtos {
     }
     }
 
 
     public const int MessageArrayFieldNumber = 4;
     public const int MessageArrayFieldNumber = 4;
+    private static readonly pb::FieldCodec<global::UnitTest.Issues.TestProtos.DeprecatedChild> _repeated_messageArray_codec
+        = pb::FieldCodec.ForMessage(34, global::UnitTest.Issues.TestProtos.DeprecatedChild.Parser);
     private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild> messageArray_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild>();
     private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild> messageArray_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild>();
     [global::System.ObsoleteAttribute()]
     [global::System.ObsoleteAttribute()]
     public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild> MessageArray {
     public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild> MessageArray {
@@ -714,7 +701,8 @@ namespace UnitTest.Issues.TestProtos {
     }
     }
 
 
     public const int EnumArrayFieldNumber = 6;
     public const int EnumArrayFieldNumber = 6;
-    private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> enumArray_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum>();
+    private static readonly pb::FieldCodec<global::UnitTest.Issues.TestProtos.DeprecatedEnum> _repeated_enumArray_codec
+        = pb::FieldCodec.ForEnum(50, x => (int) x, x => (global::UnitTest.Issues.TestProtos.DeprecatedEnum) x);private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> enumArray_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum>();
     [global::System.ObsoleteAttribute()]
     [global::System.ObsoleteAttribute()]
     public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> EnumArray {
     public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> EnumArray {
       get { return enumArray_; }
       get { return enumArray_; }
@@ -756,25 +744,17 @@ namespace UnitTest.Issues.TestProtos {
         output.WriteRawTag(8);
         output.WriteRawTag(8);
         output.WriteInt32(PrimitiveValue);
         output.WriteInt32(PrimitiveValue);
       }
       }
-      if (primitiveArray_.Count > 0) {
-        output.WriteRawTag(18);
-        output.WritePackedInt32Array(primitiveArray_);
-      }
+      primitiveArray_.WriteTo(output, _repeated_primitiveArray_codec);
       if (messageValue_ != null) {
       if (messageValue_ != null) {
         output.WriteRawTag(26);
         output.WriteRawTag(26);
         output.WriteMessage(MessageValue);
         output.WriteMessage(MessageValue);
       }
       }
-      if (messageArray_.Count > 0) {
-        output.WriteMessageArray(4, messageArray_);
-      }
+      messageArray_.WriteTo(output, _repeated_messageArray_codec);
       if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO) {
       if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO) {
         output.WriteRawTag(40);
         output.WriteRawTag(40);
         output.WriteEnum((int) EnumValue);
         output.WriteEnum((int) EnumValue);
       }
       }
-      if (enumArray_.Count > 0) {
-        output.WriteRawTag(50);
-        output.WritePackedEnumArray(enumArray_);
-      }
+      enumArray_.WriteTo(output, _repeated_enumArray_codec);
     }
     }
 
 
     public int CalculateSize() {
     public int CalculateSize() {
@@ -782,34 +762,15 @@ namespace UnitTest.Issues.TestProtos {
       if (PrimitiveValue != 0) {
       if (PrimitiveValue != 0) {
         size += 1 + pb::CodedOutputStream.ComputeInt32Size(PrimitiveValue);
         size += 1 + pb::CodedOutputStream.ComputeInt32Size(PrimitiveValue);
       }
       }
-      if (primitiveArray_.Count > 0) {
-        int dataSize = 0;
-        foreach (int element in primitiveArray_) {
-          dataSize += pb::CodedOutputStream.ComputeInt32Size(element);
-        }
-        size += dataSize;
-        size += 1 + pb::CodedOutputStream.ComputeInt32Size(dataSize);
-      }
+      size += primitiveArray_.CalculateSize(_repeated_primitiveArray_codec);
       if (messageValue_ != null) {
       if (messageValue_ != null) {
         size += 1 + pb::CodedOutputStream.ComputeMessageSize(MessageValue);
         size += 1 + pb::CodedOutputStream.ComputeMessageSize(MessageValue);
       }
       }
-      if (messageArray_.Count > 0) {
-        foreach (global::UnitTest.Issues.TestProtos.DeprecatedChild element in messageArray_) {
-          size += pb::CodedOutputStream.ComputeMessageSize(element);
-        }
-        size += 1 * messageArray_.Count;
-      }
+      size += messageArray_.CalculateSize(_repeated_messageArray_codec);
       if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO) {
       if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) EnumValue);
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) EnumValue);
       }
       }
-      if (enumArray_.Count > 0) {
-        int dataSize = 0;
-        foreach (global::UnitTest.Issues.TestProtos.DeprecatedEnum element in enumArray_) {
-          dataSize += pb::CodedOutputStream.ComputeEnumSize((int) element);
-        }
-        size += dataSize;
-        size += 1 + pb::CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);
-      }
+      size += enumArray_.CalculateSize(_repeated_enumArray_codec);
       return size;
       return size;
     }
     }
 
 
@@ -851,7 +812,7 @@ namespace UnitTest.Issues.TestProtos {
           }
           }
           case 18:
           case 18:
           case 16: {
           case 16: {
-            input.ReadInt32Array(primitiveArray_);
+            primitiveArray_.AddEntriesFrom(input, _repeated_primitiveArray_codec);
             break;
             break;
           }
           }
           case 26: {
           case 26: {
@@ -862,7 +823,7 @@ namespace UnitTest.Issues.TestProtos {
             break;
             break;
           }
           }
           case 34: {
           case 34: {
-            input.ReadMessageArray(messageArray_, global::UnitTest.Issues.TestProtos.DeprecatedChild.Parser);
+            messageArray_.AddEntriesFrom(input, _repeated_messageArray_codec);
             break;
             break;
           }
           }
           case 40: {
           case 40: {
@@ -871,7 +832,7 @@ namespace UnitTest.Issues.TestProtos {
           }
           }
           case 50:
           case 50:
           case 48: {
           case 48: {
-            input.ReadEnumArray<global::UnitTest.Issues.TestProtos.DeprecatedEnum>(enumArray_);
+            enumArray_.AddEntriesFrom(input, _repeated_enumArray_codec);
             break;
             break;
           }
           }
         }
         }

文件差异内容过多而无法显示
+ 196 - 487
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestProto3.cs


+ 148 - 436
csharp/src/ProtocolBuffers/CodedInputStream.cs

@@ -37,7 +37,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
-using Google.Protobuf.Collections;
 
 
 namespace Google.Protobuf
 namespace Google.Protobuf
 {
 {
@@ -172,8 +171,67 @@ namespace Google.Protobuf
             }
             }
         }
         }
 
 
-        #region Validation
+        /// <summary>
+        /// Returns the last tag read, or 0 if no tags have been read or we've read beyond
+        /// the end of the stream.
+        /// </summary>
+        internal uint LastTag { get { return lastTag; } }
+
+        #region Limits for recursion and length
+        /// <summary>
+        /// Set the maximum message recursion depth.
+        /// </summary>
+        /// <remarks>
+        /// In order to prevent malicious
+        /// messages from causing stack overflows, CodedInputStream limits
+        /// how deeply messages may be nested.  The default limit is 64.
+        /// </remarks>
+        public int SetRecursionLimit(int limit)
+        {
+            if (limit < 0)
+            {
+                throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
+            }
+            int oldLimit = recursionLimit;
+            recursionLimit = limit;
+            return oldLimit;
+        }
 
 
+        /// <summary>
+        /// Set the maximum message size.
+        /// </summary>
+        /// <remarks>
+        /// In order to prevent malicious messages from exhausting memory or
+        /// causing integer overflows, CodedInputStream limits how large a message may be.
+        /// The default limit is 64MB.  You should set this limit as small
+        /// as you can without harming your app's functionality.  Note that
+        /// size limits only apply when reading from an InputStream, not
+        /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
+        /// If you want to read several messages from a single CodedInputStream, you
+        /// can call ResetSizeCounter() after each message to avoid hitting the
+        /// size limit.
+        /// </remarks>
+        public int SetSizeLimit(int limit)
+        {
+            if (limit < 0)
+            {
+                throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
+            }
+            int oldLimit = sizeLimit;
+            sizeLimit = limit;
+            return oldLimit;
+        }
+
+        /// <summary>
+        /// Resets the current size counter to zero (see <see cref="SetSizeLimit"/>).
+        /// </summary>
+        public void ResetSizeCounter()
+        {
+            totalBytesRetired = 0;
+        }
+        #endregion
+
+        #region Validation
         /// <summary>
         /// <summary>
         /// Verifies that the last call to ReadTag() returned the given tag value.
         /// Verifies that the last call to ReadTag() returned the given tag value.
         /// This is used to verify that a nested group ended with the correct
         /// This is used to verify that a nested group ended with the correct
@@ -188,13 +246,12 @@ namespace Google.Protobuf
                 throw InvalidProtocolBufferException.InvalidEndTag();
                 throw InvalidProtocolBufferException.InvalidEndTag();
             }
             }
         }
         }
-
         #endregion
         #endregion
 
 
         #region Reading of tags etc
         #region Reading of tags etc
 
 
         /// <summary>
         /// <summary>
-        /// Attempt to peek at the next field tag.
+        /// Attempts to peek at the next field tag.
         /// </summary>
         /// </summary>
         public bool PeekNextTag(out uint fieldTag)
         public bool PeekNextTag(out uint fieldTag)
         {
         {
@@ -212,7 +269,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Attempt to read a field tag, returning false if we have reached the end
+        /// Attempts to read a field tag, returning false if we have reached the end
         /// of the input data.
         /// of the input data.
         /// </summary>
         /// </summary>
         /// <param name="fieldTag">The 'tag' of the field (id * 8 + wire-format)</param>
         /// <param name="fieldTag">The 'tag' of the field (id * 8 + wire-format)</param>
@@ -227,14 +284,42 @@ namespace Google.Protobuf
                 return true;
                 return true;
             }
             }
 
 
-            if (IsAtEnd)
+            // Optimize for the incredibly common case of having at least two bytes left in the buffer,
+            // and those two bytes being enough to get the tag. This will be true for fields up to 4095.
+            if (bufferPos + 2 <= bufferSize)
             {
             {
-                fieldTag = 0;
-                lastTag = fieldTag;
-                return false;
+                int tmp = buffer[bufferPos++];
+                if (tmp < 128)
+                {
+                    fieldTag = (uint)tmp;
+                }
+                else
+                {
+                    int result = tmp & 0x7f;
+                    if ((tmp = buffer[bufferPos++]) < 128)
+                    {
+                        result |= tmp << 7;
+                        fieldTag = (uint) result;
+                    }
+                    else
+                    {
+                        // Nope, rewind and go the potentially slow route.
+                        bufferPos -= 2;
+                        fieldTag = ReadRawVarint32();
+                    }
+                }
             }
             }
+            else
+            {
+                if (IsAtEnd)
+                {
+                    fieldTag = 0;
+                    lastTag = fieldTag;
+                    return false;
+                }
 
 
-            fieldTag = ReadRawVarint32();
+                fieldTag = ReadRawVarint32();
+            }
             lastTag = fieldTag;
             lastTag = fieldTag;
             if (lastTag == 0)
             if (lastTag == 0)
             {
             {
@@ -245,7 +330,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read a double field from the stream.
+        /// Reads a double field from the stream.
         /// </summary>
         /// </summary>
         public double ReadDouble()
         public double ReadDouble()
         {
         {
@@ -253,7 +338,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read a float field from the stream.
+        /// Reads a float field from the stream.
         /// </summary>
         /// </summary>
         public float ReadFloat()
         public float ReadFloat()
         {
         {
@@ -275,7 +360,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read a uint64 field from the stream.
+        /// Reads a uint64 field from the stream.
         /// </summary>
         /// </summary>
         public ulong ReadUInt64()
         public ulong ReadUInt64()
         {
         {
@@ -283,7 +368,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read an int64 field from the stream.
+        /// Reads an int64 field from the stream.
         /// </summary>
         /// </summary>
         public long ReadInt64()
         public long ReadInt64()
         {
         {
@@ -291,7 +376,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read an int32 field from the stream.
+        /// Reads an int32 field from the stream.
         /// </summary>
         /// </summary>
         public int ReadInt32()
         public int ReadInt32()
         {
         {
@@ -299,7 +384,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read a fixed64 field from the stream.
+        /// Reads a fixed64 field from the stream.
         /// </summary>
         /// </summary>
         public ulong ReadFixed64()
         public ulong ReadFixed64()
         {
         {
@@ -307,7 +392,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read a fixed32 field from the stream.
+        /// Reads a fixed32 field from the stream.
         /// </summary>
         /// </summary>
         public uint ReadFixed32()
         public uint ReadFixed32()
         {
         {
@@ -315,7 +400,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read a bool field from the stream.
+        /// Reads a bool field from the stream.
         /// </summary>
         /// </summary>
         public bool ReadBool()
         public bool ReadBool()
         {
         {
@@ -327,22 +412,22 @@ namespace Google.Protobuf
         /// </summary>
         /// </summary>
         public string ReadString()
         public string ReadString()
         {
         {
-            int size = (int) ReadRawVarint32();
+            int length = ReadLength();
             // No need to read any data for an empty string.
             // No need to read any data for an empty string.
-            if (size == 0)
+            if (length == 0)
             {
             {
                 return "";
                 return "";
             }
             }
-            if (size <= bufferSize - bufferPos)
+            if (length <= bufferSize - bufferPos)
             {
             {
                 // Fast path:  We already have the bytes in a contiguous buffer, so
                 // Fast path:  We already have the bytes in a contiguous buffer, so
                 //   just copy directly from it.
                 //   just copy directly from it.
-                String result = CodedOutputStream.Utf8Encoding.GetString(buffer, bufferPos, size);
-                bufferPos += size;
+                String result = CodedOutputStream.Utf8Encoding.GetString(buffer, bufferPos, length);
+                bufferPos += length;
                 return result;
                 return result;
             }
             }
             // Slow path: Build a byte array first then copy it.
             // Slow path: Build a byte array first then copy it.
-            return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(size), 0, size);
+            return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(length), 0, length);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -350,7 +435,7 @@ namespace Google.Protobuf
         /// </summary>   
         /// </summary>   
         public void ReadMessage(IMessage builder)
         public void ReadMessage(IMessage builder)
         {
         {
-            int length = (int) ReadRawVarint32();
+            int length = ReadLength();
             if (recursionDepth >= recursionLimit)
             if (recursionDepth >= recursionLimit)
             {
             {
                 throw InvalidProtocolBufferException.RecursionLimitExceeded();
                 throw InvalidProtocolBufferException.RecursionLimitExceeded();
@@ -368,19 +453,19 @@ namespace Google.Protobuf
         /// </summary>   
         /// </summary>   
         public ByteString ReadBytes()
         public ByteString ReadBytes()
         {
         {
-            int size = (int) ReadRawVarint32();
-            if (size <= bufferSize - bufferPos && size > 0)
+            int length = ReadLength();
+            if (length <= bufferSize - bufferPos && length > 0)
             {
             {
                 // Fast path:  We already have the bytes in a contiguous buffer, so
                 // Fast path:  We already have the bytes in a contiguous buffer, so
                 //   just copy directly from it.
                 //   just copy directly from it.
-                ByteString result = ByteString.CopyFrom(buffer, bufferPos, size);
-                bufferPos += size;
+                ByteString result = ByteString.CopyFrom(buffer, bufferPos, length);
+                bufferPos += length;
                 return result;
                 return result;
             }
             }
             else
             else
             {
             {
                 // Slow path:  Build a byte array and attach it to a new ByteString.
                 // Slow path:  Build a byte array and attach it to a new ByteString.
-                return ByteString.AttachBytes(ReadRawBytes(size));
+                return ByteString.AttachBytes(ReadRawBytes(length));
             }
             }
         }
         }
 
 
@@ -435,24 +520,16 @@ namespace Google.Protobuf
             return DecodeZigZag64(ReadRawVarint64());
             return DecodeZigZag64(ReadRawVarint64());
         }
         }
 
 
-        private bool BeginArray(uint fieldTag, out bool isPacked, out int oldLimit)
+        /// <summary>
+        /// Reads a length for length-delimited data.
+        /// </summary>
+        /// <remarks>
+        /// This is internally just reading a varint, but this method exists
+        /// to make the calling code clearer.
+        /// </remarks>
+        public int ReadLength()
         {
         {
-            isPacked = WireFormat.GetTagWireType(fieldTag) == WireFormat.WireType.LengthDelimited;
-
-            if (isPacked)
-            {
-                int length = (int) (ReadRawVarint32() & int.MaxValue);
-                if (length > 0)
-                {
-                    oldLimit = PushLimit(length);
-                    return true;
-                }
-                oldLimit = -1;
-                return false; //packed but empty
-            }
-
-            oldLimit = -1;
-            return true;
+            return (int) ReadRawVarint32();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -474,268 +551,6 @@ namespace Google.Protobuf
             return false;
             return false;
         }
         }
 
 
-        /// <summary>
-        /// Returns true if the next tag is also part of the same array, which may or may not be packed.
-        /// </summary>
-        private bool ContinueArray(uint currentTag, bool packed, int oldLimit)
-        {
-            if (packed)
-            {
-                if (ReachedLimit)
-                {
-                    PopLimit(oldLimit);
-                    return false;
-                }
-                return true;
-            }
-            return MaybeConsumeTag(currentTag);
-        }
-
-        /// <summary>
-        /// Reads a string array.
-        /// </summary>
-        /// <remarks>The stream is assumed to be positioned after a tag indicating the field
-        /// repeated string value. A string is read, and then if the next tag is the same,
-        /// the process is repeated, until the next tag is a different one.</remarks>
-        /// <param name="list"></param>
-        public void ReadStringArray(ICollection<string> list)
-        {
-            uint fieldTag = lastTag;
-            do
-            {
-                list.Add(ReadString());
-            } while (MaybeConsumeTag(fieldTag));
-        }
-
-        public void ReadBytesArray(ICollection<ByteString> list)
-        {
-            uint fieldTag = lastTag;
-            do
-            {
-                list.Add(ReadBytes());
-            } while (MaybeConsumeTag(fieldTag));
-        }
-
-        public void ReadBoolArray(ICollection<bool> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadBool());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadInt32Array(ICollection<int> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadInt32());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadSInt32Array(ICollection<int> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadSInt32());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadUInt32Array(ICollection<uint> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadUInt32());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadFixed32Array(ICollection<uint> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadFixed32());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadSFixed32Array(ICollection<int> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadSFixed32());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadInt64Array(ICollection<long> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadInt64());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadSInt64Array(ICollection<long> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadSInt64());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadUInt64Array(ICollection<ulong> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadUInt64());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadFixed64Array(ICollection<ulong> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadFixed64());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadSFixed64Array(ICollection<long> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadSFixed64());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadDoubleArray(ICollection<double> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadDouble());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadFloatArray(ICollection<float> list)
-        {
-            uint fieldTag = lastTag;
-            bool isPacked;
-            int holdLimit;
-            if (BeginArray(fieldTag, out isPacked, out holdLimit))
-            {
-                do
-                {
-                    list.Add(ReadFloat());
-                } while (ContinueArray(fieldTag, isPacked, holdLimit));
-            }
-        }
-
-        public void ReadEnumArray<T>(RepeatedField<T> list)
-            where T : struct, IComparable, IFormattable
-        {
-            uint fieldTag = lastTag;
-            WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
-
-            // 2.3 allows packed form even if the field is not declared packed.
-            if (wformat == WireFormat.WireType.LengthDelimited)
-            {
-                int length = (int) (ReadRawVarint32() & int.MaxValue);
-                int limit = PushLimit(length);
-                while (!ReachedLimit)
-                {
-                    // Ghastly hack, but it works...
-                    list.AddInt32(ReadEnum());
-                }
-                PopLimit(limit);
-            }
-            else
-            {
-                do
-                {
-                    list.Add((T)(object) ReadEnum());
-                } while (MaybeConsumeTag(fieldTag));
-            }
-        }
-
-        public void ReadMessageArray<T>(ICollection<T> list, MessageParser<T> messageParser)
-            where T : IMessage<T>
-        {
-            uint fieldTag = lastTag;
-            do
-            {
-                T message = messageParser.CreateTemplate();
-                ReadMessage(message);
-                list.Add(message);
-            } while (MaybeConsumeTag(fieldTag));
-        }
         #endregion
         #endregion
 
 
         #region Underlying reading primitives
         #region Underlying reading primitives
@@ -793,12 +608,12 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read a raw Varint from the stream.  If larger than 32 bits, discard the upper bits.
+        /// Reads a raw Varint from the stream.  If larger than 32 bits, discard the upper bits.
         /// This method is optimised for the case where we've got lots of data in the buffer.
         /// This method is optimised for the case where we've got lots of data in the buffer.
         /// That means we can check the size just once, then just read directly from the buffer
         /// That means we can check the size just once, then just read directly from the buffer
         /// without constant rechecking of the buffer length.
         /// without constant rechecking of the buffer length.
         /// </summary>
         /// </summary>
-        public uint ReadRawVarint32()
+        internal uint ReadRawVarint32()
         {
         {
             if (bufferPos + 5 > bufferSize)
             if (bufferPos + 5 > bufferSize)
             {
             {
@@ -857,13 +672,13 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Reads a varint from the input one byte at a time, so that it does not
         /// Reads a varint from the input one byte at a time, so that it does not
         /// read any bytes after the end of the varint. If you simply wrapped the
         /// read any bytes after the end of the varint. If you simply wrapped the
-        /// stream in a CodedInputStream and used ReadRawVarint32(Stream)}
+        /// stream in a CodedInputStream and used ReadRawVarint32(Stream)
         /// then you would probably end up reading past the end of the varint since
         /// then you would probably end up reading past the end of the varint since
         /// CodedInputStream buffers its input.
         /// CodedInputStream buffers its input.
         /// </summary>
         /// </summary>
         /// <param name="input"></param>
         /// <param name="input"></param>
         /// <returns></returns>
         /// <returns></returns>
-        public static uint ReadRawVarint32(Stream input)
+        internal static uint ReadRawVarint32(Stream input)
         {
         {
             int result = 0;
             int result = 0;
             int offset = 0;
             int offset = 0;
@@ -897,9 +712,9 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read a raw varint from the stream.
+        /// Reads a raw varint from the stream.
         /// </summary>
         /// </summary>
-        public ulong ReadRawVarint64()
+        internal ulong ReadRawVarint64()
         {
         {
             int shift = 0;
             int shift = 0;
             ulong result = 0;
             ulong result = 0;
@@ -917,9 +732,9 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read a 32-bit little-endian integer from the stream.
+        /// Reads a 32-bit little-endian integer from the stream.
         /// </summary>
         /// </summary>
-        public uint ReadRawLittleEndian32()
+        internal uint ReadRawLittleEndian32()
         {
         {
             uint b1 = ReadRawByte();
             uint b1 = ReadRawByte();
             uint b2 = ReadRawByte();
             uint b2 = ReadRawByte();
@@ -929,9 +744,9 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read a 64-bit little-endian integer from the stream.
+        /// Reads a 64-bit little-endian integer from the stream.
         /// </summary>
         /// </summary>
-        public ulong ReadRawLittleEndian64()
+        internal ulong ReadRawLittleEndian64()
         {
         {
             ulong b1 = ReadRawByte();
             ulong b1 = ReadRawByte();
             ulong b2 = ReadRawByte();
             ulong b2 = ReadRawByte();
@@ -945,8 +760,6 @@ namespace Google.Protobuf
                    | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
                    | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
         }
         }
 
 
-        #endregion
-
         /// <summary>
         /// <summary>
         /// Decode a 32-bit value with ZigZag encoding.
         /// Decode a 32-bit value with ZigZag encoding.
         /// </summary>
         /// </summary>
@@ -956,9 +769,9 @@ namespace Google.Protobuf
         /// sign-extended to 64 bits to be varint encoded, thus always taking
         /// sign-extended to 64 bits to be varint encoded, thus always taking
         /// 10 bytes on the wire.)
         /// 10 bytes on the wire.)
         /// </remarks>
         /// </remarks>
-        public static int DecodeZigZag32(uint n)
+        internal static int DecodeZigZag32(uint n)
         {
         {
-            return (int) (n >> 1) ^ -(int) (n & 1);
+            return (int)(n >> 1) ^ -(int)(n & 1);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -970,72 +783,21 @@ namespace Google.Protobuf
         /// sign-extended to 64 bits to be varint encoded, thus always taking
         /// sign-extended to 64 bits to be varint encoded, thus always taking
         /// 10 bytes on the wire.)
         /// 10 bytes on the wire.)
         /// </remarks>
         /// </remarks>
-        public static long DecodeZigZag64(ulong n)
+        internal static long DecodeZigZag64(ulong n)
         {
         {
-            return (long) (n >> 1) ^ -(long) (n & 1);
-        }
-
-        /// <summary>
-        /// Set the maximum message recursion depth.
-        /// </summary>
-        /// <remarks>
-        /// In order to prevent malicious
-        /// messages from causing stack overflows, CodedInputStream limits
-        /// how deeply messages may be nested.  The default limit is 64.
-        /// </remarks>
-        public int SetRecursionLimit(int limit)
-        {
-            if (limit < 0)
-            {
-                throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
-            }
-            int oldLimit = recursionLimit;
-            recursionLimit = limit;
-            return oldLimit;
-        }
-
-        /// <summary>
-        /// Set the maximum message size.
-        /// </summary>
-        /// <remarks>
-        /// In order to prevent malicious messages from exhausting memory or
-        /// causing integer overflows, CodedInputStream limits how large a message may be.
-        /// The default limit is 64MB.  You should set this limit as small
-        /// as you can without harming your app's functionality.  Note that
-        /// size limits only apply when reading from an InputStream, not
-        /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
-        /// If you want to read several messages from a single CodedInputStream, you
-        /// can call ResetSizeCounter() after each message to avoid hitting the
-        /// size limit.
-        /// </remarks>
-        public int SetSizeLimit(int limit)
-        {
-            if (limit < 0)
-            {
-                throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
-            }
-            int oldLimit = sizeLimit;
-            sizeLimit = limit;
-            return oldLimit;
+            return (long)(n >> 1) ^ -(long)(n & 1);
         }
         }
+        #endregion
 
 
         #region Internal reading and buffer management
         #region Internal reading and buffer management
 
 
-        /// <summary>
-        /// Resets the current size counter to zero (see SetSizeLimit).
-        /// </summary>
-        public void ResetSizeCounter()
-        {
-            totalBytesRetired = 0;
-        }
-
         /// <summary>
         /// <summary>
         /// Sets currentLimit to (current position) + byteLimit. This is called
         /// Sets currentLimit to (current position) + byteLimit. This is called
         /// when descending into a length-delimited embedded message. The previous
         /// when descending into a length-delimited embedded message. The previous
         /// limit is returned.
         /// limit is returned.
         /// </summary>
         /// </summary>
         /// <returns>The old limit.</returns>
         /// <returns>The old limit.</returns>
-        public int PushLimit(int byteLimit)
+        internal int PushLimit(int byteLimit)
         {
         {
             if (byteLimit < 0)
             if (byteLimit < 0)
             {
             {
@@ -1073,7 +835,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Discards the current limit, returning the previous limit.
         /// Discards the current limit, returning the previous limit.
         /// </summary>
         /// </summary>
-        public void PopLimit(int oldLimit)
+        internal void PopLimit(int oldLimit)
         {
         {
             currentLimit = oldLimit;
             currentLimit = oldLimit;
             RecomputeBufferSizeAfterLimit();
             RecomputeBufferSizeAfterLimit();
@@ -1083,7 +845,7 @@ namespace Google.Protobuf
         /// Returns whether or not all the data before the limit has been read.
         /// Returns whether or not all the data before the limit has been read.
         /// </summary>
         /// </summary>
         /// <returns></returns>
         /// <returns></returns>
-        public bool ReachedLimit
+        internal bool ReachedLimit
         {
         {
             get
             get
             {
             {
@@ -1173,7 +935,7 @@ namespace Google.Protobuf
         /// <exception cref="InvalidProtocolBufferException">
         /// <exception cref="InvalidProtocolBufferException">
         /// the end of the stream or the current limit was reached
         /// the end of the stream or the current limit was reached
         /// </exception>
         /// </exception>
-        public byte ReadRawByte()
+        internal byte ReadRawByte()
         {
         {
             if (bufferPos == bufferSize)
             if (bufferPos == bufferSize)
             {
             {
@@ -1183,12 +945,12 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Read a fixed size of bytes from the input.
+        /// Reads a fixed size of bytes from the input.
         /// </summary>
         /// </summary>
         /// <exception cref="InvalidProtocolBufferException">
         /// <exception cref="InvalidProtocolBufferException">
         /// the end of the stream or the current limit was reached
         /// the end of the stream or the current limit was reached
         /// </exception>
         /// </exception>
-        public byte[] ReadRawBytes(int size)
+        internal byte[] ReadRawBytes(int size)
         {
         {
             if (size < 0)
             if (size < 0)
             {
             {
@@ -1197,7 +959,8 @@ namespace Google.Protobuf
 
 
             if (totalBytesRetired + bufferPos + size > currentLimit)
             if (totalBytesRetired + bufferPos + size > currentLimit)
             {
             {
-                // Read to the end of the stream anyway.
+                // Read to the end of the stream (up to the current limit) anyway.
+                // TODO(jonskeet): This is the only usage of SkipRawBytes. Do we really need to do it?
                 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
                 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
                 // Then fail.
                 // Then fail.
                 throw InvalidProtocolBufferException.TruncatedMessage();
                 throw InvalidProtocolBufferException.TruncatedMessage();
@@ -1301,63 +1064,12 @@ namespace Google.Protobuf
             }
             }
         }
         }
 
 
-        /// <summary>
-        /// Reads and discards a single field, given its tag value.
-        /// </summary>
-        /// <returns>false if the tag is an end-group tag, in which case
-        /// nothing is skipped. Otherwise, returns true.</returns>
-        public bool SkipField()
-        {
-            uint tag = lastTag;
-            switch (WireFormat.GetTagWireType(tag))
-            {
-                case WireFormat.WireType.Varint:
-                    ReadRawVarint64();
-                    return true;
-                case WireFormat.WireType.Fixed64:
-                    ReadRawLittleEndian64();
-                    return true;
-                case WireFormat.WireType.LengthDelimited:
-                    SkipRawBytes((int) ReadRawVarint32());
-                    return true;
-                case WireFormat.WireType.StartGroup:
-                    SkipMessage();
-                    CheckLastTagWas(
-                        WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag),
-                                           WireFormat.WireType.EndGroup));
-                    return true;
-                case WireFormat.WireType.EndGroup:
-                    return false;
-                case WireFormat.WireType.Fixed32:
-                    ReadRawLittleEndian32();
-                    return true;
-                default:
-                    throw InvalidProtocolBufferException.InvalidWireType();
-            }
-        }
-
-        /// <summary>
-        /// Reads and discards an entire message.  This will read either until EOF
-        /// or until an endgroup tag, whichever comes first.
-        /// </summary>
-        public void SkipMessage()
-        {
-            uint tag;
-            while (ReadTag(out tag))
-            {
-                if (!SkipField())
-                {
-                    return;
-                }
-            }
-        }
-
         /// <summary>
         /// <summary>
         /// Reads and discards <paramref name="size"/> bytes.
         /// Reads and discards <paramref name="size"/> bytes.
         /// </summary>
         /// </summary>
         /// <exception cref="InvalidProtocolBufferException">the end of the stream
         /// <exception cref="InvalidProtocolBufferException">the end of the stream
         /// or the current limit was reached</exception>
         /// or the current limit was reached</exception>
-        public void SkipRawBytes(int size)
+        private void SkipRawBytes(int size)
         {
         {
             if (size < 0)
             if (size < 0)
             {
             {

+ 30 - 21
csharp/src/ProtocolBuffers/CodedOutputStream.ComputeSize.cs

@@ -50,7 +50,7 @@ namespace Google.Protobuf
         private const int LittleEndian32Size = 4;        
         private const int LittleEndian32Size = 4;        
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a
+        /// Computes the number of bytes that would be needed to encode a
         /// double field, including the tag.
         /// double field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeDoubleSize(double value)
         public static int ComputeDoubleSize(double value)
@@ -59,7 +59,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a
+        /// Computes the number of bytes that would be needed to encode a
         /// float field, including the tag.
         /// float field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeFloatSize(float value)
         public static int ComputeFloatSize(float value)
@@ -68,7 +68,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a
+        /// Computes the number of bytes that would be needed to encode a
         /// uint64 field, including the tag.
         /// uint64 field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeUInt64Size(ulong value)
         public static int ComputeUInt64Size(ulong value)
@@ -77,7 +77,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode an
+        /// Computes the number of bytes that would be needed to encode an
         /// int64 field, including the tag.
         /// int64 field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeInt64Size(long value)
         public static int ComputeInt64Size(long value)
@@ -86,7 +86,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode an
+        /// Computes the number of bytes that would be needed to encode an
         /// int32 field, including the tag.
         /// int32 field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeInt32Size(int value)
         public static int ComputeInt32Size(int value)
@@ -103,7 +103,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a
+        /// Computes the number of bytes that would be needed to encode a
         /// fixed64 field, including the tag.
         /// fixed64 field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeFixed64Size(ulong value)
         public static int ComputeFixed64Size(ulong value)
@@ -112,7 +112,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a
+        /// Computes the number of bytes that would be needed to encode a
         /// fixed32 field, including the tag.
         /// fixed32 field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeFixed32Size(uint value)
         public static int ComputeFixed32Size(uint value)
@@ -121,7 +121,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a
+        /// Computes the number of bytes that would be needed to encode a
         /// bool field, including the tag.
         /// bool field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeBoolSize(bool value)
         public static int ComputeBoolSize(bool value)
@@ -130,7 +130,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a
+        /// Computes the number of bytes that would be needed to encode a
         /// string field, including the tag.
         /// string field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeStringSize(String value)
         public static int ComputeStringSize(String value)
@@ -141,7 +141,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a
+        /// Computes the number of bytes that would be needed to encode a
         /// group field, including the tag.
         /// group field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeGroupSize(IMessage value)
         public static int ComputeGroupSize(IMessage value)
@@ -150,7 +150,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode an
+        /// Computes the number of bytes that would be needed to encode an
         /// embedded message field, including the tag.
         /// embedded message field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeMessageSize(IMessage value)
         public static int ComputeMessageSize(IMessage value)
@@ -160,7 +160,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a
+        /// Computes the number of bytes that would be needed to encode a
         /// bytes field, including the tag.
         /// bytes field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeBytesSize(ByteString value)
         public static int ComputeBytesSize(ByteString value)
@@ -170,7 +170,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a
+        /// Computes the number of bytes that would be needed to encode a
         /// uint32 field, including the tag.
         /// uint32 field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeUInt32Size(uint value)
         public static int ComputeUInt32Size(uint value)
@@ -179,7 +179,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a
+        /// Computes the number of bytes that would be needed to encode a
         /// enum field, including the tag. The caller is responsible for
         /// enum field, including the tag. The caller is responsible for
         /// converting the enum value to its numeric value.
         /// converting the enum value to its numeric value.
         /// </summary>
         /// </summary>
@@ -190,7 +190,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode an
+        /// Computes the number of bytes that would be needed to encode an
         /// sfixed32 field, including the tag.
         /// sfixed32 field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeSFixed32Size(int value)
         public static int ComputeSFixed32Size(int value)
@@ -199,7 +199,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode an
+        /// Computes the number of bytes that would be needed to encode an
         /// sfixed64 field, including the tag.
         /// sfixed64 field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeSFixed64Size(long value)
         public static int ComputeSFixed64Size(long value)
@@ -208,7 +208,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode an
+        /// Computes the number of bytes that would be needed to encode an
         /// sint32 field, including the tag.
         /// sint32 field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeSInt32Size(int value)
         public static int ComputeSInt32Size(int value)
@@ -217,7 +217,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode an
+        /// Computes the number of bytes that would be needed to encode an
         /// sint64 field, including the tag.
         /// sint64 field, including the tag.
         /// </summary>
         /// </summary>
         public static int ComputeSInt64Size(long value)
         public static int ComputeSInt64Size(long value)
@@ -226,7 +226,16 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a varint.
+        /// Computes the number of bytes that would be needed to encode a length,
+        /// as written by <see cref="WriteLength"/>.
+        /// </summary>
+        public static int ComputeLengthSize(int length)
+        {
+            return ComputeRawVarint32Size((uint) length);
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a varint.
         /// </summary>
         /// </summary>
         public static int ComputeRawVarint32Size(uint value)
         public static int ComputeRawVarint32Size(uint value)
         {
         {
@@ -250,7 +259,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a varint.
+        /// Computes the number of bytes that would be needed to encode a varint.
         /// </summary>
         /// </summary>
         public static int ComputeRawVarint64Size(ulong value)
         public static int ComputeRawVarint64Size(ulong value)
         {
         {
@@ -294,7 +303,7 @@ namespace Google.Protobuf
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Compute the number of bytes that would be needed to encode a tag.
+        /// Computes the number of bytes that would be needed to encode a tag.
         /// </summary>
         /// </summary>
         public static int ComputeTagSize(int fieldNumber)
         public static int ComputeTagSize(int fieldNumber)
         {
         {

+ 80 - 336
csharp/src/ProtocolBuffers/CodedOutputStream.cs

@@ -37,7 +37,6 @@
 using System;
 using System;
 using System.IO;
 using System.IO;
 using System.Text;
 using System.Text;
-using Google.Protobuf.Collections;
 
 
 namespace Google.Protobuf
 namespace Google.Protobuf
 {
 {
@@ -141,11 +140,12 @@ namespace Google.Protobuf
             }
             }
         }
         }
 
 
-        #region Writing of values without tags
+        #region Writing of values (not including tags)
 
 
         /// <summary>
         /// <summary>
-        /// Writes a double field value, including tag, to the stream.
+        /// Writes a double field value, without a tag, to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteDouble(double value)
         public void WriteDouble(double value)
         {
         {
             WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
             WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
@@ -154,6 +154,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes a float field value, without a tag, to the stream.
         /// Writes a float field value, without a tag, to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteFloat(float value)
         public void WriteFloat(float value)
         {
         {
             byte[] rawBytes = BitConverter.GetBytes(value);
             byte[] rawBytes = BitConverter.GetBytes(value);
@@ -178,6 +179,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes a uint64 field value, without a tag, to the stream.
         /// Writes a uint64 field value, without a tag, to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteUInt64(ulong value)
         public void WriteUInt64(ulong value)
         {
         {
             WriteRawVarint64(value);
             WriteRawVarint64(value);
@@ -186,6 +188,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes an int64 field value, without a tag, to the stream.
         /// Writes an int64 field value, without a tag, to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteInt64(long value)
         public void WriteInt64(long value)
         {
         {
             WriteRawVarint64((ulong) value);
             WriteRawVarint64((ulong) value);
@@ -194,6 +197,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes an int32 field value, without a tag, to the stream.
         /// Writes an int32 field value, without a tag, to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteInt32(int value)
         public void WriteInt32(int value)
         {
         {
             if (value >= 0)
             if (value >= 0)
@@ -210,6 +214,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes a fixed64 field value, without a tag, to the stream.
         /// Writes a fixed64 field value, without a tag, to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteFixed64(ulong value)
         public void WriteFixed64(ulong value)
         {
         {
             WriteRawLittleEndian64(value);
             WriteRawLittleEndian64(value);
@@ -218,6 +223,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes a fixed32 field value, without a tag, to the stream.
         /// Writes a fixed32 field value, without a tag, to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteFixed32(uint value)
         public void WriteFixed32(uint value)
         {
         {
             WriteRawLittleEndian32(value);
             WriteRawLittleEndian32(value);
@@ -226,6 +232,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes a bool field value, without a tag, to the stream.
         /// Writes a bool field value, without a tag, to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteBool(bool value)
         public void WriteBool(bool value)
         {
         {
             WriteRawByte(value ? (byte) 1 : (byte) 0);
             WriteRawByte(value ? (byte) 1 : (byte) 0);
@@ -233,13 +240,15 @@ namespace Google.Protobuf
 
 
         /// <summary>
         /// <summary>
         /// Writes a string field value, without a tag, to the stream.
         /// Writes a string field value, without a tag, to the stream.
+        /// The data is length-prefixed.
         /// </summary>
         /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteString(string value)
         public void WriteString(string value)
         {
         {
             // Optimise the case where we have enough space to write
             // Optimise the case where we have enough space to write
             // the string directly to the buffer, which should be common.
             // the string directly to the buffer, which should be common.
             int length = Utf8Encoding.GetByteCount(value);
             int length = Utf8Encoding.GetByteCount(value);
-            WriteRawVarint32((uint)length);
+            WriteLength(length);
             if (limit - position >= length)
             if (limit - position >= length)
             {
             {
                 if (length == value.Length) // Must be all ASCII...
                 if (length == value.Length) // Must be all ASCII...
@@ -262,23 +271,41 @@ namespace Google.Protobuf
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// Writes a message, without a tag, to the stream.
+        /// The data is length-prefixed.
+        /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteMessage(IMessage value)
         public void WriteMessage(IMessage value)
         {
         {
             WriteRawVarint32((uint) value.CalculateSize());
             WriteRawVarint32((uint) value.CalculateSize());
             value.WriteTo(this);
             value.WriteTo(this);
         }
         }
 
 
+        /// <summary>
+        /// Write a byte string, without a tag, to the stream.
+        /// The data is length-prefixed.
+        /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteBytes(ByteString value)
         public void WriteBytes(ByteString value)
         {
         {
             WriteRawVarint32((uint) value.Length);
             WriteRawVarint32((uint) value.Length);
             value.WriteRawBytesTo(this);
             value.WriteRawBytesTo(this);
         }
         }
 
 
+        /// <summary>
+        /// Writes a uint32 value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteUInt32(uint value)
         public void WriteUInt32(uint value)
         {
         {
             WriteRawVarint32(value);
             WriteRawVarint32(value);
         }
         }
 
 
+        /// <summary>
+        /// Writes an enum value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteEnum(int value)
         public void WriteEnum(int value)
         {
         {
             WriteInt32(value);
             WriteInt32(value);
@@ -289,179 +316,43 @@ namespace Google.Protobuf
             WriteRawLittleEndian32((uint) value);
             WriteRawLittleEndian32((uint) value);
         }
         }
 
 
+        /// <summary>
+        /// Writes an sfixed64 value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteSFixed64(long value)
         public void WriteSFixed64(long value)
         {
         {
             WriteRawLittleEndian64((ulong) value);
             WriteRawLittleEndian64((ulong) value);
         }
         }
 
 
+        /// <summary>
+        /// Writes an sint32 value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteSInt32(int value)
         public void WriteSInt32(int value)
         {
         {
             WriteRawVarint32(EncodeZigZag32(value));
             WriteRawVarint32(EncodeZigZag32(value));
         }
         }
 
 
+        /// <summary>
+        /// Writes an sint64 value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
         public void WriteSInt64(long value)
         public void WriteSInt64(long value)
         {
         {
             WriteRawVarint64(EncodeZigZag64(value));
             WriteRawVarint64(EncodeZigZag64(value));
         }
         }
 
 
-        #endregion
-
-        #region Write array members, with fields.
-        public void WriteMessageArray<T>(int fieldNumber, RepeatedField<T> list)
-            where T : IMessage
-        {
-            foreach (T value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
-                WriteMessage(value);
-            }
-        }
-
-        public void WriteStringArray(int fieldNumber, RepeatedField<string> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
-                WriteString(value);
-            }
-        }
-
-        public void WriteBytesArray(int fieldNumber, RepeatedField<ByteString> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
-                WriteBytes(value);
-            }
-        }
-
-        public void WriteBoolArray(int fieldNumber, RepeatedField<bool> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Varint);
-                WriteBool(value);
-            }
-        }
-
-        public void WriteInt32Array(int fieldNumber, RepeatedField<int> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Varint);
-                WriteInt32(value);
-            }
-        }
-
-        public void WriteSInt32Array(int fieldNumber, RepeatedField<int> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Varint);
-                WriteSInt32(value);
-            }
-        }
-
-        public void WriteUInt32Array(int fieldNumber, RepeatedField<uint> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Varint);
-                WriteUInt32(value);
-            }
-        }
-
-        public void WriteFixed32Array(int fieldNumber, RepeatedField<uint> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
-                WriteFixed32(value);
-            }
-        }
-
-        public void WriteSFixed32Array(int fieldNumber, RepeatedField<int> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
-                WriteSFixed32(value);
-            }
-        }
-
-        public void WriteInt64Array(int fieldNumber, RepeatedField<long> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
-                WriteInt64(value);
-            }
-        }
-
-        public void WriteSInt64Array(int fieldNumber, RepeatedField<long> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Varint);
-                WriteSInt64(value);
-            }
-        }
-
-        public void WriteUInt64Array(int fieldNumber, RepeatedField<ulong> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Varint);
-                WriteUInt64(value);
-            }
-        }
-
-        public void WriteFixed64Array(int fieldNumber, RepeatedField<ulong> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
-                WriteFixed64(value);
-            }
-        }
-
-        public void WriteSFixed64Array(int fieldNumber, RepeatedField<long> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
-                WriteSFixed64(value);
-            }
-        }
-
-        public void WriteDoubleArray(int fieldNumber, RepeatedField<double> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
-                WriteDouble(value);
-            }
-        }
-
-        public void WriteFloatArray(int fieldNumber, RepeatedField<float> list)
-        {
-            foreach (var value in list)
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
-                WriteFloat(value);
-            }
-        }
-
-        public void WriteEnumArray<T>(int fieldNumber, RepeatedField<T> list)
-            where T : struct, IComparable, IFormattable
+        /// <summary>
+        /// Writes a length (in bytes) for length-delimited data.
+        /// </summary>
+        /// <remarks>
+        /// This method simply writes a rawint, but exists for clarity in calling code.
+        /// </remarks>
+        /// <param name="length">Length value, in bytes.</param>
+        public void WriteLength(int length)
         {
         {
-            // Bit of a hack, to access the values as ints
-            var iterator = list.GetInt32Enumerator();
-            while (iterator.MoveNext())
-            {
-                WriteTag(fieldNumber, WireFormat.WireType.Varint);
-                WriteEnum(iterator.Current);
-            }
+            WriteRawVarint32((uint) length);
         }
         }
 
 
         #endregion
         #endregion
@@ -470,6 +361,8 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Encodes and writes a tag.
         /// Encodes and writes a tag.
         /// </summary>
         /// </summary>
+        /// <param name="fieldNumber">The number of the field to write the tag for</param>
+        /// <param name="type">The wire format type of the tag to write</param>
         public void WriteTag(int fieldNumber, WireFormat.WireType type)
         public void WriteTag(int fieldNumber, WireFormat.WireType type)
         {
         {
             WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
             WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
@@ -478,6 +371,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes an already-encoded tag.
         /// Writes an already-encoded tag.
         /// </summary>
         /// </summary>
+        /// <param name="tag">The encoded tag</param>
         public void WriteTag(uint tag)
         public void WriteTag(uint tag)
         {
         {
             WriteRawVarint32(tag);
             WriteRawVarint32(tag);
@@ -486,6 +380,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes the given single-byte tag directly to the stream.
         /// Writes the given single-byte tag directly to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="b1">The encoded tag</param>
         public void WriteRawTag(byte b1)
         public void WriteRawTag(byte b1)
         {
         {
             WriteRawByte(b1);
             WriteRawByte(b1);
@@ -494,6 +389,8 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes the given two-byte tag directly to the stream.
         /// Writes the given two-byte tag directly to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="b1">The first byte of the encoded tag</param>
+        /// <param name="b2">The second byte of the encoded tag</param>
         public void WriteRawTag(byte b1, byte b2)
         public void WriteRawTag(byte b1, byte b2)
         {
         {
             WriteRawByte(b1);
             WriteRawByte(b1);
@@ -503,6 +400,9 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes the given three-byte tag directly to the stream.
         /// Writes the given three-byte tag directly to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="b1">The first byte of the encoded tag</param>
+        /// <param name="b2">The second byte of the encoded tag</param>
+        /// <param name="b3">The third byte of the encoded tag</param>
         public void WriteRawTag(byte b1, byte b2, byte b3)
         public void WriteRawTag(byte b1, byte b2, byte b3)
         {
         {
             WriteRawByte(b1);
             WriteRawByte(b1);
@@ -513,6 +413,10 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes the given four-byte tag directly to the stream.
         /// Writes the given four-byte tag directly to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="b1">The first byte of the encoded tag</param>
+        /// <param name="b2">The second byte of the encoded tag</param>
+        /// <param name="b3">The third byte of the encoded tag</param>
+        /// <param name="b4">The fourth byte of the encoded tag</param>
         public void WriteRawTag(byte b1, byte b2, byte b3, byte b4)
         public void WriteRawTag(byte b1, byte b2, byte b3, byte b4)
         {
         {
             WriteRawByte(b1);
             WriteRawByte(b1);
@@ -524,6 +428,11 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes the given five-byte tag directly to the stream.
         /// Writes the given five-byte tag directly to the stream.
         /// </summary>
         /// </summary>
+        /// <param name="b1">The first byte of the encoded tag</param>
+        /// <param name="b2">The second byte of the encoded tag</param>
+        /// <param name="b3">The third byte of the encoded tag</param>
+        /// <param name="b4">The fourth byte of the encoded tag</param>
+        /// <param name="b5">The fifth byte of the encoded tag</param>
         public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)
         public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)
         {
         {
             WriteRawByte(b1);
             WriteRawByte(b1);
@@ -534,178 +443,13 @@ namespace Google.Protobuf
         }
         }
         #endregion
         #endregion
 
 
-        #region Write packed array members
-        // TODO(jonskeet): A lot of these are really inefficient, due to method group conversions. Fix!
-        // (Alternatively, add extension methods to RepeatedField, accepting the Write* methods via delegates too.)
-        public void WritePackedBoolArray(RepeatedField<bool> list)
-        {
-            uint size = (uint)list.Count;
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteBool(value);
-            }
-        }
-
-        public void WritePackedInt32Array(RepeatedField<int> list)
-        {
-            uint size = list.CalculateSize(ComputeInt32Size);
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteInt32(value);
-            }
-        }
-
-        public void WritePackedSInt32Array(RepeatedField<int> list)
-        {
-            uint size = list.CalculateSize(ComputeSInt32Size);
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteSInt32(value);
-            }
-        }
-
-        public void WritePackedUInt32Array(RepeatedField<uint> list)
-        {
-            uint size = list.CalculateSize(ComputeUInt32Size);
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteUInt32(value);
-            }
-        }
-
-        public void WritePackedFixed32Array(RepeatedField<uint> list)
-        {
-            uint size = (uint) list.Count * 4;
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteFixed32(value);
-            }
-        }
-
-        public void WritePackedSFixed32Array(RepeatedField<int> list)
-        {
-            uint size = (uint) list.Count * 4;
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteSFixed32(value);
-            }
-        }
-
-        public void WritePackedInt64Array(RepeatedField<long> list)
-        {
-            uint size = list.CalculateSize(ComputeInt64Size);
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteInt64(value);
-            }
-        }
-
-        public void WritePackedSInt64Array(RepeatedField<long> list)
-        {
-            uint size = list.CalculateSize(ComputeSInt64Size);
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteSInt64(value);
-            }
-        }
-
-        public void WritePackedUInt64Array(RepeatedField<ulong> list)
-        {
-            if (list.Count == 0)
-            {
-                return;
-            }
-            uint size = list.CalculateSize(ComputeUInt64Size);
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteUInt64(value);
-            }
-        }
-
-        public void WritePackedFixed64Array(RepeatedField<ulong> list)
-        {
-            uint size = (uint) list.Count * 8;
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteFixed64(value);
-            }
-        }
-
-        public void WritePackedSFixed64Array(RepeatedField<long> list)
-        {
-            uint size = (uint) list.Count * 8;
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteSFixed64(value);
-            }
-        }
-
-        public void WritePackedDoubleArray(RepeatedField<double> list)
-        {
-            uint size = (uint) list.Count * 8;
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteDouble(value);
-            }
-        }
-
-        public void WritePackedFloatArray(RepeatedField<float> list)
-        {
-            if (list.Count == 0)
-            {
-                return;
-            }
-            uint size = (uint) list.Count * 4;
-            WriteRawVarint32(size);
-            foreach (var value in list)
-            {
-                WriteFloat(value);
-            }
-        }
-
-        public void WritePackedEnumArray<T>(RepeatedField<T> list)
-            where T : struct, IComparable, IFormattable
-        {
-            if (list.Count == 0)
-            {
-                return;
-            }
-            // Bit of a hack, to access the values as ints
-            var iterator = list.GetInt32Enumerator();
-            uint size = 0;
-            while (iterator.MoveNext())
-            {
-                size += (uint) ComputeEnumSize(iterator.Current);
-            }
-            iterator.Reset();
-            WriteRawVarint32(size);
-            while (iterator.MoveNext())
-            {
-                WriteEnum(iterator.Current);
-            }
-        }
-
-        #endregion
-
         #region Underlying writing primitives
         #region Underlying writing primitives
         /// <summary>
         /// <summary>
         /// Writes a 32 bit value as a varint. The fast route is taken when
         /// Writes a 32 bit value as a varint. The fast route is taken when
         /// there's enough buffer space left to whizz through without checking
         /// there's enough buffer space left to whizz through without checking
         /// for each byte; otherwise, we resort to calling WriteRawByte each time.
         /// for each byte; otherwise, we resort to calling WriteRawByte each time.
         /// </summary>
         /// </summary>
-        public void WriteRawVarint32(uint value)
+        internal void WriteRawVarint32(uint value)
         {
         {
             // Optimize for the common case of a single byte value
             // Optimize for the common case of a single byte value
             if (value < 128 && position < limit)
             if (value < 128 && position < limit)
@@ -734,7 +478,7 @@ namespace Google.Protobuf
             }
             }
         }
         }
 
 
-        public void WriteRawVarint64(ulong value)
+        internal void WriteRawVarint64(ulong value)
         {
         {
             while (value > 127 && position < limit)
             while (value > 127 && position < limit)
             {
             {
@@ -756,7 +500,7 @@ namespace Google.Protobuf
             }
             }
         }
         }
 
 
-        public void WriteRawLittleEndian32(uint value)
+        internal void WriteRawLittleEndian32(uint value)
         {
         {
             if (position + 4 > limit)
             if (position + 4 > limit)
             {
             {
@@ -774,7 +518,7 @@ namespace Google.Protobuf
             }
             }
         }
         }
 
 
-        public void WriteRawLittleEndian64(ulong value)
+        internal void WriteRawLittleEndian64(ulong value)
         {
         {
             if (position + 8 > limit)
             if (position + 8 > limit)
             {
             {
@@ -800,7 +544,7 @@ namespace Google.Protobuf
             }
             }
         }
         }
 
 
-        public void WriteRawByte(byte value)
+        internal void WriteRawByte(byte value)
         {
         {
             if (position == limit)
             if (position == limit)
             {
             {
@@ -810,7 +554,7 @@ namespace Google.Protobuf
             buffer[position++] = value;
             buffer[position++] = value;
         }
         }
 
 
-        public void WriteRawByte(uint value)
+        internal void WriteRawByte(uint value)
         {
         {
             WriteRawByte((byte) value);
             WriteRawByte((byte) value);
         }
         }
@@ -818,7 +562,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes out an array of bytes.
         /// Writes out an array of bytes.
         /// </summary>
         /// </summary>
-        public void WriteRawBytes(byte[] value)
+        internal void WriteRawBytes(byte[] value)
         {
         {
             WriteRawBytes(value, 0, value.Length);
             WriteRawBytes(value, 0, value.Length);
         }
         }
@@ -826,7 +570,7 @@ namespace Google.Protobuf
         /// <summary>
         /// <summary>
         /// Writes out part of an array of bytes.
         /// Writes out part of an array of bytes.
         /// </summary>
         /// </summary>
-        public void WriteRawBytes(byte[] value, int offset, int length)
+        internal void WriteRawBytes(byte[] value, int offset, int length)
         {
         {
             if (limit - position >= length)
             if (limit - position >= length)
             {
             {
@@ -873,7 +617,7 @@ namespace Google.Protobuf
         /// sign-extended to 64 bits to be varint encoded, thus always taking
         /// sign-extended to 64 bits to be varint encoded, thus always taking
         /// 10 bytes on the wire.)
         /// 10 bytes on the wire.)
         /// </remarks>
         /// </remarks>
-        public static uint EncodeZigZag32(int n)
+        internal static uint EncodeZigZag32(int n)
         {
         {
             // Note:  the right-shift must be arithmetic
             // Note:  the right-shift must be arithmetic
             return (uint) ((n << 1) ^ (n >> 31));
             return (uint) ((n << 1) ^ (n >> 31));
@@ -888,7 +632,7 @@ namespace Google.Protobuf
         /// sign-extended to 64 bits to be varint encoded, thus always taking
         /// sign-extended to 64 bits to be varint encoded, thus always taking
         /// 10 bytes on the wire.)
         /// 10 bytes on the wire.)
         /// </remarks>
         /// </remarks>
-        public static ulong EncodeZigZag64(long n)
+        internal static ulong EncodeZigZag64(long n)
         {
         {
             return (ulong) ((n << 1) ^ (n >> 63));
             return (ulong) ((n << 1) ^ (n >> 63));
         }
         }

+ 7 - 3
csharp/src/ProtocolBuffers/Collections/MapField.cs

@@ -334,6 +334,10 @@ namespace Google.Protobuf.Collections
 
 
         public int CalculateSize(Codec codec)
         public int CalculateSize(Codec codec)
         {
         {
+            if (Count == 0)
+            {
+                return 0;
+            }
             var message = new Codec.MessageAdapter(codec);
             var message = new Codec.MessageAdapter(codec);
             int size = 0;
             int size = 0;
             foreach (var entry in list)
             foreach (var entry in list)
@@ -419,13 +423,13 @@ namespace Google.Protobuf.Collections
 
 
                 public void WriteTo(CodedOutputStream output)
                 public void WriteTo(CodedOutputStream output)
                 {
                 {
-                    codec.keyCodec.Write(output, Key);
-                    codec.valueCodec.Write(output, Value);
+                    codec.keyCodec.WriteTagAndValue(output, Key);
+                    codec.valueCodec.WriteTagAndValue(output, Value);
                 }
                 }
 
 
                 public int CalculateSize()
                 public int CalculateSize()
                 {
                 {
-                    return codec.keyCodec.CalculateSize(Key) + codec.valueCodec.CalculateSize(Value);
+                    return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value);
                 }
                 }
             }
             }
         }
         }

+ 1 - 1
csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs

@@ -38,7 +38,7 @@ namespace Google.Protobuf.Collections
     /// <summary>
     /// <summary>
     /// Read-only wrapper around another dictionary.
     /// Read-only wrapper around another dictionary.
     /// </summary>
     /// </summary>
-    public sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
+    internal sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
     {
     {
         private readonly IDictionary<TKey, TValue> wrapped;
         private readonly IDictionary<TKey, TValue> wrapped;
 
 

+ 110 - 75
csharp/src/ProtocolBuffers/Collections/RepeatedField.cs

@@ -49,6 +49,113 @@ namespace Google.Protobuf.Collections
             return clone;
             return clone;
         }
         }
 
 
+        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 and the mutability.
+            uint tag = input.LastTag;
+            var reader = codec.ValueReader;
+            // Value types can be packed or not.
+            if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
+            {
+                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));
+            }
+        }
+
+        public int CalculateSize(FieldCodec<T> codec)
+        {
+            if (count == 0)
+            {
+                return 0;
+            }
+            uint tag = codec.Tag;
+            if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
+            {
+                int dataSize = CalculatePackedDataSize(codec);
+                return CodedOutputStream.ComputeRawVarint32Size(tag) +
+                    CodedOutputStream.ComputeRawVarint32Size((uint)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;
+            }
+        }
+
+        public void WriteTo(CodedOutputStream output, FieldCodec<T> codec)
+        {
+            if (count == 0)
+            {
+                return;
+            }
+            var writer = codec.ValueWriter;
+            var tag = codec.Tag;
+            if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
+            {
+                // 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]);
+                }
+            }
+        }
+
         public bool IsFrozen { get { return frozen; } }
         public bool IsFrozen { get { return frozen; } }
 
 
         public void Freeze()
         public void Freeze()
@@ -66,9 +173,9 @@ namespace Google.Protobuf.Collections
 
 
         private void EnsureSize(int size)
         private void EnsureSize(int size)
         {
         {
-            size = Math.Max(size, MinArraySize);
             if (array.Length < size)
             if (array.Length < size)
             {
             {
+                size = Math.Max(size, MinArraySize);
                 int newSize = Math.Max(array.Length * 2, size);
                 int newSize = Math.Max(array.Length * 2, size);
                 var tmp = new T[newSize];
                 var tmp = new T[newSize];
                 Array.Copy(array, 0, tmp, 0, array.Length);
                 Array.Copy(array, 0, tmp, 0, array.Length);
@@ -87,18 +194,6 @@ namespace Google.Protobuf.Collections
             array[count++] = item;
             array[count++] = item;
         }
         }
 
 
-        /// <summary>
-        /// Hack to allow us to add enums easily... will only work with int-based types.
-        /// </summary>
-        /// <param name="readEnum"></param>
-        internal void AddInt32(int item)
-        {
-            this.CheckMutable();
-            EnsureSize(count + 1);
-            int[] castArray = (int[]) (object) array;
-            castArray[count++] = item;
-        }
-
         public void Clear()
         public void Clear()
         {
         {
             this.CheckMutable();
             this.CheckMutable();
@@ -180,16 +275,7 @@ namespace Google.Protobuf.Collections
         IEnumerator IEnumerable.GetEnumerator()
         IEnumerator IEnumerable.GetEnumerator()
         {
         {
             return GetEnumerator();
             return GetEnumerator();
-        }
-
-        /// <summary>
-        /// Returns an enumerator of the values in this list as integers.
-        /// Used for enum types.
-        /// </summary>
-        internal Int32Enumerator GetInt32Enumerator()
-        {
-            return new Int32Enumerator((int[])(object)array, count);
-        }
+        }        
 
 
         public override int GetHashCode()
         public override int GetHashCode()
         {
         {
@@ -297,17 +383,7 @@ namespace Google.Protobuf.Collections
                 array[index] = value;
                 array[index] = value;
             }
             }
         }
         }
-
-        internal uint CalculateSize(Func<T, int> sizeComputer)
-        {
-            int size = 0;
-            for (int i = 0; i < count; i++)
-            {
-                size += sizeComputer(array[i]);
-            }
-            return (uint)size;
-        }
-
+        
         public struct Enumerator : IEnumerator<T>
         public struct Enumerator : IEnumerator<T>
         {
         {
             private int index;
             private int index;
@@ -355,46 +431,5 @@ namespace Google.Protobuf.Collections
             {
             {
             }
             }
         }
         }
-
-        internal struct Int32Enumerator : IEnumerator<int>
-        {
-            private int index;
-            private readonly int[] array;
-            private readonly int count;
-
-            public Int32Enumerator(int[] array, int count)
-            {
-                this.array = array;
-                this.index = -1;
-                this.count = count;
-            }
-
-            public bool MoveNext()
-            {
-                if (index + 1 >= count)
-                {
-                    return false;
-                }
-                index++;
-                return true;
-            }
-
-            public void Reset()
-            {
-                index = -1;
-            }
-
-            // No guard here, as we're only going to use this internally...
-            public int Current { get { return array[index]; } }
-
-            object IEnumerator.Current
-            {
-                get { return Current; }
-            }
-
-            public void Dispose()
-            {
-            }
-        }
     }
     }
 }
 }

文件差异内容过多而无法显示
+ 147 - 313
csharp/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs


+ 52 - 7
csharp/src/ProtocolBuffers/FieldCodec.cs

@@ -8,6 +8,7 @@ namespace Google.Protobuf
     /// </summary>
     /// </summary>
     public static class FieldCodec
     public static class FieldCodec
     {
     {
+        // TODO: Avoid the "dual hit" of lambda expressions: create open delegates instead. (At least test...)
         public static FieldCodec<string> ForString(uint tag)
         public static FieldCodec<string> ForString(uint tag)
         {
         {
             return new FieldCodec<string>(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag); 
             return new FieldCodec<string>(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag); 
@@ -84,7 +85,7 @@ namespace Google.Protobuf
         }
         }
 
 
         // Enums are tricky. We can probably use expression trees to build these delegates automatically,
         // Enums are tricky. We can probably use expression trees to build these delegates automatically,
-        // but it's easy to generate the code fdor it.
+        // but it's easy to generate the code for it.
         public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32)
         public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32)
         {
         {
             return new FieldCodec<T>(input => fromInt32(
             return new FieldCodec<T>(input => fromInt32(
@@ -145,28 +146,68 @@ namespace Google.Protobuf
 
 
         private readonly Func<CodedInputStream, T> reader;
         private readonly Func<CodedInputStream, T> reader;
         private readonly Action<CodedOutputStream, T> writer;
         private readonly Action<CodedOutputStream, T> writer;
-        private readonly Func<T, int> sizeComputer;
+        private readonly Func<T, int> sizeCalculator;
         private readonly uint tag;
         private readonly uint tag;
         private readonly int tagSize;
         private readonly int tagSize;
+        private readonly int fixedSize;
 
 
         internal FieldCodec(
         internal FieldCodec(
             Func<CodedInputStream, T> reader,
             Func<CodedInputStream, T> reader,
             Action<CodedOutputStream, T> writer,
             Action<CodedOutputStream, T> writer,
-            Func<T, int> sizeComputer,
+            Func<T, int> sizeCalculator,
             uint tag)
             uint tag)
         {
         {
             this.reader = reader;
             this.reader = reader;
             this.writer = writer;
             this.writer = writer;
-            this.sizeComputer = sizeComputer;
+            this.sizeCalculator = sizeCalculator;
+            this.fixedSize = 0;
             this.tag = tag;
             this.tag = tag;
             tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
             tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
         }
         }
 
 
+        internal FieldCodec(
+            Func<CodedInputStream, T> reader,
+            Action<CodedOutputStream, T> writer,
+            int fixedSize,
+            uint tag)
+        {
+            this.reader = reader;
+            this.writer = writer;
+            this.sizeCalculator = _ => fixedSize;
+            this.fixedSize = fixedSize;
+            this.tag = tag;
+            tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
+        }
+
+        /// <summary>
+        /// Returns the size calculator for just a value.
+        /// </summary>
+        internal Func<T, int> ValueSizeCalculator { get { return sizeCalculator; } }
+
+        /// <summary>
+        /// Returns a delegate to write a value (unconditionally) to a coded output stream.
+        /// </summary>
+        internal Action<CodedOutputStream, T> ValueWriter { get { return writer; } }
+
+        /// <summary>
+        /// Returns a delegate to read a value from a coded input stream. It is assumed that
+        /// the stream is already positioned on the appropriate tag.
+        /// </summary>
+        internal Func<CodedInputStream, T> ValueReader { get { return reader; } }
+
+        /// <summary>
+        /// Returns the fixed size for an entry, or 0 if sizes vary.
+        /// </summary>
+        internal int FixedSize { get { return fixedSize; } }
+
         public uint Tag { get { return tag; } }
         public uint Tag { get { return tag; } }
 
 
         public T DefaultValue { get { return Default; } }
         public T DefaultValue { get { return Default; } }
 
 
-        public void Write(CodedOutputStream output, T value)
+        /// <summary>
+        /// Write a tag and the given value, *if* the value is not the default.
+        /// </summary>
+        public void WriteTagAndValue(CodedOutputStream output, T value)
         {
         {
             if (!IsDefault(value))
             if (!IsDefault(value))
             {
             {
@@ -180,9 +221,13 @@ namespace Google.Protobuf
             return reader(input);
             return reader(input);
         }
         }
 
 
-        public int CalculateSize(T value)
+        /// <summary>
+        /// Calculates the size required to write the given value, with a tag,
+        /// if the value is not the default.
+        /// </summary>
+        public int CalculateSizeWithTag(T value)
         {
         {
-            return IsDefault(value) ? 0 : sizeComputer(value) + CodedOutputStream.ComputeRawVarint32Size(tag);
+            return IsDefault(value) ? 0 : sizeCalculator(value) + tagSize;
         }        
         }        
     }
     }
 }
 }

+ 1 - 1
csharp/src/ProtocolBuffers/MessageExtensions.cs

@@ -38,7 +38,7 @@ namespace Google.Protobuf
         {
         {
             ThrowHelper.ThrowIfNull(message, "message");
             ThrowHelper.ThrowIfNull(message, "message");
             ThrowHelper.ThrowIfNull(input, "input");
             ThrowHelper.ThrowIfNull(input, "input");
-            int size = (int)CodedInputStream.ReadRawVarint32(input);
+            int size = (int) CodedInputStream.ReadRawVarint32(input);
             Stream limitedStream = new LimitedInputStream(input, size);
             Stream limitedStream = new LimitedInputStream(input, size);
             message.MergeFrom(limitedStream);
             message.MergeFrom(limitedStream);
         }
         }

+ 7 - 0
csharp/src/ProtocolBuffers/Properties/AssemblyInfo.cs

@@ -61,6 +61,13 @@ using System.Security;
 
 
 [assembly: AssemblyVersion("2.4.1.555")]
 [assembly: AssemblyVersion("2.4.1.555")]
 
 
+[assembly: InternalsVisibleTo("Google.Protobuf.Test, PublicKey=" +
+    "00240000048000009400000006020000002400005253413100040000110000003b4611704c5379" +
+    "39c3e0fbe9447dd6fa5462507f9dd4fd9fbf0712457e415b037da6d2c4eb5d2c7d29c86380af68" +
+    "7cf400401bb183f2a70bd3b631c1fcb7db8aa66c766694a9fb53fa765df6303104da8c978f3b6d" +
+    "53909cd30685b8bc9922c726cd82b5995e9e2cfca6df7a2d189d851492e49f4b76f269ce6dfd08" +
+    "c34a7d98")]
+
 #if !NOFILEVERSION
 #if !NOFILEVERSION
 [assembly: AssemblyFileVersion("2.4.1.555")]
 [assembly: AssemblyFileVersion("2.4.1.555")]
 #endif
 #endif

+ 9 - 27
csharp/src/ProtocolBuffers/WireFormat.cs

@@ -53,13 +53,13 @@ namespace Google.Protobuf
         #region Fixed sizes.
         #region Fixed sizes.
 
 
         // TODO(jonskeet): Move these somewhere else. They're messy. Consider making FieldType a smarter kind of enum
         // TODO(jonskeet): Move these somewhere else. They're messy. Consider making FieldType a smarter kind of enum
-        public const int Fixed32Size = 4;
-        public const int Fixed64Size = 8;
-        public const int SFixed32Size = 4;
-        public const int SFixed64Size = 8;
-        public const int FloatSize = 4;
-        public const int DoubleSize = 8;
-        public const int BoolSize = 1;
+        internal const int Fixed32Size = 4;
+        internal const int Fixed64Size = 8;
+        internal const int SFixed32Size = 4;
+        internal const int SFixed64Size = 8;
+        internal const int FloatSize = 4;
+        internal const int DoubleSize = 8;
+        internal const int BoolSize = 1;
 
 
         #endregion
         #endregion
 
 
@@ -72,22 +72,7 @@ namespace Google.Protobuf
             EndGroup = 4,
             EndGroup = 4,
             Fixed32 = 5
             Fixed32 = 5
         }
         }
-
-        internal static class MessageSetField
-        {
-            internal const int Item = 1;
-            internal const int TypeID = 2;
-            internal const int Message = 3;
-        }
-
-        internal static class MessageSetTag
-        {
-            internal static readonly uint ItemStart = MakeTag(MessageSetField.Item, WireType.StartGroup);
-            internal static readonly uint ItemEnd = MakeTag(MessageSetField.Item, WireType.EndGroup);
-            internal static readonly uint TypeID = MakeTag(MessageSetField.TypeID, WireType.Varint);
-            internal static readonly uint Message = MakeTag(MessageSetField.Message, WireType.LengthDelimited);
-        }
-
+        
         private const int TagTypeBits = 3;
         private const int TagTypeBits = 3;
         private const uint TagTypeMask = (1 << TagTypeBits) - 1;
         private const uint TagTypeMask = (1 << TagTypeBits) - 1;
 
 
@@ -120,7 +105,6 @@ namespace Google.Protobuf
             return (uint) (fieldNumber << TagTypeBits) | (uint) wireType;
             return (uint) (fieldNumber << TagTypeBits) | (uint) wireType;
         }
         }
 
 
-#if !LITE
         public static uint MakeTag(FieldDescriptor field)
         public static uint MakeTag(FieldDescriptor field)
         {
         {
             return MakeTag(field.FieldNumber, GetWireType(field));
             return MakeTag(field.FieldNumber, GetWireType(field));
@@ -135,8 +119,6 @@ namespace Google.Protobuf
             return descriptor.IsPacked ? WireType.LengthDelimited : GetWireType(descriptor.FieldType);
             return descriptor.IsPacked ? WireType.LengthDelimited : GetWireType(descriptor.FieldType);
         }
         }
 
 
-#endif
-
         /// <summary>
         /// <summary>
         /// Converts a field type to its wire type. Done with a switch for the sake
         /// Converts a field type to its wire type. Done with a switch for the sake
         /// of speed - this is significantly faster than a dictionary lookup.
         /// of speed - this is significantly faster than a dictionary lookup.
@@ -177,7 +159,7 @@ namespace Google.Protobuf
                 case FieldType.Enum:
                 case FieldType.Enum:
                     return WireType.Varint;
                     return WireType.Varint;
                 default:
                 default:
-                    throw new ArgumentOutOfRangeException("No such field type");
+                    throw new ArgumentOutOfRangeException("fieldType", "No such field type");
             }
             }
         }
         }
     }
     }

+ 8 - 39
src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc

@@ -49,7 +49,6 @@ namespace csharp {
 RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator(
 RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator(
     const FieldDescriptor* descriptor, int fieldOrdinal)
     const FieldDescriptor* descriptor, int fieldOrdinal)
     : FieldGeneratorBase(descriptor, fieldOrdinal) {
     : FieldGeneratorBase(descriptor, fieldOrdinal) {
-  variables_["packed"] = descriptor->is_packed() ? "Packed" : "";
 }
 }
 
 
 RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {
 RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {
@@ -57,6 +56,10 @@ RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {
 }
 }
 
 
 void RepeatedEnumFieldGenerator::GenerateMembers(io::Printer* printer) {
 void RepeatedEnumFieldGenerator::GenerateMembers(io::Printer* printer) {
+  printer->Print(
+    variables_,
+    "private static readonly pb::FieldCodec<$type_name$> _repeated_$name$_codec\n"
+    "    = pb::FieldCodec.ForEnum($tag$, x => (int) x, x => ($type_name$) x);");
   printer->Print(variables_,
   printer->Print(variables_,
     "private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
     "private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
   AddDeprecatedFlag(printer);
   AddDeprecatedFlag(printer);
@@ -76,53 +79,19 @@ void RepeatedEnumFieldGenerator::GenerateMergingCode(io::Printer* printer) {
 void RepeatedEnumFieldGenerator::GenerateParsingCode(io::Printer* printer) {
 void RepeatedEnumFieldGenerator::GenerateParsingCode(io::Printer* printer) {
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "input.ReadEnumArray<$type_name$>($name$_);\n");
+    "$name$_.AddEntriesFrom(input, _repeated_$name$_codec);\n");
 }
 }
 
 
 void RepeatedEnumFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
 void RepeatedEnumFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "if ($name$_.Count > 0) {\n");
-  printer->Indent();
-  if (descriptor_->is_packed()) {
-    printer->Print(
-      variables_,
-      "output.WriteRawTag($tag_bytes$);\n"
-      "output.WritePackedEnumArray($name$_);\n");
-  } else {
-    printer->Print(
-      variables_,
-      "output.Write$capitalized_type_name$Array($number$, $name$_);\n");
-  }
-  printer->Outdent();
-  printer->Print("}\n");
+    "$name$_.WriteTo(output, _repeated_$name$_codec);\n");
 }
 }
 
 
-void RepeatedEnumFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
-  // TODO(jonskeet): Move all this code into CodedOutputStream? It's a lot to repeat everywhere...
-  printer->Print(
-    variables_,
-    "if ($name$_.Count > 0) {\n");
-  printer->Indent();
-  printer->Print("int dataSize = 0;\n");
+void RepeatedEnumFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {  
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "foreach ($type_name$ element in $name$_) {\n"
-    "  dataSize += pb::CodedOutputStream.ComputeEnumSize((int) element);\n"
-    "}\n"
-    "size += dataSize;\n");
-  int tagSize = internal::WireFormat::TagSize(descriptor_->number(), descriptor_->type());
-  if (descriptor_->is_packed()) {
-    printer->Print(
-      "size += $tag_size$ + pb::CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);\n",
-      "tag_size", SimpleItoa(tagSize));
-  } else {
-    printer->Print(
-      "size += $tag_size$ * $name$_.Count;\n",
-      "tag_size", SimpleItoa(tagSize), "name", name());
-  }
-  printer->Outdent();
-  printer->Print("}\n");
+    "size += $name$_.CalculateSize(_repeated_$name$_codec);\n");
 }
 }
 
 
 void RepeatedEnumFieldGenerator::WriteHash(io::Printer* printer) {
 void RepeatedEnumFieldGenerator::WriteHash(io::Printer* printer) {

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

@@ -55,6 +55,10 @@ RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {
 }
 }
 
 
 void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) {
 void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) {
+  printer->Print(
+    variables_,
+    "private static readonly pb::FieldCodec<$type_name$> _repeated_$name$_codec\n"
+    "    = pb::FieldCodec.ForMessage($tag$, $type_name$.Parser);\n");
   printer->Print(
   printer->Print(
     variables_,
     variables_,
     "private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
     "private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
@@ -75,28 +79,19 @@ void RepeatedMessageFieldGenerator::GenerateMergingCode(io::Printer* printer) {
 void RepeatedMessageFieldGenerator::GenerateParsingCode(io::Printer* printer) {
 void RepeatedMessageFieldGenerator::GenerateParsingCode(io::Printer* printer) {
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "input.ReadMessageArray($name$_, $type_name$.Parser);\n");
+    "$name$_.AddEntriesFrom(input, _repeated_$name$_codec);\n");
 }
 }
 
 
 void RepeatedMessageFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
 void RepeatedMessageFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
-  // TODO(jonskeet): Bake the foreach loop into the generated code? We lose the
-  // advantage of knowing the tag bytes this way :(
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "if ($name$_.Count > 0) {\n"
-    "  output.WriteMessageArray($number$, $name$_);\n"
-    "}\n");
+    "$name$_.WriteTo(output, _repeated_$name$_codec);\n");
 }
 }
 
 
 void RepeatedMessageFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
 void RepeatedMessageFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "if ($name$_.Count > 0) {\n"
-    "  foreach ($type_name$ element in $name$_) {\n"
-    "    size += pb::CodedOutputStream.ComputeMessageSize(element);\n"
-    "  }\n"
-    "  size += $tag_size$ * $name$_.Count;\n"
-    "}\n");
+    "size += $name$_.CalculateSize(_repeated_$name$_codec);\n");
 }
 }
 
 
 void RepeatedMessageFieldGenerator::WriteHash(io::Printer* printer) {
 void RepeatedMessageFieldGenerator::WriteHash(io::Printer* printer) {

+ 12 - 51
src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc

@@ -49,7 +49,6 @@ namespace csharp {
 RepeatedPrimitiveFieldGenerator::RepeatedPrimitiveFieldGenerator(
 RepeatedPrimitiveFieldGenerator::RepeatedPrimitiveFieldGenerator(
     const FieldDescriptor* descriptor, int fieldOrdinal)
     const FieldDescriptor* descriptor, int fieldOrdinal)
     : FieldGeneratorBase(descriptor, fieldOrdinal) {
     : FieldGeneratorBase(descriptor, fieldOrdinal) {
-  variables_["packed"] = descriptor->is_packed() ? "Packed" : "";
 }
 }
 
 
 RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {
 RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {
@@ -57,6 +56,10 @@ RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {
 }
 }
 
 
 void RepeatedPrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
 void RepeatedPrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
+  printer->Print(
+    variables_,
+    "private static readonly pb::FieldCodec<$type_name$> _repeated_$name$_codec\n"
+    "    = pb::FieldCodec.For$capitalized_type_name$($tag$);\n");
   printer->Print(variables_,
   printer->Print(variables_,
     "private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
     "private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
   AddDeprecatedFlag(printer);
   AddDeprecatedFlag(printer);
@@ -74,63 +77,21 @@ void RepeatedPrimitiveFieldGenerator::GenerateMergingCode(io::Printer* printer)
 }
 }
 
 
 void RepeatedPrimitiveFieldGenerator::GenerateParsingCode(io::Printer* printer) {
 void RepeatedPrimitiveFieldGenerator::GenerateParsingCode(io::Printer* printer) {
-  printer->Print(variables_,
-    "input.Read$capitalized_type_name$Array($name$_);\n");
+  printer->Print(
+    variables_,
+    "$name$_.AddEntriesFrom(input, _repeated_$name$_codec);\n");
 }
 }
 
 
-void RepeatedPrimitiveFieldGenerator::GenerateSerializationCode(
-    io::Printer* printer) {
+void RepeatedPrimitiveFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "if ($name$_.Count > 0) {\n");
-  printer->Indent();
-  if (descriptor_->is_packed()) {
-    printer->Print(
-      variables_,
-      "output.WriteRawTag($tag_bytes$);\n"
-      "output.WritePacked$capitalized_type_name$Array($name$_);\n");
-  } else {
-    printer->Print(
-      variables_,
-      "output.Write$capitalized_type_name$Array($number$, $name$_);\n");
-  }
-  printer->Outdent();
-  printer->Print("}\n");
+    "$name$_.WriteTo(output, _repeated_$name$_codec);\n");
 }
 }
 
 
-void RepeatedPrimitiveFieldGenerator::GenerateSerializedSizeCode(
-    io::Printer* printer) {
-  // TODO(jonskeet): Do this in the runtime if possible. It's a pain, but it must be feasible...
+void RepeatedPrimitiveFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
   printer->Print(
   printer->Print(
-    "if ($name$_.Count > 0) {\n",
-    "name", name());
-  printer->Indent();
-  printer->Print("int dataSize = 0;\n");
-  int fixedSize = GetFixedSize(descriptor_->type());
-  if (fixedSize == -1) {
-    printer->Print(
-      variables_,
-      "foreach ($type_name$ element in $name$_) {\n"
-      "  dataSize += pb::CodedOutputStream.Compute$capitalized_type_name$Size(element);\n"
-      "}\n");
-  } else {
-    printer->Print(
-      "dataSize = $size$ * $name$_.Count;\n",
-      "size", SimpleItoa(fixedSize), "name", name());
-  }
-  printer->Print("size += dataSize;\n");
-  int tagSize = internal::WireFormat::TagSize(descriptor_->number(), descriptor_->type());
-  if (descriptor_->is_packed()) {
-    printer->Print(
-      "size += $tag_size$ + pb::CodedOutputStream.ComputeInt32Size(dataSize);\n",
-      "tag_size", SimpleItoa(tagSize));
-  } else {
-    printer->Print(
-      "size += $tag_size$ * $name$_.Count;\n",
-      "tag_size", SimpleItoa(tagSize), "name", name());
-  }
-  printer->Outdent();
-  printer->Print("}\n");
+    variables_,
+    "size += $name$_.CalculateSize(_repeated_$name$_codec);\n");
 }
 }
 
 
 void RepeatedPrimitiveFieldGenerator::WriteHash(io::Printer* printer) {
 void RepeatedPrimitiveFieldGenerator::WriteHash(io::Printer* printer) {

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