Browse Source

First pass (not yet compiling) at removing all the array handling code from Coded*Stream.

Prod code works, but some tests are broken. Obviously those need fixing, then more tests,
and review benchmarks.
Jon Skeet 10 years ago
parent
commit
f2a27cc2c7

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

@@ -131,6 +131,8 @@ namespace Google.Protobuf.Examples.AddressBook {
     }
 
     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>();
     public pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> Phone {
       get { return phone_; }
@@ -176,9 +178,7 @@ namespace Google.Protobuf.Examples.AddressBook {
         output.WriteRawTag(26);
         output.WriteString(Email);
       }
-      if (phone_.Count > 0) {
-        output.WriteMessageArray(4, phone_);
-      }
+      phone_.WriteTo(output, _repeated_phone_codec);
     }
 
     public int CalculateSize() {
@@ -192,12 +192,7 @@ namespace Google.Protobuf.Examples.AddressBook {
       if (Email.Length != 0) {
         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;
     }
 
@@ -241,7 +236,7 @@ namespace Google.Protobuf.Examples.AddressBook {
             break;
           }
           case 34: {
-            input.ReadMessageArray(phone_, global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser);
+            phone_.AddEntriesFrom(input, _repeated_phone_codec);
             break;
           }
         }
@@ -437,6 +432,8 @@ namespace Google.Protobuf.Examples.AddressBook {
     }
 
     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>();
     public pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person> Person {
       get { return person_; }
@@ -464,19 +461,12 @@ namespace Google.Protobuf.Examples.AddressBook {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (person_.Count > 0) {
-        output.WriteMessageArray(1, person_);
-      }
+      person_.WriteTo(output, _repeated_person_codec);
     }
 
     public int CalculateSize() {
       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;
     }
 
@@ -499,7 +489,7 @@ namespace Google.Protobuf.Examples.AddressBook {
             }
             break;
           case 10: {
-            input.ReadMessageArray(person_, global::Google.Protobuf.Examples.AddressBook.Person.Parser);
+            person_.AddEntriesFrom(input, _repeated_person_codec);
             break;
           }
         }

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

@@ -422,13 +422,15 @@ namespace UnitTest.Issues.TestProtos {
     }
 
     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 {
       get { return values_; }
     }
 
     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 {
       get { return packedValues_; }
     }
@@ -463,13 +465,8 @@ namespace UnitTest.Issues.TestProtos {
         output.WriteRawTag(8);
         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() {
@@ -477,22 +474,8 @@ namespace UnitTest.Issues.TestProtos {
       if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO) {
         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;
     }
 
@@ -524,12 +507,12 @@ namespace UnitTest.Issues.TestProtos {
           }
           case 18:
           case 16: {
-            input.ReadEnumArray<global::UnitTest.Issues.TestProtos.NegativeEnum>(values_);
+            values_.AddEntriesFrom(input, _repeated_values_codec);
             break;
           }
           case 26:
           case 24: {
-            input.ReadEnumArray<global::UnitTest.Issues.TestProtos.NegativeEnum>(packedValues_);
+            packedValues_.AddEntriesFrom(input, _repeated_packedValues_codec);
             break;
           }
         }
@@ -678,6 +661,8 @@ namespace UnitTest.Issues.TestProtos {
     }
 
     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>();
     [global::System.ObsoleteAttribute()]
     public pbc::RepeatedField<int> PrimitiveArray {
@@ -696,6 +681,8 @@ namespace UnitTest.Issues.TestProtos {
     }
 
     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>();
     [global::System.ObsoleteAttribute()]
     public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild> MessageArray {
@@ -714,7 +701,8 @@ namespace UnitTest.Issues.TestProtos {
     }
 
     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()]
     public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> EnumArray {
       get { return enumArray_; }
@@ -756,25 +744,17 @@ namespace UnitTest.Issues.TestProtos {
         output.WriteRawTag(8);
         output.WriteInt32(PrimitiveValue);
       }
-      if (primitiveArray_.Count > 0) {
-        output.WriteRawTag(18);
-        output.WritePackedInt32Array(primitiveArray_);
-      }
+      primitiveArray_.WriteTo(output, _repeated_primitiveArray_codec);
       if (messageValue_ != null) {
         output.WriteRawTag(26);
         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) {
         output.WriteRawTag(40);
         output.WriteEnum((int) EnumValue);
       }
-      if (enumArray_.Count > 0) {
-        output.WriteRawTag(50);
-        output.WritePackedEnumArray(enumArray_);
-      }
+      enumArray_.WriteTo(output, _repeated_enumArray_codec);
     }
 
     public int CalculateSize() {
@@ -782,34 +762,15 @@ namespace UnitTest.Issues.TestProtos {
       if (PrimitiveValue != 0) {
         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) {
         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) {
         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;
     }
 
@@ -851,7 +812,7 @@ namespace UnitTest.Issues.TestProtos {
           }
           case 18:
           case 16: {
-            input.ReadInt32Array(primitiveArray_);
+            primitiveArray_.AddEntriesFrom(input, _repeated_primitiveArray_codec);
             break;
           }
           case 26: {
@@ -862,7 +823,7 @@ namespace UnitTest.Issues.TestProtos {
             break;
           }
           case 34: {
-            input.ReadMessageArray(messageArray_, global::UnitTest.Issues.TestProtos.DeprecatedChild.Parser);
+            messageArray_.AddEntriesFrom(input, _repeated_messageArray_codec);
             break;
           }
           case 40: {
@@ -871,7 +832,7 @@ namespace UnitTest.Issues.TestProtos {
           }
           case 50:
           case 48: {
-            input.ReadEnumArray<global::UnitTest.Issues.TestProtos.DeprecatedEnum>(enumArray_);
+            enumArray_.AddEntriesFrom(input, _repeated_enumArray_codec);
             break;
           }
         }

File diff suppressed because it is too large
+ 196 - 487
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestProto3.cs


+ 6 - 282
csharp/src/ProtocolBuffers/CodedInputStream.cs

@@ -172,6 +172,12 @@ namespace Google.Protobuf
             }
         }
 
+        /// <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 Validation
 
         /// <summary>
@@ -435,26 +441,6 @@ namespace Google.Protobuf
             return DecodeZigZag64(ReadRawVarint64());
         }
 
-        private bool BeginArray(uint fieldTag, out bool isPacked, out int oldLimit)
-        {
-            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;
-        }
-
         /// <summary>
         /// Peeks at the next tag in the stream. If it matches <paramref name="tag"/>,
         /// the tag is consumed and the method returns <c>true</c>; otherwise, the
@@ -474,268 +460,6 @@ namespace Google.Protobuf
             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
 
         #region Underlying reading primitives

+ 0 - 325
csharp/src/ProtocolBuffers/CodedOutputStream.cs

@@ -306,166 +306,6 @@ namespace Google.Protobuf
 
         #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
-        {
-            // 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);
-            }
-        }
-
-        #endregion
-
         #region Raw tag writing
         /// <summary>
         /// Encodes and writes a tag.
@@ -534,171 +374,6 @@ namespace Google.Protobuf
         }
         #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
         /// <summary>
         /// Writes a 32 bit value as a varint. The fast route is taken when

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

@@ -334,6 +334,10 @@ namespace Google.Protobuf.Collections
 
         public int CalculateSize(Codec codec)
         {
+            if (Count == 0)
+            {
+                return 0;
+            }
             var message = new Codec.MessageAdapter(codec);
             int size = 0;
             foreach (var entry in list)
@@ -419,13 +423,13 @@ namespace Google.Protobuf.Collections
 
                 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()
                 {
-                    return codec.keyCodec.CalculateSize(Key) + codec.valueCodec.CalculateSize(Value);
+                    return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value);
                 }
             }
         }

