Tidying up and extra tests.

This is mostly just making things internal instead of public, removing and reordering a bunch of code in CodedInputStream/CodedOutputStream, and generally tidying up.
diff --git a/csharp/src/ProtocolBuffers.Test/CodedInputStreamExtensions.cs b/csharp/src/ProtocolBuffers.Test/CodedInputStreamExtensions.cs
new file mode 100644
index 0000000..408c7cb
--- /dev/null
+++ b/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;
+        }
+    }
+}
diff --git a/csharp/src/ProtocolBuffers.Test/CodedInputStreamTest.cs b/csharp/src/ProtocolBuffers.Test/CodedInputStreamTest.cs
index 47b5e0a..a64994f 100644
--- a/csharp/src/ProtocolBuffers.Test/CodedInputStreamTest.cs
+++ b/csharp/src/ProtocolBuffers.Test/CodedInputStreamTest.cs
@@ -35,10 +35,8 @@
 #endregion

 

 using System;

-using System.Collections.Generic;

 using System.IO;

 using Google.Protobuf.Collections;

-using Google.Protobuf.Descriptors;

 using Google.Protobuf.TestProtos;

 using NUnit.Framework;

 

@@ -62,7 +60,7 @@
         }

 

         /// <summary>

-        /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and

+        /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64()

         /// </summary>

         private static void AssertReadVarint(byte[] data, ulong value)

         {

@@ -232,66 +230,26 @@
             Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));

             Assert.AreEqual(unchecked((long) 0x8000000000000000L), CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));

         }