+ 108 - 74
csharp/src/ProtocolBuffers/Collections/RepeatedField.cs

@@ -49,6 +49,112 @@ namespace Google.Protobuf.Collections
             return clone;
         }
 
+        public void AddEntriesFrom(CodedInputStream input, FieldCodec<T> codec)
+        {
+            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 = (int)(input.ReadRawVarint32() & int.MaxValue);
+                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)
+        {
+            // TODO: Assert that T is a value type, and that codec.Tag is packed?
+            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 void Freeze()
@@ -87,18 +193,6 @@ namespace Google.Protobuf.Collections
             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()
         {
             this.CheckMutable();
@@ -180,16 +274,7 @@ namespace Google.Protobuf.Collections
         IEnumerator IEnumerable.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()
         {
@@ -297,17 +382,7 @@ namespace Google.Protobuf.Collections
                 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>
         {
             private int index;
@@ -355,46 +430,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()
-            {
-            }
-        }
     }
 }

File diff suppressed because it is too large
+ 147 - 313
csharp/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs


+ 50 - 6
csharp/src/ProtocolBuffers/FieldCodec.cs

@@ -145,28 +145,68 @@ namespace Google.Protobuf
 
         private readonly Func<CodedInputStream, T> reader;
         private readonly Action<CodedOutputStream, T> writer;
-        private readonly Func<T, int> sizeComputer;
+        private readonly Func<T, int> sizeCalculator;
         private readonly uint tag;
         private readonly int tagSize;
+        private readonly int fixedSize;
 
         internal FieldCodec(
             Func<CodedInputStream, T> reader,
             Action<CodedOutputStream, T> writer,
-            Func<T, int> sizeComputer,
+            Func<T, int> sizeCalculator,
             uint tag)
         {
             this.reader = reader;
             this.writer = writer;
-            this.sizeComputer = sizeComputer;
+            this.sizeCalculator = sizeCalculator;
+            this.fixedSize = 0;
             this.tag = 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 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))
             {
@@ -180,9 +220,13 @@ namespace Google.Protobuf
             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;
         }        
     }
 }

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