-        /*

+        

         [Test]

-        public void ReadWholeMessage()

+        public void ReadWholeMessage_VaryingBlockSizes()

         {

-            TestAllTypes message = TestUtil.GetAllSet();

+            TestAllTypes message = GeneratedMessageTest.GetSampleMessage();

 

             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.

             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]

-        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()

         {

             // Allocate and initialize a 1MB blob.

@@ -302,24 +260,15 @@
             }

 

             // 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

             // directly from a ByteString, so that CodedInputStream uses buffered

             // 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]

         public void ReadMaliciouslyLargeBlob()

@@ -461,85 +410,15 @@
             }

         }

 

-        enum TestNegEnum { None = 0, Value = -2 }

-

         [Test]

         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);

-            Assert.AreEqual((int)TestNegEnum.Value, input.ReadEnum());

+            Assert.AreEqual((int)SampleEnum.NegativeValue, input.ReadEnum());

             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);

-            // 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<TestNegEnum> values = new RepeatedField<TestNegEnum>();

-            values.AddEntriesFrom(input, FieldCodec.ForEnum(tag, x => (int) x, x => (TestNegEnum) x));

-

-            Assert.AreEqual(6, values.Count);

-            Assert.AreEqual(TestNegEnum.None, values[0]);

-            Assert.AreEqual(((TestNegEnum) (-1)), values[1]);

-            Assert.AreEqual(TestNegEnum.Value, values[2]);

-            Assert.AreEqual(((TestNegEnum)(-3)), values[3]);

-            Assert.AreEqual(((TestNegEnum)(-4)), values[4]);

-            Assert.AreEqual(((TestNegEnum)(-5)), values[5]);

-        }

-

-        [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<TestNegEnum> values = new RepeatedField<TestNegEnum>();

-            values.AddEntriesFrom(input, FieldCodec.ForEnum(tag, x => (int)x, x => (TestNegEnum)x));

-

-            Assert.AreEqual(6, values.Count);

-            Assert.AreEqual(TestNegEnum.None, values[0]);

-            Assert.AreEqual(((TestNegEnum)(-1)), values[1]);

-            Assert.AreEqual(TestNegEnum.Value, values[2]);

-            Assert.AreEqual(((TestNegEnum)(-3)), values[3]);

-            Assert.AreEqual(((TestNegEnum)(-4)), values[4]);

-            Assert.AreEqual(((TestNegEnum)(-5)), values[5]);

-        }

-

         //Issue 71:	CodedInputStream.ReadBytes go to slow path unnecessarily

         [Test]

         public void TestSlowPathAvoidance()

diff --git a/csharp/src/ProtocolBuffers.Test/CodedOutputStreamTest.cs b/csharp/src/ProtocolBuffers.Test/CodedOutputStreamTest.cs
index dd49e3d..ab5dcbd 100644
--- a/csharp/src/ProtocolBuffers.Test/CodedOutputStreamTest.cs
+++ b/csharp/src/ProtocolBuffers.Test/CodedOutputStreamTest.cs
@@ -35,9 +35,8 @@
 #endregion

 

 using System;

-using System.Collections.Generic;

 using System.IO;

-using Google.Protobuf.Collections;

+using Google.Protobuf.TestProtos;

 using NUnit.Framework;

 

 namespace Google.Protobuf

@@ -195,42 +194,24 @@
                 0x9abcdef012345678UL);

         }

 

-        /*

         [Test]

-        public void WriteWholeMessage()

+        public void WriteWholeMessage_VaryingBlockSizes()

         {

-            TestAllTypes message = TestUtil.GetAllSet();

+            TestAllTypes message = GeneratedMessageTest.GetSampleMessage();

 

             byte[] rawBytes = message.ToByteArray();

-            TestUtil.AssertEqualBytes(TestUtil.GoldenMessage.ToByteArray(), rawBytes);

 

             // Try different block sizes.

             for (int blockSize = 1; blockSize < 256; blockSize *= 2)

             {

                 MemoryStream rawOutput = new MemoryStream();

-                CodedOutputStream output =

-                    CodedOutputStream.CreateInstance(rawOutput, blockSize);

+                CodedOutputStream output = CodedOutputStream.CreateInstance(rawOutput, blockSize);

                 message.WriteTo(output);

                 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]

         public void EncodeZigZag32()

         {

@@ -296,68 +277,16 @@
         public void TestNegativeEnumNoTag()

         {

             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];

             CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);

-            output.WriteEnum((int) TestNegEnum.Value);

+            output.WriteEnum((int) SampleEnum.NegativeValue);

 

             Assert.AreEqual(0, output.SpaceLeft);

             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]

         public void TestCodedInputOutputPosition()

         {

@@ -407,7 +336,7 @@
                 Assert.AreEqual(130, cout.Position);

                 cout.Flush();

             }

-            //Now test Input stream:

+            // Now test Input stream:

             {

                 CodedInputStream cin = CodedInputStream.CreateInstance(new MemoryStream(bytes), new byte[50]);

                 uint tag;

@@ -420,8 +349,8 @@
                 //Field 2:

                 Assert.IsTrue(cin.ReadTag(out tag) && tag >> 3 == 2);

                 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);

                 int oldlimit = cin.PushLimit((int)childlen);

                 Assert.AreEqual(5, cin.Position);

diff --git a/csharp/src/ProtocolBuffers.Test/Collections/RepeatedFieldTest.cs b/csharp/src/ProtocolBuffers.Test/Collections/RepeatedFieldTest.cs
index 29945c3..988801b 100644
--- a/csharp/src/ProtocolBuffers.Test/Collections/RepeatedFieldTest.cs
+++ b/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.IO;
+using System.Linq;
 using Google.Protobuf.TestProtos;
 using NUnit.Framework;
 
@@ -89,5 +123,260 @@
             var clone = list.Clone();
             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]);
+        }
     }
 }
diff --git a/csharp/src/ProtocolBuffers.Test/FieldCodecTest.cs b/csharp/src/ProtocolBuffers.Test/FieldCodecTest.cs
index 5014162..a14040d 100644
--- a/csharp/src/ProtocolBuffers.Test/FieldCodecTest.cs
+++ b/csharp/src/ProtocolBuffers.Test/FieldCodecTest.cs
@@ -63,15 +63,21 @@
         };
 
         [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")]
         public void CalculateSize(ICodecTestData codec)
         {
-            codec.TestCalculateSize();
+            codec.TestCalculateSizeWithTag();
         }
 
         [Test, TestCaseSource("Codecs")]
@@ -82,8 +88,9 @@
 
         public interface ICodecTestData
         {
-            void TestRoundTrip();
-            void TestCalculateSize();
+            void TestRoundTripRaw();
+            void TestRoundTripWithTag();
+            void TestCalculateSizeWithTag();
             void TestDefaultValue();
         }
 
@@ -100,7 +107,19 @@
                 this.name = name;
             }
 
-            public void TestRoundTrip()
+            public void TestRoundTripRaw()
+            {
+                var stream = new MemoryStream();
+                var codedOutput = CodedOutputStream.CreateInstance(stream);
+                codec.ValueWriter(codedOutput, sampleValue);
+                codedOutput.Flush();
+                stream.Position = 0;
+                var codedInput = CodedInputStream.CreateInstance(stream);
+                Assert.AreEqual(sampleValue, codec.ValueReader(codedInput));
+                Assert.IsTrue(codedInput.IsAtEnd);
+            }
+
+            public void TestRoundTripWithTag()
             {
                 var stream = new MemoryStream();
                 var codedOutput = CodedOutputStream.CreateInstance(stream);
@@ -108,14 +127,12 @@
                 codedOutput.Flush();
                 stream.Position = 0;
                 var codedInput = CodedInputStream.CreateInstance(stream);
-                uint tag;
-                Assert.IsTrue(codedInput.ReadTag(out tag));
-                Assert.AreEqual(codec.Tag, tag);
+                codedInput.AssertNextTag(codec.Tag);
                 Assert.AreEqual(sampleValue, codec.Read(codedInput));
                 Assert.IsTrue(codedInput.IsAtEnd);
             }
 
-            public void TestCalculateSize()
+            public void TestCalculateSizeWithTag()
             {
                 var stream = new MemoryStream();
                 var codedOutput = CodedOutputStream.CreateInstance(stream);
@@ -126,6 +143,7 @@
 
             public void TestDefaultValue()
             {
+                // WriteTagAndValue ignores default values
                 var stream = new MemoryStream();
                 var codedOutput = CodedOutputStream.CreateInstance(stream);
                 codec.WriteTagAndValue(codedOutput, codec.DefaultValue);
@@ -136,9 +154,20 @@
                 {
                     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()
             {
diff --git a/csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs b/csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs
index f866866..a094e46 100644
--- a/csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs
+++ b/csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs
@@ -10,6 +10,61 @@
     /// </summary>

     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]

         public void EmptyMessageFieldDistinctFromMissingMessageField()

         {

@@ -485,5 +540,83 @@
             Assert.Throws<InvalidOperationException>(() => frozen.RepeatedDouble.Add(0.0));

             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);

+        }

     }

 }

diff --git a/csharp/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj b/csharp/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj
index 80b504a..ae7d757 100644
--- a/csharp/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj
+++ b/csharp/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj
@@ -72,6 +72,7 @@
   </ItemGroup>

   <ItemGroup>

     <Compile Include="ByteStringTest.cs" />

+    <Compile Include="CodedInputStreamExtensions.cs" />

     <Compile Include="CodedInputStreamTest.cs" />

     <Compile Include="CodedOutputStreamTest.cs" />

     <Compile Include="EqualityTester.cs" />

@@ -79,6 +80,7 @@
     <Compile Include="GeneratedMessageTest.cs" />

     <Compile Include="Collections\MapFieldTest.cs" />

     <Compile Include="Collections\RepeatedFieldTest.cs" />

+    <Compile Include="SampleEnum.cs" />

     <Compile Include="TestProtos\MapUnittestProto3.cs" />

     <Compile Include="TestProtos\UnittestImportProto3.cs" />

     <Compile Include="TestProtos\UnittestImportPublicProto3.cs" />

diff --git a/csharp/src/ProtocolBuffers.Test/SampleEnum.cs b/csharp/src/ProtocolBuffers.Test/SampleEnum.cs
new file mode 100644
index 0000000..001f9b0
--- /dev/null
+++ b/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
+    }
+}
\ No newline at end of file
diff --git a/csharp/src/ProtocolBuffers/CodedInputStream.cs b/csharp/src/ProtocolBuffers/CodedInputStream.cs
index 75178d1..5c64fd9 100644
--- a/csharp/src/ProtocolBuffers/CodedInputStream.cs
+++ b/csharp/src/ProtocolBuffers/CodedInputStream.cs
@@ -37,7 +37,6 @@
 using System;

 using System.Collections.Generic;

 using System.IO;

-using Google.Protobuf.Collections;

 

 namespace Google.Protobuf

 {

@@ -178,9 +177,62 @@
         /// </summary>

         internal uint LastTag { get { return lastTag; } }

 

-        #region Validation

+        #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>

         /// 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

         /// end tag.

@@ -194,13 +246,12 @@
                 throw InvalidProtocolBufferException.InvalidEndTag();

             }

         }

-

         #endregion

 

         #region Reading of tags etc

 

         /// <summary>

-        /// Attempt to peek at the next field tag.

+        /// Attempts to peek at the next field tag.

         /// </summary>

         public bool PeekNextTag(out uint fieldTag)

         {

@@ -218,7 +269,7 @@
         }

 

         /// <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.

         /// </summary>

         /// <param name="fieldTag">The 'tag' of the field (id * 8 + wire-format)</param>

@@ -233,14 +284,42 @@
                 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;

             if (lastTag == 0)

             {

@@ -251,7 +330,7 @@
         }

 

         /// <summary>

-        /// Read a double field from the stream.

+        /// Reads a double field from the stream.

         /// </summary>

         public double ReadDouble()

         {

@@ -259,7 +338,7 @@
         }

 

         /// <summary>

-        /// Read a float field from the stream.

+        /// Reads a float field from the stream.

         /// </summary>

         public float ReadFloat()

         {

@@ -281,7 +360,7 @@
         }

 

         /// <summary>

-        /// Read a uint64 field from the stream.

+        /// Reads a uint64 field from the stream.

         /// </summary>

         public ulong ReadUInt64()

         {

@@ -289,7 +368,7 @@
         }

 

         /// <summary>

-        /// Read an int64 field from the stream.

+        /// Reads an int64 field from the stream.

         /// </summary>

         public long ReadInt64()

         {

@@ -297,7 +376,7 @@
         }

 

         /// <summary>

-        /// Read an int32 field from the stream.

+        /// Reads an int32 field from the stream.

         /// </summary>

         public int ReadInt32()

         {

@@ -305,7 +384,7 @@
         }

 

         /// <summary>

-        /// Read a fixed64 field from the stream.

+        /// Reads a fixed64 field from the stream.

         /// </summary>

         public ulong ReadFixed64()

         {

@@ -313,7 +392,7 @@
         }

 

         /// <summary>

-        /// Read a fixed32 field from the stream.

+        /// Reads a fixed32 field from the stream.

         /// </summary>

         public uint ReadFixed32()

         {

@@ -321,7 +400,7 @@
         }

 

         /// <summary>

-        /// Read a bool field from the stream.

+        /// Reads a bool field from the stream.

         /// </summary>

         public bool ReadBool()

         {

@@ -333,22 +412,22 @@
         /// </summary>

         public string ReadString()

         {

-            int size = (int) ReadRawVarint32();

+            int length = ReadLength();

             // No need to read any data for an empty string.

-            if (size == 0)

+            if (length == 0)

             {

                 return "";

             }

-            if (size <= bufferSize - bufferPos)

+            if (length <= bufferSize - bufferPos)

             {

                 // Fast path:  We already have the bytes in a contiguous buffer, so

                 //   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;

             }

             // 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>

@@ -356,7 +435,7 @@
         /// </summary>   

         public void ReadMessage(IMessage builder)

         {

-            int length = (int) ReadRawVarint32();

+            int length = ReadLength();

             if (recursionDepth >= recursionLimit)

             {

                 throw InvalidProtocolBufferException.RecursionLimitExceeded();

@@ -374,19 +453,19 @@
         /// </summary>   

         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

                 //   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;

             }

             else

             {

                 // Slow path:  Build a byte array and attach it to a new ByteString.

-                return ByteString.AttachBytes(ReadRawBytes(size));

+                return ByteString.AttachBytes(ReadRawBytes(length));

             }

         }

 

@@ -442,6 +521,18 @@
         }

 

         /// <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()

+        {

+            return (int) ReadRawVarint32();

+        }

+

+        /// <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

         /// stream is left in the original position and the method returns <c>false</c>.

@@ -517,12 +608,12 @@
         }

 

         /// <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.

         /// That means we can check the size just once, then just read directly from the buffer

         /// without constant rechecking of the buffer length.

         /// </summary>

-        public uint ReadRawVarint32()

+        internal uint ReadRawVarint32()

         {

             if (bufferPos + 5 > bufferSize)

             {

@@ -581,13 +672,13 @@
         /// <summary>

         /// 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

-        /// 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

         /// CodedInputStream buffers its input.

         /// </summary>

         /// <param name="input"></param>

         /// <returns></returns>

-        public static uint ReadRawVarint32(Stream input)

+        internal static uint ReadRawVarint32(Stream input)

         {

             int result = 0;

             int offset = 0;

@@ -621,9 +712,9 @@
         }

 

         /// <summary>

-        /// Read a raw varint from the stream.

+        /// Reads a raw varint from the stream.

         /// </summary>

-        public ulong ReadRawVarint64()

+        internal ulong ReadRawVarint64()

         {

             int shift = 0;

             ulong result = 0;

@@ -641,9 +732,9 @@
         }

 

         /// <summary>

-        /// Read a 32-bit little-endian integer from the stream.

+        /// Reads a 32-bit little-endian integer from the stream.

         /// </summary>

-        public uint ReadRawLittleEndian32()

+        internal uint ReadRawLittleEndian32()

         {

             uint b1 = ReadRawByte();

             uint b2 = ReadRawByte();

@@ -653,9 +744,9 @@
         }

 

         /// <summary>

-        /// Read a 64-bit little-endian integer from the stream.

+        /// Reads a 64-bit little-endian integer from the stream.

         /// </summary>

-        public ulong ReadRawLittleEndian64()

+        internal ulong ReadRawLittleEndian64()

         {

             ulong b1 = ReadRawByte();

             ulong b2 = ReadRawByte();

@@ -669,97 +760,44 @@
                    | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);

         }

 

+        /// <summary>

+        /// Decode a 32-bit value with ZigZag encoding.

+        /// </summary>

+        /// <remarks>

+        /// ZigZag encodes signed integers into values that can be efficiently

+        /// encoded with varint.  (Otherwise, negative values must be 

+        /// sign-extended to 64 bits to be varint encoded, thus always taking

+        /// 10 bytes on the wire.)

+        /// </remarks>

+        internal static int DecodeZigZag32(uint n)

+        {

+            return (int)(n >> 1) ^ -(int)(n & 1);

+        }

+

+        /// <summary>

+        /// Decode a 32-bit value with ZigZag encoding.

+        /// </summary>

+        /// <remarks>

+        /// ZigZag encodes signed integers into values that can be efficiently

+        /// encoded with varint.  (Otherwise, negative values must be 

+        /// sign-extended to 64 bits to be varint encoded, thus always taking

+        /// 10 bytes on the wire.)

+        /// </remarks>

+        internal static long DecodeZigZag64(ulong n)

+        {

+            return (long)(n >> 1) ^ -(long)(n & 1);

+        }

         #endregion

 

-        /// <summary>

-        /// Decode a 32-bit value with ZigZag encoding.

-        /// </summary>

-        /// <remarks>

-        /// ZigZag encodes signed integers into values that can be efficiently

-        /// encoded with varint.  (Otherwise, negative values must be 

-        /// sign-extended to 64 bits to be varint encoded, thus always taking

-        /// 10 bytes on the wire.)

-        /// </remarks>

-        public static int DecodeZigZag32(uint n)

-        {

-            return (int) (n >> 1) ^ -(int) (n & 1);

-        }

-

-        /// <summary>

-        /// Decode a 32-bit value with ZigZag encoding.

-        /// </summary>

-        /// <remarks>

-        /// ZigZag encodes signed integers into values that can be efficiently

-        /// encoded with varint.  (Otherwise, negative values must be 

-        /// sign-extended to 64 bits to be varint encoded, thus always taking

-        /// 10 bytes on the wire.)

-        /// </remarks>

-        public 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;

-        }

-

         #region Internal reading and buffer management

 

         /// <summary>

-        /// Resets the current size counter to zero (see SetSizeLimit).

-        /// </summary>

-        public void ResetSizeCounter()

-        {

-            totalBytesRetired = 0;

-        }

-

-        /// <summary>

         /// Sets currentLimit to (current position) + byteLimit. This is called

         /// when descending into a length-delimited embedded message. The previous

         /// limit is returned.

         /// </summary>

         /// <returns>The old limit.</returns>

-        public int PushLimit(int byteLimit)

+        internal int PushLimit(int byteLimit)

         {

             if (byteLimit < 0)

             {

@@ -797,7 +835,7 @@
         /// <summary>

         /// Discards the current limit, returning the previous limit.

         /// </summary>

-        public void PopLimit(int oldLimit)

+        internal void PopLimit(int oldLimit)

         {

             currentLimit = oldLimit;

             RecomputeBufferSizeAfterLimit();

@@ -807,7 +845,7 @@
         /// Returns whether or not all the data before the limit has been read.

         /// </summary>

         /// <returns></returns>

-        public bool ReachedLimit

+        internal bool ReachedLimit

         {

             get

             {

@@ -897,7 +935,7 @@
         /// <exception cref="InvalidProtocolBufferException">

         /// the end of the stream or the current limit was reached

         /// </exception>

-        public byte ReadRawByte()

+        internal byte ReadRawByte()

         {

             if (bufferPos == bufferSize)

             {

@@ -907,12 +945,12 @@
         }

 

         /// <summary>

-        /// Read a fixed size of bytes from the input.

+        /// Reads a fixed size of bytes from the input.

         /// </summary>

         /// <exception cref="InvalidProtocolBufferException">

         /// the end of the stream or the current limit was reached

         /// </exception>

-        public byte[] ReadRawBytes(int size)

+        internal byte[] ReadRawBytes(int size)

         {

             if (size < 0)

             {

@@ -921,7 +959,8 @@
 

             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);

                 // Then fail.

                 throw InvalidProtocolBufferException.TruncatedMessage();

@@ -1026,62 +1065,11 @@
         }

 

         /// <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>

         /// Reads and discards <paramref name="size"/> bytes.

         /// </summary>

         /// <exception cref="InvalidProtocolBufferException">the end of the stream

         /// or the current limit was reached</exception>

-        public void SkipRawBytes(int size)

+        private void SkipRawBytes(int size)

         {

             if (size < 0)

             {

diff --git a/csharp/src/ProtocolBuffers/CodedOutputStream.ComputeSize.cs b/csharp/src/ProtocolBuffers/CodedOutputStream.ComputeSize.cs
index ef1f4c0..82aba51 100644
--- a/csharp/src/ProtocolBuffers/CodedOutputStream.ComputeSize.cs
+++ b/csharp/src/ProtocolBuffers/CodedOutputStream.ComputeSize.cs
@@ -50,7 +50,7 @@
         private const int LittleEndian32Size = 4;        

 

         /// <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.

         /// </summary>

         public static int ComputeDoubleSize(double value)

@@ -59,7 +59,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeFloatSize(float value)

@@ -68,7 +68,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeUInt64Size(ulong value)

@@ -77,7 +77,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeInt64Size(long value)

@@ -86,7 +86,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeInt32Size(int value)

@@ -103,7 +103,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeFixed64Size(ulong value)

@@ -112,7 +112,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeFixed32Size(uint value)

@@ -121,7 +121,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeBoolSize(bool value)

@@ -130,7 +130,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeStringSize(String value)

@@ -141,7 +141,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeGroupSize(IMessage value)

@@ -150,7 +150,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeMessageSize(IMessage value)

@@ -160,7 +160,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeBytesSize(ByteString value)

@@ -170,7 +170,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeUInt32Size(uint value)

@@ -179,7 +179,7 @@
         }

 

         /// <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

         /// converting the enum value to its numeric value.

         /// </summary>

@@ -190,7 +190,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeSFixed32Size(int value)

@@ -199,7 +199,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeSFixed64Size(long value)

@@ -208,7 +208,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeSInt32Size(int value)

@@ -217,7 +217,7 @@
         }

 

         /// <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.

         /// </summary>

         public static int ComputeSInt64Size(long value)

@@ -226,7 +226,16 @@
         }

 

         /// <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>

         public static int ComputeRawVarint32Size(uint value)

         {

@@ -250,7 +259,7 @@
         }

 

         /// <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>

         public static int ComputeRawVarint64Size(ulong value)

         {

@@ -294,7 +303,7 @@
         }

 

         /// <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>

         public static int ComputeTagSize(int fieldNumber)

         {

diff --git a/csharp/src/ProtocolBuffers/CodedOutputStream.cs b/csharp/src/ProtocolBuffers/CodedOutputStream.cs
index 99a99ae..161f48f 100644
--- a/csharp/src/ProtocolBuffers/CodedOutputStream.cs
+++ b/csharp/src/ProtocolBuffers/CodedOutputStream.cs
@@ -37,7 +37,6 @@
 using System;

 using System.IO;

 using System.Text;

-using Google.Protobuf.Collections;

 

 namespace Google.Protobuf

 {

@@ -141,11 +140,12 @@
             }

         }

 

-        #region Writing of values without tags

+        #region Writing of values (not including tags)

 

         /// <summary>

-        /// Writes a double field value, including tag, to the stream.

+        /// Writes a double field value, without a tag, to the stream.

         /// </summary>

+        /// <param name="value">The value to write</param>

         public void WriteDouble(double value)

         {

             WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));

@@ -154,6 +154,7 @@
         /// <summary>

         /// Writes a float field value, without a tag, to the stream.

         /// </summary>

+        /// <param name="value">The value to write</param>

         public void WriteFloat(float value)

         {

             byte[] rawBytes = BitConverter.GetBytes(value);

@@ -178,6 +179,7 @@
         /// <summary>

         /// Writes a uint64 field value, without a tag, to the stream.

         /// </summary>

+        /// <param name="value">The value to write</param>

         public void WriteUInt64(ulong value)

         {

             WriteRawVarint64(value);

@@ -186,6 +188,7 @@
         /// <summary>

         /// Writes an int64 field value, without a tag, to the stream.

         /// </summary>

+        /// <param name="value">The value to write</param>

         public void WriteInt64(long value)

         {

             WriteRawVarint64((ulong) value);

@@ -194,6 +197,7 @@
         /// <summary>

         /// Writes an int32 field value, without a tag, to the stream.

         /// </summary>

+        /// <param name="value">The value to write</param>

         public void WriteInt32(int value)

         {

             if (value >= 0)

@@ -210,6 +214,7 @@
         /// <summary>

         /// Writes a fixed64 field value, without a tag, to the stream.

         /// </summary>

+        /// <param name="value">The value to write</param>

         public void WriteFixed64(ulong value)

         {

             WriteRawLittleEndian64(value);

@@ -218,6 +223,7 @@
         /// <summary>

         /// Writes a fixed32 field value, without a tag, to the stream.

         /// </summary>

+        /// <param name="value">The value to write</param>

         public void WriteFixed32(uint value)

         {

             WriteRawLittleEndian32(value);

@@ -226,6 +232,7 @@
         /// <summary>

         /// Writes a bool field value, without a tag, to the stream.

         /// </summary>

+        /// <param name="value">The value to write</param>

         public void WriteBool(bool value)

         {

             WriteRawByte(value ? (byte) 1 : (byte) 0);

@@ -233,13 +240,15 @@
 

         /// <summary>

         /// Writes a string field value, without a tag, to the stream.

+        /// The data is length-prefixed.

         /// </summary>

+        /// <param name="value">The value to write</param>

         public void WriteString(string value)

         {

             // Optimise the case where we have enough space to write

             // the string directly to the buffer, which should be common.

             int length = Utf8Encoding.GetByteCount(value);

-            WriteRawVarint32((uint)length);

+            WriteLength(length);

             if (limit - position >= length)

             {

                 if (length == value.Length) // Must be all ASCII...

@@ -262,23 +271,41 @@
             }

         }

 

+        /// <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)

         {

             WriteRawVarint32((uint) value.CalculateSize());

             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)

         {

             WriteRawVarint32((uint) value.Length);

             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)

         {

             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)

         {

             WriteInt32(value);

@@ -289,27 +316,53 @@
             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)

         {

             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)

         {

             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)

         {

             WriteRawVarint64(EncodeZigZag64(value));

         }

 

+        /// <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)

+        {

+            WriteRawVarint32((uint) length);

+        }

+

         #endregion

 

         #region Raw tag writing

         /// <summary>

         /// Encodes and writes a tag.

         /// </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)

         {

             WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));

@@ -318,6 +371,7 @@
         /// <summary>

         /// Writes an already-encoded tag.

         /// </summary>

+        /// <param name="tag">The encoded tag</param>

         public void WriteTag(uint tag)

         {

             WriteRawVarint32(tag);

@@ -326,6 +380,7 @@
         /// <summary>

         /// Writes the given single-byte tag directly to the stream.

         /// </summary>

+        /// <param name="b1">The encoded tag</param>

         public void WriteRawTag(byte b1)

         {

             WriteRawByte(b1);

@@ -334,6 +389,8 @@
         /// <summary>

         /// Writes the given two-byte tag directly to the stream.

         /// </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)

         {

             WriteRawByte(b1);

@@ -343,6 +400,9 @@
         /// <summary>

         /// Writes the given three-byte tag directly to the stream.

         /// </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)

         {

             WriteRawByte(b1);

@@ -353,6 +413,10 @@
         /// <summary>

         /// Writes the given four-byte tag directly to the stream.

         /// </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)

         {

             WriteRawByte(b1);

@@ -364,6 +428,11 @@
         /// <summary>

         /// Writes the given five-byte tag directly to the stream.

         /// </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)

         {

             WriteRawByte(b1);

@@ -380,7 +449,7 @@
         /// there's enough buffer space left to whizz through without checking

         /// for each byte; otherwise, we resort to calling WriteRawByte each time.

         /// </summary>

-        public void WriteRawVarint32(uint value)

+        internal void WriteRawVarint32(uint value)

         {

             // Optimize for the common case of a single byte value

             if (value < 128 && position < limit)

@@ -409,7 +478,7 @@
             }

         }

 

-        public void WriteRawVarint64(ulong value)

+        internal void WriteRawVarint64(ulong value)

         {

             while (value > 127 && position < limit)

             {

@@ -431,7 +500,7 @@
             }

         }

 

-        public void WriteRawLittleEndian32(uint value)

+        internal void WriteRawLittleEndian32(uint value)

         {

             if (position + 4 > limit)

             {

@@ -449,7 +518,7 @@
             }

         }

 

-        public void WriteRawLittleEndian64(ulong value)

+        internal void WriteRawLittleEndian64(ulong value)

         {

             if (position + 8 > limit)

             {

@@ -475,7 +544,7 @@
             }

         }

 

-        public void WriteRawByte(byte value)

+        internal void WriteRawByte(byte value)

         {

             if (position == limit)

             {

@@ -485,7 +554,7 @@
             buffer[position++] = value;

         }

 

-        public void WriteRawByte(uint value)

+        internal void WriteRawByte(uint value)

         {

             WriteRawByte((byte) value);

         }

@@ -493,7 +562,7 @@
         /// <summary>

         /// Writes out an array of bytes.

         /// </summary>

-        public void WriteRawBytes(byte[] value)

+        internal void WriteRawBytes(byte[] value)

         {

             WriteRawBytes(value, 0, value.Length);

         }

@@ -501,7 +570,7 @@
         /// <summary>

         /// Writes out part of an array of bytes.

         /// </summary>

-        public void WriteRawBytes(byte[] value, int offset, int length)

+        internal void WriteRawBytes(byte[] value, int offset, int length)

         {

             if (limit - position >= length)

             {

@@ -548,7 +617,7 @@
         /// sign-extended to 64 bits to be varint encoded, thus always taking

         /// 10 bytes on the wire.)

         /// </remarks>

-        public static uint EncodeZigZag32(int n)

+        internal static uint EncodeZigZag32(int n)

         {

             // Note:  the right-shift must be arithmetic

             return (uint) ((n << 1) ^ (n >> 31));

@@ -563,7 +632,7 @@
         /// sign-extended to 64 bits to be varint encoded, thus always taking

         /// 10 bytes on the wire.)

         /// </remarks>

-        public static ulong EncodeZigZag64(long n)

+        internal static ulong EncodeZigZag64(long n)

         {

             return (ulong) ((n << 1) ^ (n >> 63));

         }

diff --git a/csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs b/csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs
index 031ebd0..cf3ff83 100644
--- a/csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs
+++ b/csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs
@@ -38,7 +38,7 @@
     /// <summary>

     /// Read-only wrapper around another dictionary.

     /// </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;

 

diff --git a/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs b/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs
index 588f66a..0d82e3b 100644
--- a/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs
+++ b/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs
@@ -51,12 +51,14 @@
 
         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 = (int)(input.ReadRawVarint32() & int.MaxValue);
+                int length = input.ReadLength();
                 if (length > 0)
                 {
                     int oldLimit = input.PushLimit(length);
@@ -125,7 +127,6 @@
 
         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;
@@ -172,9 +173,9 @@
 
         private void EnsureSize(int size)
         {
-            size = Math.Max(size, MinArraySize);
             if (array.Length < size)
             {
+                size = Math.Max(size, MinArraySize);
                 int newSize = Math.Max(array.Length * 2, size);
                 var tmp = new T[newSize];
                 Array.Copy(array, 0, tmp, 0, array.Length);
diff --git a/csharp/src/ProtocolBuffers/FieldCodec.cs b/csharp/src/ProtocolBuffers/FieldCodec.cs
index d3fc2f7..f075dbb 100644
--- a/csharp/src/ProtocolBuffers/FieldCodec.cs
+++ b/csharp/src/ProtocolBuffers/FieldCodec.cs
@@ -8,6 +8,7 @@
     /// </summary>
     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)
         {
             return new FieldCodec<string>(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag); 
@@ -84,7 +85,7 @@
         }
 
         // 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)
         {
             return new FieldCodec<T>(input => fromInt32(
diff --git a/csharp/src/ProtocolBuffers/MessageExtensions.cs b/csharp/src/ProtocolBuffers/MessageExtensions.cs
index 57cecfd..253c18a 100644
--- a/csharp/src/ProtocolBuffers/MessageExtensions.cs
+++ b/csharp/src/ProtocolBuffers/MessageExtensions.cs
@@ -38,7 +38,7 @@
         {
             ThrowHelper.ThrowIfNull(message, "message");
             ThrowHelper.ThrowIfNull(input, "input");
-            int size = (int)CodedInputStream.ReadRawVarint32(input);
+            int size = (int) CodedInputStream.ReadRawVarint32(input);
             Stream limitedStream = new LimitedInputStream(input, size);
             message.MergeFrom(limitedStream);
         }
diff --git a/csharp/src/ProtocolBuffers/Properties/AssemblyInfo.cs b/csharp/src/ProtocolBuffers/Properties/AssemblyInfo.cs
index 806bd5d..27ccddb 100644
--- a/csharp/src/ProtocolBuffers/Properties/AssemblyInfo.cs
+++ b/csharp/src/ProtocolBuffers/Properties/AssemblyInfo.cs
@@ -61,6 +61,13 @@
 

 [assembly: AssemblyVersion("2.4.1.555")]

 

+[assembly: InternalsVisibleTo("Google.Protobuf.Test, PublicKey=" +

+    "00240000048000009400000006020000002400005253413100040000110000003b4611704c5379" +

+    "39c3e0fbe9447dd6fa5462507f9dd4fd9fbf0712457e415b037da6d2c4eb5d2c7d29c86380af68" +

+    "7cf400401bb183f2a70bd3b631c1fcb7db8aa66c766694a9fb53fa765df6303104da8c978f3b6d" +

+    "53909cd30685b8bc9922c726cd82b5995e9e2cfca6df7a2d189d851492e49f4b76f269ce6dfd08" +

+    "c34a7d98")]

+

 #if !NOFILEVERSION

 [assembly: AssemblyFileVersion("2.4.1.555")]

 #endif

diff --git a/csharp/src/ProtocolBuffers/WireFormat.cs b/csharp/src/ProtocolBuffers/WireFormat.cs
index 221ffef..974665f 100644
--- a/csharp/src/ProtocolBuffers/WireFormat.cs
+++ b/csharp/src/ProtocolBuffers/WireFormat.cs
@@ -53,13 +53,13 @@
         #region Fixed sizes.

 

         // 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

 

@@ -72,22 +72,7 @@
             EndGroup = 4,

             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 uint TagTypeMask = (1 << TagTypeBits) - 1;

 

@@ -120,7 +105,6 @@
             return (uint) (fieldNumber << TagTypeBits) | (uint) wireType;

         }

 

-#if !LITE

         public static uint MakeTag(FieldDescriptor field)

         {

             return MakeTag(field.FieldNumber, GetWireType(field));

@@ -135,8 +119,6 @@
             return descriptor.IsPacked ? WireType.LengthDelimited : GetWireType(descriptor.FieldType);

         }

 

-#endif

-

         /// <summary>

         /// 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.

@@ -177,7 +159,7 @@
                 case FieldType.Enum:

                     return WireType.Varint;

                 default:

-                    throw new ArgumentOutOfRangeException("No such field type");

+                    throw new ArgumentOutOfRangeException("fieldType", "No such field type");

             }

         }

     }