@@ -49,7 +49,6 @@ namespace csharp {
 RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator(
     const FieldDescriptor* descriptor, int fieldOrdinal)
     : FieldGeneratorBase(descriptor, fieldOrdinal) {
-  variables_["packed"] = descriptor->is_packed() ? "Packed" : "";
 }
 
 RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {
@@ -57,6 +56,10 @@ RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {
 }
 
 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_,
     "private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
   AddDeprecatedFlag(printer);
@@ -76,53 +79,19 @@ void RepeatedEnumFieldGenerator::GenerateMergingCode(io::Printer* printer) {
 void RepeatedEnumFieldGenerator::GenerateParsingCode(io::Printer* printer) {
   printer->Print(
     variables_,
-    "input.ReadEnumArray<$type_name$>($name$_);\n");
+    "$name$_.AddEntriesFrom(input, _repeated_$name$_codec);\n");
 }
 
 void RepeatedEnumFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
   printer->Print(
     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(
     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) {

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

@@ -55,6 +55,10 @@ RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {
 }
 
 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(
     variables_,
     "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) {
   printer->Print(
     variables_,
-    "input.ReadMessageArray($name$_, $type_name$.Parser);\n");
+    "$name$_.AddEntriesFrom(input, _repeated_$name$_codec);\n");
 }
 
 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(
     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) {
   printer->Print(
     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) {

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

@@ -49,7 +49,6 @@ namespace csharp {
 RepeatedPrimitiveFieldGenerator::RepeatedPrimitiveFieldGenerator(
     const FieldDescriptor* descriptor, int fieldOrdinal)
     : FieldGeneratorBase(descriptor, fieldOrdinal) {
-  variables_["packed"] = descriptor->is_packed() ? "Packed" : "";
 }
 
 RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {
@@ -57,6 +56,10 @@ RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {
 }
 
 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_,
     "private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
   AddDeprecatedFlag(printer);
@@ -74,63 +77,21 @@ void RepeatedPrimitiveFieldGenerator::GenerateMergingCode(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(
     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(
-    "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) {

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