Sync from Piper @439400376

PROTOBUF_SYNC_PIPER
diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml
index 497e811..41dc715 100644
--- a/.github/workflows/codespell.yml
+++ b/.github/workflows/codespell.yml
@@ -13,4 +13,4 @@
         with:
           check_filenames: true
           skip: ./.git,./conformance/third_party,*.snk,*.pb,*.pb.cc,*.pb.h,./src/google/protobuf/testdata,./objectivec/Tests,./python/compatibility_tests/v2.5.0/tests/google/protobuf/internal,./.github/workflows/codespell.yml
-          ignore_words_list: "alow,alse,ba,cleare,copyable,cloneable,dedup,dur,errorprone,files',fo,fundementals,hel,importd,inout,leapyear,nd,nin,ois,ons,parseable,process',te,testof,ue,unparseable,wasn,wee,gae,keyserver,objext,od,optin,sur"
+          ignore_words_list: "alow,alse,ba,cleare,copyable,cloneable,dedup,dur,errorprone,files',fo,fundementals,hel,importd,inout,leapyear,nd,nin,ois,ons,parseable,process',te,testof,ue,unparseable,wasn,wee,gae,keyserver,objext,od,optin,streem,sur"
diff --git a/.gitignore b/.gitignore
index 4fe3edd..8b76496 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,7 +46,7 @@
 any_test.pb.*
 map*unittest.pb.*
 unittest*.pb.*
-cpp_test*.pb.*
+src/google/protobuf/compiler/cpp/test*.pb.*
 src/google/protobuf/util/**/*.pb.cc
 src/google/protobuf/util/**/*.pb.h
 
@@ -89,6 +89,10 @@
 # Windows native output.
 cmake/build
 build_msvc
+# Directories suggested by cmake/README.md
+/debug/
+/solution/
+/release/
 
 # NuGet packages: we want the repository configuration, but not the
 # packages themselves.
diff --git a/BUILD b/BUILD
index 887e5e3..5de4aa3 100644
--- a/BUILD
+++ b/BUILD
@@ -159,6 +159,7 @@
         "src/google/protobuf/any_lite.cc",
         "src/google/protobuf/arena.cc",
         "src/google/protobuf/arenastring.cc",
+        "src/google/protobuf/arenaz_sampler.cc",
         "src/google/protobuf/extension_set.cc",
         "src/google/protobuf/generated_enum_util.cc",
         "src/google/protobuf/generated_message_tctable_lite.cc",
@@ -419,21 +420,21 @@
         # AUTOGEN(protoc_lib_srcs)
         "src/google/protobuf/compiler/code_generator.cc",
         "src/google/protobuf/compiler/command_line_interface.cc",
-        "src/google/protobuf/compiler/cpp/cpp_enum.cc",
-        "src/google/protobuf/compiler/cpp/cpp_enum_field.cc",
-        "src/google/protobuf/compiler/cpp/cpp_extension.cc",
-        "src/google/protobuf/compiler/cpp/cpp_field.cc",
-        "src/google/protobuf/compiler/cpp/cpp_file.cc",
-        "src/google/protobuf/compiler/cpp/cpp_generator.cc",
-        "src/google/protobuf/compiler/cpp/cpp_helpers.cc",
-        "src/google/protobuf/compiler/cpp/cpp_map_field.cc",
-        "src/google/protobuf/compiler/cpp/cpp_message.cc",
-        "src/google/protobuf/compiler/cpp/cpp_message_field.cc",
-        "src/google/protobuf/compiler/cpp/cpp_padding_optimizer.cc",
-        "src/google/protobuf/compiler/cpp/cpp_parse_function_generator.cc",
-        "src/google/protobuf/compiler/cpp/cpp_primitive_field.cc",
-        "src/google/protobuf/compiler/cpp/cpp_service.cc",
-        "src/google/protobuf/compiler/cpp/cpp_string_field.cc",
+        "src/google/protobuf/compiler/cpp/enum.cc",
+        "src/google/protobuf/compiler/cpp/enum_field.cc",
+        "src/google/protobuf/compiler/cpp/extension.cc",
+        "src/google/protobuf/compiler/cpp/field.cc",
+        "src/google/protobuf/compiler/cpp/file.cc",
+        "src/google/protobuf/compiler/cpp/generator.cc",
+        "src/google/protobuf/compiler/cpp/helpers.cc",
+        "src/google/protobuf/compiler/cpp/map_field.cc",
+        "src/google/protobuf/compiler/cpp/message.cc",
+        "src/google/protobuf/compiler/cpp/message_field.cc",
+        "src/google/protobuf/compiler/cpp/padding_optimizer.cc",
+        "src/google/protobuf/compiler/cpp/parse_function_generator.cc",
+        "src/google/protobuf/compiler/cpp/primitive_field.cc",
+        "src/google/protobuf/compiler/cpp/service.cc",
+        "src/google/protobuf/compiler/cpp/string_field.cc",
         "src/google/protobuf/compiler/csharp/csharp_doc_comment.cc",
         "src/google/protobuf/compiler/csharp/csharp_enum.cc",
         "src/google/protobuf/compiler/csharp/csharp_enum_field.cc",
@@ -450,35 +451,35 @@
         "src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc",
         "src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc",
         "src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc",
-        "src/google/protobuf/compiler/java/java_context.cc",
-        "src/google/protobuf/compiler/java/java_doc_comment.cc",
-        "src/google/protobuf/compiler/java/java_enum.cc",
-        "src/google/protobuf/compiler/java/java_enum_field.cc",
-        "src/google/protobuf/compiler/java/java_enum_field_lite.cc",
-        "src/google/protobuf/compiler/java/java_enum_lite.cc",
-        "src/google/protobuf/compiler/java/java_extension.cc",
-        "src/google/protobuf/compiler/java/java_extension_lite.cc",
-        "src/google/protobuf/compiler/java/java_field.cc",
-        "src/google/protobuf/compiler/java/java_file.cc",
-        "src/google/protobuf/compiler/java/java_generator.cc",
-        "src/google/protobuf/compiler/java/java_generator_factory.cc",
-        "src/google/protobuf/compiler/java/java_helpers.cc",
-        "src/google/protobuf/compiler/java/java_kotlin_generator.cc",
-        "src/google/protobuf/compiler/java/java_map_field.cc",
-        "src/google/protobuf/compiler/java/java_map_field_lite.cc",
-        "src/google/protobuf/compiler/java/java_message.cc",
-        "src/google/protobuf/compiler/java/java_message_builder.cc",
-        "src/google/protobuf/compiler/java/java_message_builder_lite.cc",
-        "src/google/protobuf/compiler/java/java_message_field.cc",
-        "src/google/protobuf/compiler/java/java_message_field_lite.cc",
-        "src/google/protobuf/compiler/java/java_message_lite.cc",
-        "src/google/protobuf/compiler/java/java_name_resolver.cc",
-        "src/google/protobuf/compiler/java/java_primitive_field.cc",
-        "src/google/protobuf/compiler/java/java_primitive_field_lite.cc",
-        "src/google/protobuf/compiler/java/java_service.cc",
-        "src/google/protobuf/compiler/java/java_shared_code_generator.cc",
-        "src/google/protobuf/compiler/java/java_string_field.cc",
-        "src/google/protobuf/compiler/java/java_string_field_lite.cc",
+        "src/google/protobuf/compiler/java/context.cc",
+        "src/google/protobuf/compiler/java/doc_comment.cc",
+        "src/google/protobuf/compiler/java/enum.cc",
+        "src/google/protobuf/compiler/java/enum_field.cc",
+        "src/google/protobuf/compiler/java/enum_field_lite.cc",
+        "src/google/protobuf/compiler/java/enum_lite.cc",
+        "src/google/protobuf/compiler/java/extension.cc",
+        "src/google/protobuf/compiler/java/extension_lite.cc",
+        "src/google/protobuf/compiler/java/field.cc",
+        "src/google/protobuf/compiler/java/file.cc",
+        "src/google/protobuf/compiler/java/generator.cc",
+        "src/google/protobuf/compiler/java/generator_factory.cc",
+        "src/google/protobuf/compiler/java/helpers.cc",
+        "src/google/protobuf/compiler/java/kotlin_generator.cc",
+        "src/google/protobuf/compiler/java/map_field.cc",
+        "src/google/protobuf/compiler/java/map_field_lite.cc",
+        "src/google/protobuf/compiler/java/message.cc",
+        "src/google/protobuf/compiler/java/message_builder.cc",
+        "src/google/protobuf/compiler/java/message_builder_lite.cc",
+        "src/google/protobuf/compiler/java/message_field.cc",
+        "src/google/protobuf/compiler/java/message_field_lite.cc",
+        "src/google/protobuf/compiler/java/message_lite.cc",
+        "src/google/protobuf/compiler/java/name_resolver.cc",
+        "src/google/protobuf/compiler/java/primitive_field.cc",
+        "src/google/protobuf/compiler/java/primitive_field_lite.cc",
+        "src/google/protobuf/compiler/java/service.cc",
+        "src/google/protobuf/compiler/java/shared_code_generator.cc",
+        "src/google/protobuf/compiler/java/string_field.cc",
+        "src/google/protobuf/compiler/java/string_field_lite.cc",
         "src/google/protobuf/compiler/js/js_generator.cc",
         "src/google/protobuf/compiler/js/well_known_types_embed.cc",
         "src/google/protobuf/compiler/objectivec/objectivec_enum.cc",
@@ -496,9 +497,9 @@
         "src/google/protobuf/compiler/php/php_generator.cc",
         "src/google/protobuf/compiler/plugin.cc",
         "src/google/protobuf/compiler/plugin.pb.cc",
-        "src/google/protobuf/compiler/python/python_generator.cc",
-        "src/google/protobuf/compiler/python/python_helpers.cc",
-        "src/google/protobuf/compiler/python/python_pyi_generator.cc",
+        "src/google/protobuf/compiler/python/generator.cc",
+        "src/google/protobuf/compiler/python/helpers.cc",
+        "src/google/protobuf/compiler/python/pyi_generator.cc",
         "src/google/protobuf/compiler/ruby/ruby_generator.cc",
         "src/google/protobuf/compiler/subprocess.cc",
         "src/google/protobuf/compiler/zip_writer.cc",
@@ -605,8 +606,8 @@
 RELATIVE_TEST_PROTOS = [
     # AUTOGEN(test_protos)
     "google/protobuf/any_test.proto",
-    "google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto",
-    "google/protobuf/compiler/cpp/cpp_test_large_enum_value.proto",
+    "google/protobuf/compiler/cpp/test_bad_identifiers.proto",
+    "google/protobuf/compiler/cpp/test_large_enum_value.proto",
     "google/protobuf/map_proto2_unittest.proto",
     "google/protobuf/map_unittest.proto",
     "google/protobuf/unittest.proto",
@@ -765,23 +766,24 @@
         "src/google/protobuf/any_test.cc",
         "src/google/protobuf/arena_unittest.cc",
         "src/google/protobuf/arenastring_unittest.cc",
+        "src/google/protobuf/arenaz_sampler_test.cc",
         "src/google/protobuf/compiler/annotation_test_util.cc",
         "src/google/protobuf/compiler/command_line_interface_unittest.cc",
-        "src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc",
-        "src/google/protobuf/compiler/cpp/cpp_move_unittest.cc",
-        "src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc",
-        "src/google/protobuf/compiler/cpp/cpp_unittest.cc",
-        "src/google/protobuf/compiler/cpp/cpp_unittest.inc",
+        "src/google/protobuf/compiler/cpp/bootstrap_unittest.cc",
         "src/google/protobuf/compiler/cpp/metadata_test.cc",
+        "src/google/protobuf/compiler/cpp/move_unittest.cc",
+        "src/google/protobuf/compiler/cpp/plugin_unittest.cc",
+        "src/google/protobuf/compiler/cpp/unittest.cc",
+        "src/google/protobuf/compiler/cpp/unittest.inc",
         "src/google/protobuf/compiler/csharp/csharp_bootstrap_unittest.cc",
         "src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc",
         "src/google/protobuf/compiler/importer_unittest.cc",
-        "src/google/protobuf/compiler/java/java_doc_comment_unittest.cc",
-        "src/google/protobuf/compiler/java/java_plugin_unittest.cc",
+        "src/google/protobuf/compiler/java/doc_comment_unittest.cc",
+        "src/google/protobuf/compiler/java/plugin_unittest.cc",
         "src/google/protobuf/compiler/mock_code_generator.cc",
         "src/google/protobuf/compiler/objectivec/objectivec_helpers_unittest.cc",
         "src/google/protobuf/compiler/parser_unittest.cc",
-        "src/google/protobuf/compiler/python/python_plugin_unittest.cc",
+        "src/google/protobuf/compiler/python/plugin_unittest.cc",
         "src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc",
         "src/google/protobuf/descriptor_database_unittest.cc",
         "src/google/protobuf/descriptor_unittest.cc",
@@ -789,6 +791,7 @@
         "src/google/protobuf/dynamic_message_unittest.cc",
         "src/google/protobuf/extension_set_unittest.cc",
         "src/google/protobuf/generated_message_reflection_unittest.cc",
+        "src/google/protobuf/generated_message_tctable_lite_test.cc",
         "src/google/protobuf/inlined_string_field_unittest.cc",
         "src/google/protobuf/io/coded_stream_unittest.cc",
         "src/google/protobuf/io/io_win32_unittest.cc",
diff --git a/CHANGES.txt b/CHANGES.txt
index 71b4927..873c0dc 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -17,8 +17,11 @@
 
   Ruby
   * Dropped Ruby 2.3 and 2.4 support for CI and releases. (#9311)
-  * Message.decode/encode: Add max_recursion_depth option (#9218)
-  * Rename max_recursion_depth to recursion_limit (#9486)
+  * Added Ruby 3.1 support for CI and releases (#9566).
+  * Message.decode/encode: Add recursion_limit option (#9218/#9486)
+  * Allocate with xrealloc()/xfree() so message allocation is visible to the
+    Ruby GC.  In certain tests this leads to much lower memory usage due to more
+    frequent GC runs (#9586).
   * Fix conversion of singleton classes in Ruby (#9342)
   * Suppress warning for intentional circular require (#9556)
   * JSON will now output shorter strings for double and float fields when possible
diff --git a/Protobuf.podspec b/Protobuf.podspec
index 3bd7147..92c53c9 100644
--- a/Protobuf.podspec
+++ b/Protobuf.podspec
@@ -5,7 +5,7 @@
 # dependent projects use the :git notation to refer to the library.
 Pod::Spec.new do |s|
   s.name     = 'Protobuf'
-  s.version  = '3.20.0-rc1'
+  s.version  = '3.20.0'
   s.summary  = 'Protocol Buffers v.3 runtime library for Objective-C.'
   s.homepage = 'https://github.com/protocolbuffers/protobuf'
   s.license  = 'BSD-3-Clause'
diff --git a/cmake/tests.cmake b/cmake/tests.cmake
index a24b153..9c60705 100644
--- a/cmake/tests.cmake
+++ b/cmake/tests.cmake
@@ -145,6 +145,7 @@
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/annotation_test_util.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/command_line_interface_unittest.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/bootstrap_unittest.cc
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/message_size_unittest.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/metadata_test.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/move_unittest.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/plugin_unittest.cc
diff --git a/conformance/binary_json_conformance_suite.cc b/conformance/binary_json_conformance_suite.cc
index b12978f..9b1548d 100644
--- a/conformance/binary_json_conformance_suite.cc
+++ b/conformance/binary_json_conformance_suite.cc
@@ -2226,11 +2226,11 @@
       "optional_aliased_enum: ALIAS_BAZ");
   RunValidJsonTest(
       "EnumFieldWithAliasUseAlias", REQUIRED,
-      R"({"optionalAliasedEnum": "QUX"})",
+      R"({"optionalAliasedEnum": "MOO"})",
       "optional_aliased_enum: ALIAS_BAZ");
   RunValidJsonTest(
       "EnumFieldWithAliasLowerCase", REQUIRED,
-      R"({"optionalAliasedEnum": "qux"})",
+      R"({"optionalAliasedEnum": "moo"})",
       "optional_aliased_enum: ALIAS_BAZ");
   RunValidJsonTest(
       "EnumFieldWithAliasDifferentCase", REQUIRED,
diff --git a/csharp/.editorconfig b/csharp/.editorconfig
new file mode 100644
index 0000000..5e2afd0
--- /dev/null
+++ b/csharp/.editorconfig
@@ -0,0 +1,17 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# New line preferences
+end_of_line = lf
+insert_final_newline = false
+trim_trailing_whitespace = true
\ No newline at end of file
diff --git a/csharp/Google.Protobuf.Tools.nuspec b/csharp/Google.Protobuf.Tools.nuspec
index 86f12c9..21b6a62 100644
--- a/csharp/Google.Protobuf.Tools.nuspec
+++ b/csharp/Google.Protobuf.Tools.nuspec
@@ -5,7 +5,7 @@
     <title>Google Protocol Buffers tools</title>
     <summary>Tools for Protocol Buffers - Google's data interchange format.</summary>
     <description>See project site for more info.</description>
-    <version>3.20.0-rc1</version>
+    <version>3.20.0</version>
     <authors>Google Inc.</authors>
     <owners>protobuf-packages</owners>
     <licenseUrl>https://github.com/protocolbuffers/protobuf/blob/master/LICENSE</licenseUrl>
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/ByteStringTest.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/ByteStringTest.cs
index 685e130..8935b78 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/ByteStringTest.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/ByteStringTest.cs
@@ -1,171 +1,171 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.Text;

-using NUnit.Framework;

-

-namespace Google.Protobuf

-{

-    public class ByteStringTest

-    {

-        [Test]

-        public void Equality()

-        {

-            ByteString b1 = ByteString.CopyFrom(1, 2, 3);

-            ByteString b2 = ByteString.CopyFrom(1, 2, 3);

-            ByteString b3 = ByteString.CopyFrom(1, 2, 4);

-            ByteString b4 = ByteString.CopyFrom(1, 2, 3, 4);

-            EqualityTester.AssertEquality(b1, b1);

-            EqualityTester.AssertEquality(b1, b2);

-            EqualityTester.AssertInequality(b1, b3);

-            EqualityTester.AssertInequality(b1, b4);

-            EqualityTester.AssertInequality(b1, null);

-#pragma warning disable 1718 // Deliberately calling ==(b1, b1) and !=(b1, b1)

-            Assert.IsTrue(b1 == b1);

-            Assert.IsTrue(b1 == b2);

-            Assert.IsFalse(b1 == b3);

-            Assert.IsFalse(b1 == b4);

-            Assert.IsFalse(b1 == null);

-            Assert.IsTrue((ByteString) null == null);

-            Assert.IsFalse(b1 != b1);

-            Assert.IsFalse(b1 != b2);

-#pragma warning disable 1718

-            Assert.IsTrue(b1 != b3);

-            Assert.IsTrue(b1 != b4);

-            Assert.IsTrue(b1 != null);

-            Assert.IsFalse((ByteString) null != null);

-        }

-

-        [Test]

-        public void EmptyByteStringHasZeroSize()

-        {

-            Assert.AreEqual(0, ByteString.Empty.Length);

-        }

-

-        [Test]

-        public void CopyFromStringWithExplicitEncoding()

-        {

-            ByteString bs = ByteString.CopyFrom("AB", Encoding.Unicode);

-            Assert.AreEqual(4, bs.Length);

-            Assert.AreEqual(65, bs[0]);

-            Assert.AreEqual(0, bs[1]);

-            Assert.AreEqual(66, bs[2]);

-            Assert.AreEqual(0, bs[3]);

-        }

-

-        [Test]

-        public void IsEmptyWhenEmpty()

-        {

-            Assert.IsTrue(ByteString.CopyFromUtf8("").IsEmpty);

-        }

-

-        [Test]

-        public void IsEmptyWhenNotEmpty()

-        {

-            Assert.IsFalse(ByteString.CopyFromUtf8("X").IsEmpty);

-        }

-

-        [Test]

-        public void CopyFromByteArrayCopiesContents()

-        {

-            byte[] data = new byte[1];

-            data[0] = 10;

-            ByteString bs = ByteString.CopyFrom(data);

-            Assert.AreEqual(10, bs[0]);

-            data[0] = 5;

-            Assert.AreEqual(10, bs[0]);

-        }

-

-        [Test]

-        public void ToByteArrayCopiesContents()

-        {

-            ByteString bs = ByteString.CopyFromUtf8("Hello");

-            byte[] data = bs.ToByteArray();

-            Assert.AreEqual((byte)'H', data[0]);

-            Assert.AreEqual((byte)'H', bs[0]);

-            data[0] = 0;

-            Assert.AreEqual(0, data[0]);

-            Assert.AreEqual((byte)'H', bs[0]);

-        }

-

-        [Test]

-        public void CopyFromUtf8UsesUtf8()

-        {

-            ByteString bs = ByteString.CopyFromUtf8("\u20ac");

-            Assert.AreEqual(3, bs.Length);

-            Assert.AreEqual(0xe2, bs[0]);

-            Assert.AreEqual(0x82, bs[1]);

-            Assert.AreEqual(0xac, bs[2]);

-        }

-

-        [Test]

-        public void CopyFromPortion()

-        {

-            byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};

-            ByteString bs = ByteString.CopyFrom(data, 2, 3);

-            Assert.AreEqual(3, bs.Length);

-            Assert.AreEqual(2, bs[0]);

-            Assert.AreEqual(3, bs[1]);

-        }

-

-        [Test]

-        public void ToStringUtf8()

-        {

-            ByteString bs = ByteString.CopyFromUtf8("\u20ac");

-            Assert.AreEqual("\u20ac", bs.ToStringUtf8());

-        }

-

-        [Test]

-        public void ToStringWithExplicitEncoding()

-        {

-            ByteString bs = ByteString.CopyFrom("\u20ac", Encoding.Unicode);

-            Assert.AreEqual("\u20ac", bs.ToString(Encoding.Unicode));

-        }

-

-        [Test]

-        public void FromBase64_WithText()

-        {

-            byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};

-            string base64 = Convert.ToBase64String(data);

-            ByteString bs = ByteString.FromBase64(base64);

-            Assert.AreEqual(data, bs.ToByteArray());

-        }

-

-        [Test]

-        public void FromBase64_Empty()

-        {

-            // Optimization which also fixes issue 61.

-            Assert.AreSame(ByteString.Empty, ByteString.FromBase64(""));

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Text;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+    public class ByteStringTest
+    {
+        [Test]
+        public void Equality()
+        {
+            ByteString b1 = ByteString.CopyFrom(1, 2, 3);
+            ByteString b2 = ByteString.CopyFrom(1, 2, 3);
+            ByteString b3 = ByteString.CopyFrom(1, 2, 4);
+            ByteString b4 = ByteString.CopyFrom(1, 2, 3, 4);
+            EqualityTester.AssertEquality(b1, b1);
+            EqualityTester.AssertEquality(b1, b2);
+            EqualityTester.AssertInequality(b1, b3);
+            EqualityTester.AssertInequality(b1, b4);
+            EqualityTester.AssertInequality(b1, null);
+#pragma warning disable 1718 // Deliberately calling ==(b1, b1) and !=(b1, b1)
+            Assert.IsTrue(b1 == b1);
+            Assert.IsTrue(b1 == b2);
+            Assert.IsFalse(b1 == b3);
+            Assert.IsFalse(b1 == b4);
+            Assert.IsFalse(b1 == null);
+            Assert.IsTrue((ByteString) null == null);
+            Assert.IsFalse(b1 != b1);
+            Assert.IsFalse(b1 != b2);
+#pragma warning disable 1718
+            Assert.IsTrue(b1 != b3);
+            Assert.IsTrue(b1 != b4);
+            Assert.IsTrue(b1 != null);
+            Assert.IsFalse((ByteString) null != null);
+        }
+
+        [Test]
+        public void EmptyByteStringHasZeroSize()
+        {
+            Assert.AreEqual(0, ByteString.Empty.Length);
+        }
+
+        [Test]
+        public void CopyFromStringWithExplicitEncoding()
+        {
+            ByteString bs = ByteString.CopyFrom("AB", Encoding.Unicode);
+            Assert.AreEqual(4, bs.Length);
+            Assert.AreEqual(65, bs[0]);
+            Assert.AreEqual(0, bs[1]);
+            Assert.AreEqual(66, bs[2]);
+            Assert.AreEqual(0, bs[3]);
+        }
+
+        [Test]
+        public void IsEmptyWhenEmpty()
+        {
+            Assert.IsTrue(ByteString.CopyFromUtf8("").IsEmpty);
+        }
+
+        [Test]
+        public void IsEmptyWhenNotEmpty()
+        {
+            Assert.IsFalse(ByteString.CopyFromUtf8("X").IsEmpty);
+        }
+
+        [Test]
+        public void CopyFromByteArrayCopiesContents()
+        {
+            byte[] data = new byte[1];
+            data[0] = 10;
+            ByteString bs = ByteString.CopyFrom(data);
+            Assert.AreEqual(10, bs[0]);
+            data[0] = 5;
+            Assert.AreEqual(10, bs[0]);
+        }
+
+        [Test]
+        public void ToByteArrayCopiesContents()
+        {
+            ByteString bs = ByteString.CopyFromUtf8("Hello");
+            byte[] data = bs.ToByteArray();
+            Assert.AreEqual((byte)'H', data[0]);
+            Assert.AreEqual((byte)'H', bs[0]);
+            data[0] = 0;
+            Assert.AreEqual(0, data[0]);
+            Assert.AreEqual((byte)'H', bs[0]);
+        }
+
+        [Test]
+        public void CopyFromUtf8UsesUtf8()
+        {
+            ByteString bs = ByteString.CopyFromUtf8("\u20ac");
+            Assert.AreEqual(3, bs.Length);
+            Assert.AreEqual(0xe2, bs[0]);
+            Assert.AreEqual(0x82, bs[1]);
+            Assert.AreEqual(0xac, bs[2]);
+        }
+
+        [Test]
+        public void CopyFromPortion()
+        {
+            byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
+            ByteString bs = ByteString.CopyFrom(data, 2, 3);
+            Assert.AreEqual(3, bs.Length);
+            Assert.AreEqual(2, bs[0]);
+            Assert.AreEqual(3, bs[1]);
+        }
+
+        [Test]
+        public void ToStringUtf8()
+        {
+            ByteString bs = ByteString.CopyFromUtf8("\u20ac");
+            Assert.AreEqual("\u20ac", bs.ToStringUtf8());
+        }
+
+        [Test]
+        public void ToStringWithExplicitEncoding()
+        {
+            ByteString bs = ByteString.CopyFrom("\u20ac", Encoding.Unicode);
+            Assert.AreEqual("\u20ac", bs.ToString(Encoding.Unicode));
+        }
+
+        [Test]
+        public void FromBase64_WithText()
+        {
+            byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
+            string base64 = Convert.ToBase64String(data);
+            ByteString bs = ByteString.FromBase64(base64);
+            Assert.AreEqual(data, bs.ToByteArray());
+        }
+
+        [Test]
+        public void FromBase64_Empty()
+        {
+            // Optimization which also fixes issue 61.
+            Assert.AreSame(ByteString.Empty, ByteString.FromBase64(""));
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedInputStreamTest.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedInputStreamTest.cs
index 11d06f1..5ac8c71 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedInputStreamTest.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedInputStreamTest.cs
@@ -1,598 +1,598 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.IO;

-using Google.Protobuf.TestProtos;

-using NUnit.Framework;

-

-namespace Google.Protobuf

-{

-    public class CodedInputStreamTest

-    {

-        /// <summary>

-        /// Helper to construct a byte array from a bunch of bytes.  The inputs are

-        /// actually ints so that I can use hex notation and not get stupid errors

-        /// about precision.

-        /// </summary>

-        private static byte[] Bytes(params int[] bytesAsInts)

-        {

-            byte[] bytes = new byte[bytesAsInts.Length];

-            for (int i = 0; i < bytesAsInts.Length; i++)

-            {

-                bytes[i] = (byte) bytesAsInts[i];

-            }

-            return bytes;

-        }

-

-        /// <summary>

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

-        /// </summary>

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

-        {

-            CodedInputStream input = new CodedInputStream(data);

-            Assert.AreEqual((uint) value, input.ReadRawVarint32());

-

-            input = new CodedInputStream(data);

-            Assert.AreEqual(value, input.ReadRawVarint64());

-            Assert.IsTrue(input.IsAtEnd);

-

-            // Try different block sizes.

-            for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)

-            {

-                input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));

-                Assert.AreEqual((uint) value, input.ReadRawVarint32());

-

-                input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));

-                Assert.AreEqual(value, input.ReadRawVarint64());

-                Assert.IsTrue(input.IsAtEnd);

-            }

-

-            // Try reading directly from a MemoryStream. We want to verify that it

-            // doesn't read past the end of the input, so write an extra byte - this

-            // lets us test the position at the end.

-            MemoryStream memoryStream = new MemoryStream();

-            memoryStream.Write(data, 0, data.Length);

-            memoryStream.WriteByte(0);

-            memoryStream.Position = 0;

-            Assert.AreEqual((uint) value, CodedInputStream.ReadRawVarint32(memoryStream));

-            Assert.AreEqual(data.Length, memoryStream.Position);

-        }

-

-        /// <summary>

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

-        /// expects them to fail with an InvalidProtocolBufferException whose

-        /// description matches the given one.

-        /// </summary>

-        private static void AssertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)

-        {

-            CodedInputStream input = new CodedInputStream(data);

-            var exception = Assert.Throws<InvalidProtocolBufferException>(() => input.ReadRawVarint32());

-            Assert.AreEqual(expected.Message, exception.Message);

-

-            input = new CodedInputStream(data);

-            exception = Assert.Throws<InvalidProtocolBufferException>(() => input.ReadRawVarint64());

-            Assert.AreEqual(expected.Message, exception.Message);

-

-            // Make sure we get the same error when reading directly from a Stream.

-            exception = Assert.Throws<InvalidProtocolBufferException>(() => CodedInputStream.ReadRawVarint32(new MemoryStream(data)));

-            Assert.AreEqual(expected.Message, exception.Message);

-        }

-

-        [Test]

-        public void ReadVarint()

-        {

-            AssertReadVarint(Bytes(0x00), 0);

-            AssertReadVarint(Bytes(0x01), 1);

-            AssertReadVarint(Bytes(0x7f), 127);

-            // 14882

-            AssertReadVarint(Bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));

-            // 2961488830

-            AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),

-                             (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |

-                             (0x0bL << 28));

-

-            // 64-bit

-            // 7256456126

-            AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),

-                             (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |

-                             (0x1bL << 28));

-            // 41256202580718336

-            AssertReadVarint(Bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),

-                             (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |

-                             (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));

-            // 11964378330978735131

-            AssertReadVarint(Bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),

-                             (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |

-                             (0x3bUL << 28) | (0x56UL << 35) | (0x00UL << 42) |

-                             (0x05UL << 49) | (0x26UL << 56) | (0x01UL << 63));

-

-            // Failures

-            AssertReadVarintFailure(

-                InvalidProtocolBufferException.MalformedVarint(),

-                Bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,

-                      0x00));

-            AssertReadVarintFailure(

-                InvalidProtocolBufferException.TruncatedMessage(),

-                Bytes(0x80));

-        }

-

-        /// <summary>

-        /// Parses the given bytes using ReadRawLittleEndian32() and checks

-        /// that the result matches the given value.

-        /// </summary>

-        private static void AssertReadLittleEndian32(byte[] data, uint value)

-        {

-            CodedInputStream input = new CodedInputStream(data);

-            Assert.AreEqual(value, input.ReadRawLittleEndian32());

-            Assert.IsTrue(input.IsAtEnd);

-

-            // Try different block sizes.

-            for (int blockSize = 1; blockSize <= 16; blockSize *= 2)

-            {

-                input = new CodedInputStream(

-                    new SmallBlockInputStream(data, blockSize));

-                Assert.AreEqual(value, input.ReadRawLittleEndian32());

-                Assert.IsTrue(input.IsAtEnd);

-            }

-        }

-

-        /// <summary>

-        /// Parses the given bytes using ReadRawLittleEndian64() and checks

-        /// that the result matches the given value.

-        /// </summary>

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

-        {

-            CodedInputStream input = new CodedInputStream(data);

-            Assert.AreEqual(value, input.ReadRawLittleEndian64());

-            Assert.IsTrue(input.IsAtEnd);

-

-            // Try different block sizes.

-            for (int blockSize = 1; blockSize <= 16; blockSize *= 2)

-            {

-                input = new CodedInputStream(

-                    new SmallBlockInputStream(data, blockSize));

-                Assert.AreEqual(value, input.ReadRawLittleEndian64());

-                Assert.IsTrue(input.IsAtEnd);

-            }

-        }

-

-        [Test]

-        public void ReadLittleEndian()

-        {

-            AssertReadLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);

-            AssertReadLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);

-

-            AssertReadLittleEndian64(Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),

-                                     0x123456789abcdef0L);

-            AssertReadLittleEndian64(

-                Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678UL);

-        }

-

-        [Test]

-        public void DecodeZigZag32()

-        {

-            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(0));

-            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(1));

-            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(2));

-            Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag32(3));

-            Assert.AreEqual(0x3FFFFFFF, ParsingPrimitives.DecodeZigZag32(0x7FFFFFFE));

-            Assert.AreEqual(unchecked((int) 0xC0000000), ParsingPrimitives.DecodeZigZag32(0x7FFFFFFF));

-            Assert.AreEqual(0x7FFFFFFF, ParsingPrimitives.DecodeZigZag32(0xFFFFFFFE));

-            Assert.AreEqual(unchecked((int) 0x80000000), ParsingPrimitives.DecodeZigZag32(0xFFFFFFFF));

-        }

-

-        [Test]

-        public void DecodeZigZag64()

-        {

-            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(0));

-            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(1));

-            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(2));

-            Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag64(3));

-            Assert.AreEqual(0x000000003FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFEL));

-            Assert.AreEqual(unchecked((long) 0xFFFFFFFFC0000000L), ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFFL));

-            Assert.AreEqual(0x000000007FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFEL));

-            Assert.AreEqual(unchecked((long) 0xFFFFFFFF80000000L), ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFFL));

-            Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));

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

-        }

-        

-        [Test]

-        public void ReadWholeMessage_VaryingBlockSizes()

-        {

-            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();

-

-            byte[] rawBytes = message.ToByteArray();

-            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.Parser.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));

-                Assert.AreEqual(message, message2);

-            }

-        }

-                

-        [Test]

-        public void ReadHugeBlob()

-        {

-            // Allocate and initialize a 1MB blob.

-            byte[] blob = new byte[1 << 20];

-            for (int i = 0; i < blob.Length; i++)

-            {

-                blob[i] = (byte) i;

-            }

-

-            // Make a message containing it.

-            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.Parser.ParseFrom(message.ToByteString());

-

-            Assert.AreEqual(message, message2);

-        }

-

-        [Test]

-        public void ReadMaliciouslyLargeBlob()

-        {

-            MemoryStream ms = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(ms);

-

-            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteRawVarint32(tag);

-            output.WriteRawVarint32(0x7FFFFFFF);

-            output.WriteRawBytes(new byte[32]); // Pad with a few random bytes.

-            output.Flush();

-            ms.Position = 0;

-

-            CodedInputStream input = new CodedInputStream(ms);

-            Assert.AreEqual(tag, input.ReadTag());

-

-            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadBytes());

-        }

-

-        internal static TestRecursiveMessage MakeRecursiveMessage(int depth)

-        {

-            if (depth == 0)

-            {

-                return new TestRecursiveMessage { I = 5 };

-            }

-            else

-            {

-                return new TestRecursiveMessage { A = MakeRecursiveMessage(depth - 1) };

-            }

-        }

-

-        internal static void AssertMessageDepth(TestRecursiveMessage message, int depth)

-        {

-            if (depth == 0)

-            {

-                Assert.IsNull(message.A);

-                Assert.AreEqual(5, message.I);

-            }

-            else

-            {

-                Assert.IsNotNull(message.A);

-                AssertMessageDepth(message.A, depth - 1);

-            }

-        }

-

-        [Test]

-        public void MaliciousRecursion()

-        {

-            ByteString atRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit).ToByteString();

-            ByteString beyondRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit + 1).ToByteString();

-

-            AssertMessageDepth(TestRecursiveMessage.Parser.ParseFrom(atRecursiveLimit), CodedInputStream.DefaultRecursionLimit);

-

-            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(beyondRecursiveLimit));

-

-            CodedInputStream input = CodedInputStream.CreateWithLimits(new MemoryStream(atRecursiveLimit.ToByteArray()), 1000000, CodedInputStream.DefaultRecursionLimit - 1);

-            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(input));

-        }

-

-        [Test]

-        public void SizeLimit()

-        {

-            // Have to use a Stream rather than ByteString.CreateCodedInput as SizeLimit doesn't

-            // apply to the latter case.

-            MemoryStream ms = new MemoryStream(SampleMessages.CreateFullTestAllTypes().ToByteArray());

-            CodedInputStream input = CodedInputStream.CreateWithLimits(ms, 16, 100);

-            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(input));

-        }

-

-        /// <summary>

-        /// Tests that if we read an string that contains invalid UTF-8, no exception

-        /// is thrown.  Instead, the invalid bytes are replaced with the Unicode

-        /// "replacement character" U+FFFD.

-        /// </summary>

-        [Test]

-        public void ReadInvalidUtf8()

-        {

-            MemoryStream ms = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(ms);

-

-            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteRawVarint32(tag);

-            output.WriteRawVarint32(1);

-            output.WriteRawBytes(new byte[] {0x80});

-            output.Flush();

-            ms.Position = 0;

-

-            CodedInputStream input = new CodedInputStream(ms);

-

-            Assert.AreEqual(tag, input.ReadTag());

-            string text = input.ReadString();

-            Assert.AreEqual('\ufffd', text[0]);

-        }

-

-        /// <summary>

-        /// A stream which limits the number of bytes it reads at a time.

-        /// We use this to make sure that CodedInputStream doesn't screw up when

-        /// reading in small blocks.

-        /// </summary>

-        private sealed class SmallBlockInputStream : MemoryStream

-        {

-            private readonly int blockSize;

-

-            public SmallBlockInputStream(byte[] data, int blockSize)

-                : base(data)

-            {

-                this.blockSize = blockSize;

-            }

-

-            public override int Read(byte[] buffer, int offset, int count)

-            {

-                return base.Read(buffer, offset, Math.Min(count, blockSize));

-            }

-        }

-

-        [Test]

-        public void TestNegativeEnum()

-        {

-            byte[] bytes = { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };

-            CodedInputStream input = new CodedInputStream(bytes);

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

-            Assert.IsTrue(input.IsAtEnd);

-        }

-

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

-        [Test]

-        public void TestSlowPathAvoidance()

-        {

-            using (var ms = new MemoryStream())

-            {

-                CodedOutputStream output = new CodedOutputStream(ms);

-                output.WriteTag(1, WireFormat.WireType.LengthDelimited);

-                output.WriteBytes(ByteString.CopyFrom(new byte[100]));

-                output.WriteTag(2, WireFormat.WireType.LengthDelimited);

-                output.WriteBytes(ByteString.CopyFrom(new byte[100]));

-                output.Flush();

-

-                ms.Position = 0;

-                CodedInputStream input = new CodedInputStream(ms, new byte[ms.Length / 2], 0, 0, false);

-

-                uint tag = input.ReadTag();

-                Assert.AreEqual(1, WireFormat.GetTagFieldNumber(tag));

-                Assert.AreEqual(100, input.ReadBytes().Length);

-

-                tag = input.ReadTag();

-                Assert.AreEqual(2, WireFormat.GetTagFieldNumber(tag));

-                Assert.AreEqual(100, input.ReadBytes().Length);

-            }

-        }

-

-        [Test]

-        public void Tag0Throws()

-        {

-            var input = new CodedInputStream(new byte[] { 0 });

-            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadTag());

-        }

-

-        [Test]

-        public void SkipGroup()

-        {

-            // Create an output stream with a group in:

-            // Field 1: string "field 1"

-            // Field 2: group containing:

-            //   Field 1: fixed int32 value 100

-            //   Field 2: string "ignore me"

-            //   Field 3: nested group containing

-            //      Field 1: fixed int64 value 1000

-            // Field 3: string "field 3"

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-            output.WriteTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteString("field 1");

-            

-            // The outer group...

-            output.WriteTag(2, WireFormat.WireType.StartGroup);

-            output.WriteTag(1, WireFormat.WireType.Fixed32);

-            output.WriteFixed32(100);

-            output.WriteTag(2, WireFormat.WireType.LengthDelimited);

-            output.WriteString("ignore me");

-            // The nested group...

-            output.WriteTag(3, WireFormat.WireType.StartGroup);

-            output.WriteTag(1, WireFormat.WireType.Fixed64);

-            output.WriteFixed64(1000);

-            // Note: Not sure the field number is relevant for end group...

-            output.WriteTag(3, WireFormat.WireType.EndGroup);

-

-            // End the outer group

-            output.WriteTag(2, WireFormat.WireType.EndGroup);

-

-            output.WriteTag(3, WireFormat.WireType.LengthDelimited);

-            output.WriteString("field 3");

-            output.Flush();

-            stream.Position = 0;

-

-            // Now act like a generated client

-            var input = new CodedInputStream(stream);

-            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());

-            Assert.AreEqual("field 1", input.ReadString());

-            Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());

-            input.SkipLastField(); // Should consume the whole group, including the nested one.

-            Assert.AreEqual(WireFormat.MakeTag(3, WireFormat.WireType.LengthDelimited), input.ReadTag());

-            Assert.AreEqual("field 3", input.ReadString());

-        }

-

-        [Test]

-        public void SkipGroup_WrongEndGroupTag()

-        {

-            // Create an output stream with:

-            // Field 1: string "field 1"

-            // Start group 2

-            //   Field 3: fixed int32

-            // End group 4 (should give an error)

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-            output.WriteTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteString("field 1");

-

-            // The outer group...

-            output.WriteTag(2, WireFormat.WireType.StartGroup);

-            output.WriteTag(3, WireFormat.WireType.Fixed32);

-            output.WriteFixed32(100);

-            output.WriteTag(4, WireFormat.WireType.EndGroup);

-            output.Flush();

-            stream.Position = 0;

-

-            // Now act like a generated client

-            var input = new CodedInputStream(stream);

-            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());

-            Assert.AreEqual("field 1", input.ReadString());

-            Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());

-            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);

-        }

-

-        [Test]

-        public void RogueEndGroupTag()

-        {

-            // If we have an end-group tag without a leading start-group tag, generated

-            // code will just call SkipLastField... so that should fail.

-

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-            output.WriteTag(1, WireFormat.WireType.EndGroup);

-            output.Flush();

-            stream.Position = 0;

-

-            var input = new CodedInputStream(stream);

-            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.EndGroup), input.ReadTag());

-            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);

-        }

-

-        [Test]

-        public void EndOfStreamReachedWhileSkippingGroup()

-        {

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-            output.WriteTag(1, WireFormat.WireType.StartGroup);

-            output.WriteTag(2, WireFormat.WireType.StartGroup);

-            output.WriteTag(2, WireFormat.WireType.EndGroup);

-

-            output.Flush();

-            stream.Position = 0;

-

-            // Now act like a generated client

-            var input = new CodedInputStream(stream);

-            input.ReadTag();

-            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);

-        }

-

-        [Test]

-        public void RecursionLimitAppliedWhileSkippingGroup()

-        {

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-            for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)

-            {

-                output.WriteTag(1, WireFormat.WireType.StartGroup);

-            }

-            for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)

-            {

-                output.WriteTag(1, WireFormat.WireType.EndGroup);

-            }

-            output.Flush();

-            stream.Position = 0;

-

-            // Now act like a generated client

-            var input = new CodedInputStream(stream);

-            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.StartGroup), input.ReadTag());

-            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);

-        }

-

-        [Test]

-        public void Construction_Invalid()

-        {

-            Assert.Throws<ArgumentNullException>(() => new CodedInputStream((byte[]) null));

-            Assert.Throws<ArgumentNullException>(() => new CodedInputStream(null, 0, 0));

-            Assert.Throws<ArgumentNullException>(() => new CodedInputStream((Stream) null));

-            Assert.Throws<ArgumentOutOfRangeException>(() => new CodedInputStream(new byte[10], 100, 0));

-            Assert.Throws<ArgumentOutOfRangeException>(() => new CodedInputStream(new byte[10], 5, 10));

-        }

-

-        [Test]

-        public void CreateWithLimits_InvalidLimits()

-        {

-            var stream = new MemoryStream();

-            Assert.Throws<ArgumentOutOfRangeException>(() => CodedInputStream.CreateWithLimits(stream, 0, 1));

-            Assert.Throws<ArgumentOutOfRangeException>(() => CodedInputStream.CreateWithLimits(stream, 1, 0));

-        }

-

-        [Test]

-        public void Dispose_DisposesUnderlyingStream()

-        {

-            var memoryStream = new MemoryStream();

-            Assert.IsTrue(memoryStream.CanRead);

-            using (var cis = new CodedInputStream(memoryStream))

-            {

-            }

-            Assert.IsFalse(memoryStream.CanRead); // Disposed

-        }

-

-        [Test]

-        public void Dispose_WithLeaveOpen()

-        {

-            var memoryStream = new MemoryStream();

-            Assert.IsTrue(memoryStream.CanRead);

-            using (var cis = new CodedInputStream(memoryStream, true))

-            {

-            }

-            Assert.IsTrue(memoryStream.CanRead); // We left the stream open

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.IO;
+using Google.Protobuf.TestProtos;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+    public class CodedInputStreamTest
+    {
+        /// <summary>
+        /// Helper to construct a byte array from a bunch of bytes.  The inputs are
+        /// actually ints so that I can use hex notation and not get stupid errors
+        /// about precision.
+        /// </summary>
+        private static byte[] Bytes(params int[] bytesAsInts)
+        {
+            byte[] bytes = new byte[bytesAsInts.Length];
+            for (int i = 0; i < bytesAsInts.Length; i++)
+            {
+                bytes[i] = (byte) bytesAsInts[i];
+            }
+            return bytes;
+        }
+
+        /// <summary>
+        /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64()
+        /// </summary>
+        private static void AssertReadVarint(byte[] data, ulong value)
+        {
+            CodedInputStream input = new CodedInputStream(data);
+            Assert.AreEqual((uint) value, input.ReadRawVarint32());
+
+            input = new CodedInputStream(data);
+            Assert.AreEqual(value, input.ReadRawVarint64());
+            Assert.IsTrue(input.IsAtEnd);
+
+            // Try different block sizes.
+            for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
+            {
+                input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
+                Assert.AreEqual((uint) value, input.ReadRawVarint32());
+
+                input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
+                Assert.AreEqual(value, input.ReadRawVarint64());
+                Assert.IsTrue(input.IsAtEnd);
+            }
+
+            // Try reading directly from a MemoryStream. We want to verify that it
+            // doesn't read past the end of the input, so write an extra byte - this
+            // lets us test the position at the end.
+            MemoryStream memoryStream = new MemoryStream();
+            memoryStream.Write(data, 0, data.Length);
+            memoryStream.WriteByte(0);
+            memoryStream.Position = 0;
+            Assert.AreEqual((uint) value, CodedInputStream.ReadRawVarint32(memoryStream));
+            Assert.AreEqual(data.Length, memoryStream.Position);
+        }
+
+        /// <summary>
+        /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and
+        /// expects them to fail with an InvalidProtocolBufferException whose
+        /// description matches the given one.
+        /// </summary>
+        private static void AssertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)
+        {
+            CodedInputStream input = new CodedInputStream(data);
+            var exception = Assert.Throws<InvalidProtocolBufferException>(() => input.ReadRawVarint32());
+            Assert.AreEqual(expected.Message, exception.Message);
+
+            input = new CodedInputStream(data);
+            exception = Assert.Throws<InvalidProtocolBufferException>(() => input.ReadRawVarint64());
+            Assert.AreEqual(expected.Message, exception.Message);
+
+            // Make sure we get the same error when reading directly from a Stream.
+            exception = Assert.Throws<InvalidProtocolBufferException>(() => CodedInputStream.ReadRawVarint32(new MemoryStream(data)));
+            Assert.AreEqual(expected.Message, exception.Message);
+        }
+
+        [Test]
+        public void ReadVarint()
+        {
+            AssertReadVarint(Bytes(0x00), 0);
+            AssertReadVarint(Bytes(0x01), 1);
+            AssertReadVarint(Bytes(0x7f), 127);
+            // 14882
+            AssertReadVarint(Bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
+            // 2961488830
+            AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
+                             (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+                             (0x0bL << 28));
+
+            // 64-bit
+            // 7256456126
+            AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
+                             (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+                             (0x1bL << 28));
+            // 41256202580718336
+            AssertReadVarint(Bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
+                             (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
+                             (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
+            // 11964378330978735131
+            AssertReadVarint(Bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
+                             (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
+                             (0x3bUL << 28) | (0x56UL << 35) | (0x00UL << 42) |
+                             (0x05UL << 49) | (0x26UL << 56) | (0x01UL << 63));
+
+            // Failures
+            AssertReadVarintFailure(
+                InvalidProtocolBufferException.MalformedVarint(),
+                Bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+                      0x00));
+            AssertReadVarintFailure(
+                InvalidProtocolBufferException.TruncatedMessage(),
+                Bytes(0x80));
+        }
+
+        /// <summary>
+        /// Parses the given bytes using ReadRawLittleEndian32() and checks
+        /// that the result matches the given value.
+        /// </summary>
+        private static void AssertReadLittleEndian32(byte[] data, uint value)
+        {
+            CodedInputStream input = new CodedInputStream(data);
+            Assert.AreEqual(value, input.ReadRawLittleEndian32());
+            Assert.IsTrue(input.IsAtEnd);
+
+            // Try different block sizes.
+            for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
+            {
+                input = new CodedInputStream(
+                    new SmallBlockInputStream(data, blockSize));
+                Assert.AreEqual(value, input.ReadRawLittleEndian32());
+                Assert.IsTrue(input.IsAtEnd);
+            }
+        }
+
+        /// <summary>
+        /// Parses the given bytes using ReadRawLittleEndian64() and checks
+        /// that the result matches the given value.
+        /// </summary>
+        private static void AssertReadLittleEndian64(byte[] data, ulong value)
+        {
+            CodedInputStream input = new CodedInputStream(data);
+            Assert.AreEqual(value, input.ReadRawLittleEndian64());
+            Assert.IsTrue(input.IsAtEnd);
+
+            // Try different block sizes.
+            for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
+            {
+                input = new CodedInputStream(
+                    new SmallBlockInputStream(data, blockSize));
+                Assert.AreEqual(value, input.ReadRawLittleEndian64());
+                Assert.IsTrue(input.IsAtEnd);
+            }
+        }
+
+        [Test]
+        public void ReadLittleEndian()
+        {
+            AssertReadLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
+            AssertReadLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
+
+            AssertReadLittleEndian64(Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
+                                     0x123456789abcdef0L);
+            AssertReadLittleEndian64(
+                Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678UL);
+        }
+
+        [Test]
+        public void DecodeZigZag32()
+        {
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(0));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(1));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(2));
+            Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag32(3));
+            Assert.AreEqual(0x3FFFFFFF, ParsingPrimitives.DecodeZigZag32(0x7FFFFFFE));
+            Assert.AreEqual(unchecked((int) 0xC0000000), ParsingPrimitives.DecodeZigZag32(0x7FFFFFFF));
+            Assert.AreEqual(0x7FFFFFFF, ParsingPrimitives.DecodeZigZag32(0xFFFFFFFE));
+            Assert.AreEqual(unchecked((int) 0x80000000), ParsingPrimitives.DecodeZigZag32(0xFFFFFFFF));
+        }
+
+        [Test]
+        public void DecodeZigZag64()
+        {
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(0));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(1));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(2));
+            Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag64(3));
+            Assert.AreEqual(0x000000003FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFEL));
+            Assert.AreEqual(unchecked((long) 0xFFFFFFFFC0000000L), ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFFL));
+            Assert.AreEqual(0x000000007FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFEL));
+            Assert.AreEqual(unchecked((long) 0xFFFFFFFF80000000L), ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFFL));
+            Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
+            Assert.AreEqual(unchecked((long) 0x8000000000000000L), ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
+        }
+        
+        [Test]
+        public void ReadWholeMessage_VaryingBlockSizes()
+        {
+            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+            byte[] rawBytes = message.ToByteArray();
+            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.Parser.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));
+                Assert.AreEqual(message, message2);
+            }
+        }
+                
+        [Test]
+        public void ReadHugeBlob()
+        {
+            // Allocate and initialize a 1MB blob.
+            byte[] blob = new byte[1 << 20];
+            for (int i = 0; i < blob.Length; i++)
+            {
+                blob[i] = (byte) i;
+            }
+
+            // Make a message containing it.
+            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.Parser.ParseFrom(message.ToByteString());
+
+            Assert.AreEqual(message, message2);
+        }
+
+        [Test]
+        public void ReadMaliciouslyLargeBlob()
+        {
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+
+            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteRawVarint32(tag);
+            output.WriteRawVarint32(0x7FFFFFFF);
+            output.WriteRawBytes(new byte[32]); // Pad with a few random bytes.
+            output.Flush();
+            ms.Position = 0;
+
+            CodedInputStream input = new CodedInputStream(ms);
+            Assert.AreEqual(tag, input.ReadTag());
+
+            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadBytes());
+        }
+
+        internal static TestRecursiveMessage MakeRecursiveMessage(int depth)
+        {
+            if (depth == 0)
+            {
+                return new TestRecursiveMessage { I = 5 };
+            }
+            else
+            {
+                return new TestRecursiveMessage { A = MakeRecursiveMessage(depth - 1) };
+            }
+        }
+
+        internal static void AssertMessageDepth(TestRecursiveMessage message, int depth)
+        {
+            if (depth == 0)
+            {
+                Assert.IsNull(message.A);
+                Assert.AreEqual(5, message.I);
+            }
+            else
+            {
+                Assert.IsNotNull(message.A);
+                AssertMessageDepth(message.A, depth - 1);
+            }
+        }
+
+        [Test]
+        public void MaliciousRecursion()
+        {
+            ByteString atRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit).ToByteString();
+            ByteString beyondRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit + 1).ToByteString();
+
+            AssertMessageDepth(TestRecursiveMessage.Parser.ParseFrom(atRecursiveLimit), CodedInputStream.DefaultRecursionLimit);
+
+            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(beyondRecursiveLimit));
+
+            CodedInputStream input = CodedInputStream.CreateWithLimits(new MemoryStream(atRecursiveLimit.ToByteArray()), 1000000, CodedInputStream.DefaultRecursionLimit - 1);
+            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(input));
+        }
+
+        [Test]
+        public void SizeLimit()
+        {
+            // Have to use a Stream rather than ByteString.CreateCodedInput as SizeLimit doesn't
+            // apply to the latter case.
+            MemoryStream ms = new MemoryStream(SampleMessages.CreateFullTestAllTypes().ToByteArray());
+            CodedInputStream input = CodedInputStream.CreateWithLimits(ms, 16, 100);
+            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(input));
+        }
+
+        /// <summary>
+        /// Tests that if we read an string that contains invalid UTF-8, no exception
+        /// is thrown.  Instead, the invalid bytes are replaced with the Unicode
+        /// "replacement character" U+FFFD.
+        /// </summary>
+        [Test]
+        public void ReadInvalidUtf8()
+        {
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+
+            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteRawVarint32(tag);
+            output.WriteRawVarint32(1);
+            output.WriteRawBytes(new byte[] {0x80});
+            output.Flush();
+            ms.Position = 0;
+
+            CodedInputStream input = new CodedInputStream(ms);
+
+            Assert.AreEqual(tag, input.ReadTag());
+            string text = input.ReadString();
+            Assert.AreEqual('\ufffd', text[0]);
+        }
+
+        /// <summary>
+        /// A stream which limits the number of bytes it reads at a time.
+        /// We use this to make sure that CodedInputStream doesn't screw up when
+        /// reading in small blocks.
+        /// </summary>
+        private sealed class SmallBlockInputStream : MemoryStream
+        {
+            private readonly int blockSize;
+
+            public SmallBlockInputStream(byte[] data, int blockSize)
+                : base(data)
+            {
+                this.blockSize = blockSize;
+            }
+
+            public override int Read(byte[] buffer, int offset, int count)
+            {
+                return base.Read(buffer, offset, Math.Min(count, blockSize));
+            }
+        }
+
+        [Test]
+        public void TestNegativeEnum()
+        {
+            byte[] bytes = { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };
+            CodedInputStream input = new CodedInputStream(bytes);
+            Assert.AreEqual((int)SampleEnum.NegativeValue, input.ReadEnum());
+            Assert.IsTrue(input.IsAtEnd);
+        }
+
+        //Issue 71:	CodedInputStream.ReadBytes go to slow path unnecessarily
+        [Test]
+        public void TestSlowPathAvoidance()
+        {
+            using (var ms = new MemoryStream())
+            {
+                CodedOutputStream output = new CodedOutputStream(ms);
+                output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+                output.WriteBytes(ByteString.CopyFrom(new byte[100]));
+                output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+                output.WriteBytes(ByteString.CopyFrom(new byte[100]));
+                output.Flush();
+
+                ms.Position = 0;
+                CodedInputStream input = new CodedInputStream(ms, new byte[ms.Length / 2], 0, 0, false);
+
+                uint tag = input.ReadTag();
+                Assert.AreEqual(1, WireFormat.GetTagFieldNumber(tag));
+                Assert.AreEqual(100, input.ReadBytes().Length);
+
+                tag = input.ReadTag();
+                Assert.AreEqual(2, WireFormat.GetTagFieldNumber(tag));
+                Assert.AreEqual(100, input.ReadBytes().Length);
+            }
+        }
+
+        [Test]
+        public void Tag0Throws()
+        {
+            var input = new CodedInputStream(new byte[] { 0 });
+            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadTag());
+        }
+
+        [Test]
+        public void SkipGroup()
+        {
+            // Create an output stream with a group in:
+            // Field 1: string "field 1"
+            // Field 2: group containing:
+            //   Field 1: fixed int32 value 100
+            //   Field 2: string "ignore me"
+            //   Field 3: nested group containing
+            //      Field 1: fixed int64 value 1000
+            // Field 3: string "field 3"
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+            output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteString("field 1");
+            
+            // The outer group...
+            output.WriteTag(2, WireFormat.WireType.StartGroup);
+            output.WriteTag(1, WireFormat.WireType.Fixed32);
+            output.WriteFixed32(100);
+            output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+            output.WriteString("ignore me");
+            // The nested group...
+            output.WriteTag(3, WireFormat.WireType.StartGroup);
+            output.WriteTag(1, WireFormat.WireType.Fixed64);
+            output.WriteFixed64(1000);
+            // Note: Not sure the field number is relevant for end group...
+            output.WriteTag(3, WireFormat.WireType.EndGroup);
+
+            // End the outer group
+            output.WriteTag(2, WireFormat.WireType.EndGroup);
+
+            output.WriteTag(3, WireFormat.WireType.LengthDelimited);
+            output.WriteString("field 3");
+            output.Flush();
+            stream.Position = 0;
+
+            // Now act like a generated client
+            var input = new CodedInputStream(stream);
+            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());
+            Assert.AreEqual("field 1", input.ReadString());
+            Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());
+            input.SkipLastField(); // Should consume the whole group, including the nested one.
+            Assert.AreEqual(WireFormat.MakeTag(3, WireFormat.WireType.LengthDelimited), input.ReadTag());
+            Assert.AreEqual("field 3", input.ReadString());
+        }
+
+        [Test]
+        public void SkipGroup_WrongEndGroupTag()
+        {
+            // Create an output stream with:
+            // Field 1: string "field 1"
+            // Start group 2
+            //   Field 3: fixed int32
+            // End group 4 (should give an error)
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+            output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteString("field 1");
+
+            // The outer group...
+            output.WriteTag(2, WireFormat.WireType.StartGroup);
+            output.WriteTag(3, WireFormat.WireType.Fixed32);
+            output.WriteFixed32(100);
+            output.WriteTag(4, WireFormat.WireType.EndGroup);
+            output.Flush();
+            stream.Position = 0;
+
+            // Now act like a generated client
+            var input = new CodedInputStream(stream);
+            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());
+            Assert.AreEqual("field 1", input.ReadString());
+            Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());
+            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);
+        }
+
+        [Test]
+        public void RogueEndGroupTag()
+        {
+            // If we have an end-group tag without a leading start-group tag, generated
+            // code will just call SkipLastField... so that should fail.
+
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+            output.WriteTag(1, WireFormat.WireType.EndGroup);
+            output.Flush();
+            stream.Position = 0;
+
+            var input = new CodedInputStream(stream);
+            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.EndGroup), input.ReadTag());
+            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);
+        }
+
+        [Test]
+        public void EndOfStreamReachedWhileSkippingGroup()
+        {
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+            output.WriteTag(1, WireFormat.WireType.StartGroup);
+            output.WriteTag(2, WireFormat.WireType.StartGroup);
+            output.WriteTag(2, WireFormat.WireType.EndGroup);
+
+            output.Flush();
+            stream.Position = 0;
+
+            // Now act like a generated client
+            var input = new CodedInputStream(stream);
+            input.ReadTag();
+            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);
+        }
+
+        [Test]
+        public void RecursionLimitAppliedWhileSkippingGroup()
+        {
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+            for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)
+            {
+                output.WriteTag(1, WireFormat.WireType.StartGroup);
+            }
+            for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)
+            {
+                output.WriteTag(1, WireFormat.WireType.EndGroup);
+            }
+            output.Flush();
+            stream.Position = 0;
+
+            // Now act like a generated client
+            var input = new CodedInputStream(stream);
+            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.StartGroup), input.ReadTag());
+            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);
+        }
+
+        [Test]
+        public void Construction_Invalid()
+        {
+            Assert.Throws<ArgumentNullException>(() => new CodedInputStream((byte[]) null));
+            Assert.Throws<ArgumentNullException>(() => new CodedInputStream(null, 0, 0));
+            Assert.Throws<ArgumentNullException>(() => new CodedInputStream((Stream) null));
+            Assert.Throws<ArgumentOutOfRangeException>(() => new CodedInputStream(new byte[10], 100, 0));
+            Assert.Throws<ArgumentOutOfRangeException>(() => new CodedInputStream(new byte[10], 5, 10));
+        }
+
+        [Test]
+        public void CreateWithLimits_InvalidLimits()
+        {
+            var stream = new MemoryStream();
+            Assert.Throws<ArgumentOutOfRangeException>(() => CodedInputStream.CreateWithLimits(stream, 0, 1));
+            Assert.Throws<ArgumentOutOfRangeException>(() => CodedInputStream.CreateWithLimits(stream, 1, 0));
+        }
+
+        [Test]
+        public void Dispose_DisposesUnderlyingStream()
+        {
+            var memoryStream = new MemoryStream();
+            Assert.IsTrue(memoryStream.CanRead);
+            using (var cis = new CodedInputStream(memoryStream))
+            {
+            }
+            Assert.IsFalse(memoryStream.CanRead); // Disposed
+        }
+
+        [Test]
+        public void Dispose_WithLeaveOpen()
+        {
+            var memoryStream = new MemoryStream();
+            Assert.IsTrue(memoryStream.CanRead);
+            using (var cis = new CodedInputStream(memoryStream, true))
+            {
+            }
+            Assert.IsTrue(memoryStream.CanRead); // We left the stream open
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedOutputStreamTest.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
index e9b4ea8..4890417 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
@@ -1,419 +1,419 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.IO;

-using Google.Protobuf.TestProtos;

-using NUnit.Framework;

-

-namespace Google.Protobuf

-{

-    public class CodedOutputStreamTest

-    {

-        /// <summary>

-        /// Writes the given value using WriteRawVarint32() and WriteRawVarint64() and

-        /// checks that the result matches the given bytes

-        /// </summary>

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

-        {

-            // Only do 32-bit write if the value fits in 32 bits.

-            if ((value >> 32) == 0)

-            {

-                MemoryStream rawOutput = new MemoryStream();

-                CodedOutputStream output = new CodedOutputStream(rawOutput);

-                output.WriteRawVarint32((uint) value);

-                output.Flush();

-                Assert.AreEqual(data, rawOutput.ToArray());

-                // Also try computing size.

-                Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint32Size((uint) value));

-            }

-

-            {

-                MemoryStream rawOutput = new MemoryStream();

-                CodedOutputStream output = new CodedOutputStream(rawOutput);

-                output.WriteRawVarint64(value);

-                output.Flush();

-                Assert.AreEqual(data, rawOutput.ToArray());

-

-                // Also try computing size.

-                Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint64Size(value));

-            }

-

-            // Try different buffer sizes.

-            for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)

-            {

-                // Only do 32-bit write if the value fits in 32 bits.

-                if ((value >> 32) == 0)

-                {

-                    MemoryStream rawOutput = new MemoryStream();

-                    CodedOutputStream output =

-                        new CodedOutputStream(rawOutput, bufferSize);

-                    output.WriteRawVarint32((uint) value);

-                    output.Flush();

-                    Assert.AreEqual(data, rawOutput.ToArray());

-                }

-

-                {

-                    MemoryStream rawOutput = new MemoryStream();

-                    CodedOutputStream output = new CodedOutputStream(rawOutput, bufferSize);

-                    output.WriteRawVarint64(value);

-                    output.Flush();

-                    Assert.AreEqual(data, rawOutput.ToArray());

-                }

-            }

-        }

-

-        /// <summary>

-        /// Tests WriteRawVarint32() and WriteRawVarint64()

-        /// </summary>

-        [Test]

-        public void WriteVarint()

-        {

-            AssertWriteVarint(new byte[] {0x00}, 0);

-            AssertWriteVarint(new byte[] {0x01}, 1);

-            AssertWriteVarint(new byte[] {0x7f}, 127);

-            // 14882

-            AssertWriteVarint(new byte[] {0xa2, 0x74}, (0x22 << 0) | (0x74 << 7));

-            // 2961488830

-            AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x0b},

-                              (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |

-                              (0x0bL << 28));

-

-            // 64-bit

-            // 7256456126

-            AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x1b},

-                              (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |

-                              (0x1bL << 28));

-            // 41256202580718336

-            AssertWriteVarint(

-                new byte[] {0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49},

-                (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |

-                (0x43UL << 28) | (0x49L << 35) | (0x24UL << 42) | (0x49UL << 49));

-            // 11964378330978735131

-            AssertWriteVarint(

-                new byte[] {0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01},

-                unchecked((ulong)

-                          ((0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |

-                           (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |

-                           (0x05L << 49) | (0x26L << 56) | (0x01L << 63))));

-        }

-

-        /// <summary>

-        /// Parses the given bytes using WriteRawLittleEndian32() and checks

-        /// that the result matches the given value.

-        /// </summary>

-        private static void AssertWriteLittleEndian32(byte[] data, uint value)

-        {

-            MemoryStream rawOutput = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(rawOutput);

-            output.WriteRawLittleEndian32(value);

-            output.Flush();

-            Assert.AreEqual(data, rawOutput.ToArray());

-

-            // Try different buffer sizes.

-            for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)

-            {

-                rawOutput = new MemoryStream();

-                output = new CodedOutputStream(rawOutput, bufferSize);

-                output.WriteRawLittleEndian32(value);

-                output.Flush();

-                Assert.AreEqual(data, rawOutput.ToArray());

-            }

-        }

-

-        /// <summary>

-        /// Parses the given bytes using WriteRawLittleEndian64() and checks

-        /// that the result matches the given value.

-        /// </summary>

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

-        {

-            MemoryStream rawOutput = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(rawOutput);

-            output.WriteRawLittleEndian64(value);

-            output.Flush();

-            Assert.AreEqual(data, rawOutput.ToArray());

-

-            // Try different block sizes.

-            for (int blockSize = 1; blockSize <= 16; blockSize *= 2)

-            {

-                rawOutput = new MemoryStream();

-                output = new CodedOutputStream(rawOutput, blockSize);

-                output.WriteRawLittleEndian64(value);

-                output.Flush();

-                Assert.AreEqual(data, rawOutput.ToArray());

-            }

-        }

-

-        /// <summary>

-        /// Tests writeRawLittleEndian32() and writeRawLittleEndian64().

-        /// </summary>

-        [Test]

-        public void WriteLittleEndian()

-        {

-            AssertWriteLittleEndian32(new byte[] {0x78, 0x56, 0x34, 0x12}, 0x12345678);

-            AssertWriteLittleEndian32(new byte[] {0xf0, 0xde, 0xbc, 0x9a}, 0x9abcdef0);

-

-            AssertWriteLittleEndian64(

-                new byte[] {0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12},

-                0x123456789abcdef0L);

-            AssertWriteLittleEndian64(

-                new byte[] {0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a},

-                0x9abcdef012345678UL);

-        }

-

-        [Test]

-        public void WriteWholeMessage_VaryingBlockSizes()

-        {

-            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();

-

-            byte[] rawBytes = message.ToByteArray();

-

-            // Try different block sizes.

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

-            {

-                MemoryStream rawOutput = new MemoryStream();

-                CodedOutputStream output = new CodedOutputStream(rawOutput, blockSize);

-                message.WriteTo(output);

-                output.Flush();

-                Assert.AreEqual(rawBytes, rawOutput.ToArray());

-            }

-        }

-        

-        [Test]

-        public void EncodeZigZag32()

-        {

-            Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag32(0));

-            Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag32(-1));

-            Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag32(1));

-            Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag32(-2));

-            Assert.AreEqual(0x7FFFFFFEu, WritingPrimitives.EncodeZigZag32(0x3FFFFFFF));

-            Assert.AreEqual(0x7FFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0xC0000000)));

-            Assert.AreEqual(0xFFFFFFFEu, WritingPrimitives.EncodeZigZag32(0x7FFFFFFF));

-            Assert.AreEqual(0xFFFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0x80000000)));

-        }

-

-        [Test]

-        public void EncodeZigZag64()

-        {

-            Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag64(0));

-            Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag64(-1));

-            Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag64(1));

-            Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag64(-2));

-            Assert.AreEqual(0x000000007FFFFFFEuL,

-                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL)));

-            Assert.AreEqual(0x000000007FFFFFFFuL,

-                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL)));

-            Assert.AreEqual(0x00000000FFFFFFFEuL,

-                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL)));

-            Assert.AreEqual(0x00000000FFFFFFFFuL,

-                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL)));

-            Assert.AreEqual(0xFFFFFFFFFFFFFFFEL,

-                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL)));

-            Assert.AreEqual(0xFFFFFFFFFFFFFFFFL,

-                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x8000000000000000UL)));

-        }

-

-        [Test]

-        public void RoundTripZigZag32()

-        {

-            // Some easier-to-verify round-trip tests.  The inputs (other than 0, 1, -1)

-            // were chosen semi-randomly via keyboard bashing.

-            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(0)));

-            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(1)));

-            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-1)));

-            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(14927)));

-            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-3612)));

-        }

-

-        [Test]

-        public void RoundTripZigZag64()

-        {

-            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(0)));

-            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(1)));

-            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-1)));

-            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(14927)));

-            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-3612)));

-

-            Assert.AreEqual(856912304801416L,

-                            ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(856912304801416L)));

-            Assert.AreEqual(-75123905439571256L,

-                            ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-75123905439571256L)));

-        }

-

-        [Test]

-        public void TestNegativeEnumNoTag()

-        {

-            Assert.AreEqual(10, CodedOutputStream.ComputeInt32Size(-2));

-            Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) SampleEnum.NegativeValue));

-

-            byte[] bytes = new byte[10];

-            CodedOutputStream output = new CodedOutputStream(bytes);

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

-        }

-

-        [Test]

-        public void TestCodedInputOutputPosition()

-        {

-            byte[] content = new byte[110];

-            for (int i = 0; i < content.Length; i++)

-                content[i] = (byte)i;

-

-            byte[] child = new byte[120];

-            {

-                MemoryStream ms = new MemoryStream(child);

-                CodedOutputStream cout = new CodedOutputStream(ms, 20);

-                // Field 11: numeric value: 500

-                cout.WriteTag(11, WireFormat.WireType.Varint);

-                Assert.AreEqual(1, cout.Position);

-                cout.WriteInt32(500);

-                Assert.AreEqual(3, cout.Position);

-                //Field 12: length delimited 120 bytes

-                cout.WriteTag(12, WireFormat.WireType.LengthDelimited);

-                Assert.AreEqual(4, cout.Position);

-                cout.WriteBytes(ByteString.CopyFrom(content));

-                Assert.AreEqual(115, cout.Position);

-                // Field 13: fixed numeric value: 501

-                cout.WriteTag(13, WireFormat.WireType.Fixed32);

-                Assert.AreEqual(116, cout.Position);

-                cout.WriteSFixed32(501);

-                Assert.AreEqual(120, cout.Position);

-                cout.Flush();

-            }

-

-            byte[] bytes = new byte[130];

-            {

-                CodedOutputStream cout = new CodedOutputStream(bytes);

-                // Field 1: numeric value: 500

-                cout.WriteTag(1, WireFormat.WireType.Varint);

-                Assert.AreEqual(1, cout.Position);

-                cout.WriteInt32(500);

-                Assert.AreEqual(3, cout.Position);

-                //Field 2: length delimited 120 bytes

-                cout.WriteTag(2, WireFormat.WireType.LengthDelimited);

-                Assert.AreEqual(4, cout.Position);

-                cout.WriteBytes(ByteString.CopyFrom(child));

-                Assert.AreEqual(125, cout.Position);

-                // Field 3: fixed numeric value: 500

-                cout.WriteTag(3, WireFormat.WireType.Fixed32);

-                Assert.AreEqual(126, cout.Position);

-                cout.WriteSFixed32(501);

-                Assert.AreEqual(130, cout.Position);

-                cout.Flush();

-            }

-            // Now test Input stream:

-            {

-                CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50], 0, 0, false);

-                Assert.AreEqual(0, cin.Position);

-                // Field 1:

-                uint tag = cin.ReadTag();

-                Assert.AreEqual(1, tag >> 3);

-                Assert.AreEqual(1, cin.Position);

-                Assert.AreEqual(500, cin.ReadInt32());

-                Assert.AreEqual(3, cin.Position);

-                //Field 2:

-                tag = cin.ReadTag();

-                Assert.AreEqual(2, tag >> 3);

-                Assert.AreEqual(4, cin.Position);

-                int childlen = cin.ReadLength();

-                Assert.AreEqual(120, childlen);

-                Assert.AreEqual(5, cin.Position);

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

-                Assert.AreEqual(5, cin.Position);

-                // Now we are reading child message

-                {

-                    // Field 11: numeric value: 500

-                    tag = cin.ReadTag();

-                    Assert.AreEqual(11, tag >> 3);

-                    Assert.AreEqual(6, cin.Position);

-                    Assert.AreEqual(500, cin.ReadInt32());

-                    Assert.AreEqual(8, cin.Position);

-                    //Field 12: length delimited 120 bytes

-                    tag = cin.ReadTag();

-                    Assert.AreEqual(12, tag >> 3);

-                    Assert.AreEqual(9, cin.Position);

-                    ByteString bstr = cin.ReadBytes();

-                    Assert.AreEqual(110, bstr.Length);

-                    Assert.AreEqual((byte) 109, bstr[109]);

-                    Assert.AreEqual(120, cin.Position);

-                    // Field 13: fixed numeric value: 501

-                    tag = cin.ReadTag();

-                    Assert.AreEqual(13, tag >> 3);

-                    // ROK - Previously broken here, this returned 126 failing to account for bufferSizeAfterLimit

-                    Assert.AreEqual(121, cin.Position);

-                    Assert.AreEqual(501, cin.ReadSFixed32());

-                    Assert.AreEqual(125, cin.Position);

-                    Assert.IsTrue(cin.IsAtEnd);

-                }

-                cin.PopLimit(oldlimit);

-                Assert.AreEqual(125, cin.Position);

-                // Field 3: fixed numeric value: 501

-                tag = cin.ReadTag();

-                Assert.AreEqual(3, tag >> 3);

-                Assert.AreEqual(126, cin.Position);

-                Assert.AreEqual(501, cin.ReadSFixed32());

-                Assert.AreEqual(130, cin.Position);

-                Assert.IsTrue(cin.IsAtEnd);

-            }

-        }

-

-        [Test]

-        public void Dispose_DisposesUnderlyingStream()

-        {

-            var memoryStream = new MemoryStream();

-            Assert.IsTrue(memoryStream.CanWrite);

-            using (var cos = new CodedOutputStream(memoryStream))

-            {

-                cos.WriteRawBytes(new byte[] {0});

-                Assert.AreEqual(0, memoryStream.Position); // Not flushed yet

-            }

-            Assert.AreEqual(1, memoryStream.ToArray().Length); // Flushed data from CodedOutputStream to MemoryStream

-            Assert.IsFalse(memoryStream.CanWrite); // Disposed

-        }

-

-        [Test]

-        public void Dispose_WithLeaveOpen()

-        {

-            var memoryStream = new MemoryStream();

-            Assert.IsTrue(memoryStream.CanWrite);

-            using (var cos = new CodedOutputStream(memoryStream, true))

-            {

-                cos.WriteRawBytes(new byte[] {0});

-                Assert.AreEqual(0, memoryStream.Position); // Not flushed yet

-            }

-            Assert.AreEqual(1, memoryStream.Position); // Flushed data from CodedOutputStream to MemoryStream

-            Assert.IsTrue(memoryStream.CanWrite); // We left the stream open

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.IO;
+using Google.Protobuf.TestProtos;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+    public class CodedOutputStreamTest
+    {
+        /// <summary>
+        /// Writes the given value using WriteRawVarint32() and WriteRawVarint64() and
+        /// checks that the result matches the given bytes
+        /// </summary>
+        private static void AssertWriteVarint(byte[] data, ulong value)
+        {
+            // Only do 32-bit write if the value fits in 32 bits.
+            if ((value >> 32) == 0)
+            {
+                MemoryStream rawOutput = new MemoryStream();
+                CodedOutputStream output = new CodedOutputStream(rawOutput);
+                output.WriteRawVarint32((uint) value);
+                output.Flush();
+                Assert.AreEqual(data, rawOutput.ToArray());
+                // Also try computing size.
+                Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint32Size((uint) value));
+            }
+
+            {
+                MemoryStream rawOutput = new MemoryStream();
+                CodedOutputStream output = new CodedOutputStream(rawOutput);
+                output.WriteRawVarint64(value);
+                output.Flush();
+                Assert.AreEqual(data, rawOutput.ToArray());
+
+                // Also try computing size.
+                Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint64Size(value));
+            }
+
+            // Try different buffer sizes.
+            for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
+            {
+                // Only do 32-bit write if the value fits in 32 bits.
+                if ((value >> 32) == 0)
+                {
+                    MemoryStream rawOutput = new MemoryStream();
+                    CodedOutputStream output =
+                        new CodedOutputStream(rawOutput, bufferSize);
+                    output.WriteRawVarint32((uint) value);
+                    output.Flush();
+                    Assert.AreEqual(data, rawOutput.ToArray());
+                }
+
+                {
+                    MemoryStream rawOutput = new MemoryStream();
+                    CodedOutputStream output = new CodedOutputStream(rawOutput, bufferSize);
+                    output.WriteRawVarint64(value);
+                    output.Flush();
+                    Assert.AreEqual(data, rawOutput.ToArray());
+                }
+            }
+        }
+
+        /// <summary>
+        /// Tests WriteRawVarint32() and WriteRawVarint64()
+        /// </summary>
+        [Test]
+        public void WriteVarint()
+        {
+            AssertWriteVarint(new byte[] {0x00}, 0);
+            AssertWriteVarint(new byte[] {0x01}, 1);
+            AssertWriteVarint(new byte[] {0x7f}, 127);
+            // 14882
+            AssertWriteVarint(new byte[] {0xa2, 0x74}, (0x22 << 0) | (0x74 << 7));
+            // 2961488830
+            AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x0b},
+                              (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+                              (0x0bL << 28));
+
+            // 64-bit
+            // 7256456126
+            AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x1b},
+                              (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+                              (0x1bL << 28));
+            // 41256202580718336
+            AssertWriteVarint(
+                new byte[] {0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49},
+                (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
+                (0x43UL << 28) | (0x49L << 35) | (0x24UL << 42) | (0x49UL << 49));
+            // 11964378330978735131
+            AssertWriteVarint(
+                new byte[] {0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01},
+                unchecked((ulong)
+                          ((0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
+                           (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
+                           (0x05L << 49) | (0x26L << 56) | (0x01L << 63))));
+        }
+
+        /// <summary>
+        /// Parses the given bytes using WriteRawLittleEndian32() and checks
+        /// that the result matches the given value.
+        /// </summary>
+        private static void AssertWriteLittleEndian32(byte[] data, uint value)
+        {
+            MemoryStream rawOutput = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(rawOutput);
+            output.WriteRawLittleEndian32(value);
+            output.Flush();
+            Assert.AreEqual(data, rawOutput.ToArray());
+
+            // Try different buffer sizes.
+            for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
+            {
+                rawOutput = new MemoryStream();
+                output = new CodedOutputStream(rawOutput, bufferSize);
+                output.WriteRawLittleEndian32(value);
+                output.Flush();
+                Assert.AreEqual(data, rawOutput.ToArray());
+            }
+        }
+
+        /// <summary>
+        /// Parses the given bytes using WriteRawLittleEndian64() and checks
+        /// that the result matches the given value.
+        /// </summary>
+        private static void AssertWriteLittleEndian64(byte[] data, ulong value)
+        {
+            MemoryStream rawOutput = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(rawOutput);
+            output.WriteRawLittleEndian64(value);
+            output.Flush();
+            Assert.AreEqual(data, rawOutput.ToArray());
+
+            // Try different block sizes.
+            for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
+            {
+                rawOutput = new MemoryStream();
+                output = new CodedOutputStream(rawOutput, blockSize);
+                output.WriteRawLittleEndian64(value);
+                output.Flush();
+                Assert.AreEqual(data, rawOutput.ToArray());
+            }
+        }
+
+        /// <summary>
+        /// Tests writeRawLittleEndian32() and writeRawLittleEndian64().
+        /// </summary>
+        [Test]
+        public void WriteLittleEndian()
+        {
+            AssertWriteLittleEndian32(new byte[] {0x78, 0x56, 0x34, 0x12}, 0x12345678);
+            AssertWriteLittleEndian32(new byte[] {0xf0, 0xde, 0xbc, 0x9a}, 0x9abcdef0);
+
+            AssertWriteLittleEndian64(
+                new byte[] {0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12},
+                0x123456789abcdef0L);
+            AssertWriteLittleEndian64(
+                new byte[] {0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a},
+                0x9abcdef012345678UL);
+        }
+
+        [Test]
+        public void WriteWholeMessage_VaryingBlockSizes()
+        {
+            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+            byte[] rawBytes = message.ToByteArray();
+
+            // Try different block sizes.
+            for (int blockSize = 1; blockSize < 256; blockSize *= 2)
+            {
+                MemoryStream rawOutput = new MemoryStream();
+                CodedOutputStream output = new CodedOutputStream(rawOutput, blockSize);
+                message.WriteTo(output);
+                output.Flush();
+                Assert.AreEqual(rawBytes, rawOutput.ToArray());
+            }
+        }
+        
+        [Test]
+        public void EncodeZigZag32()
+        {
+            Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag32(0));
+            Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag32(-1));
+            Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag32(1));
+            Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag32(-2));
+            Assert.AreEqual(0x7FFFFFFEu, WritingPrimitives.EncodeZigZag32(0x3FFFFFFF));
+            Assert.AreEqual(0x7FFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0xC0000000)));
+            Assert.AreEqual(0xFFFFFFFEu, WritingPrimitives.EncodeZigZag32(0x7FFFFFFF));
+            Assert.AreEqual(0xFFFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0x80000000)));
+        }
+
+        [Test]
+        public void EncodeZigZag64()
+        {
+            Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag64(0));
+            Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag64(-1));
+            Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag64(1));
+            Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag64(-2));
+            Assert.AreEqual(0x000000007FFFFFFEuL,
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL)));
+            Assert.AreEqual(0x000000007FFFFFFFuL,
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL)));
+            Assert.AreEqual(0x00000000FFFFFFFEuL,
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL)));
+            Assert.AreEqual(0x00000000FFFFFFFFuL,
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL)));
+            Assert.AreEqual(0xFFFFFFFFFFFFFFFEL,
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL)));
+            Assert.AreEqual(0xFFFFFFFFFFFFFFFFL,
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x8000000000000000UL)));
+        }
+
+        [Test]
+        public void RoundTripZigZag32()
+        {
+            // Some easier-to-verify round-trip tests.  The inputs (other than 0, 1, -1)
+            // were chosen semi-randomly via keyboard bashing.
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(0)));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(1)));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-1)));
+            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(14927)));
+            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-3612)));
+        }
+
+        [Test]
+        public void RoundTripZigZag64()
+        {
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(0)));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(1)));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-1)));
+            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(14927)));
+            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-3612)));
+
+            Assert.AreEqual(856912304801416L,
+                            ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(856912304801416L)));
+            Assert.AreEqual(-75123905439571256L,
+                            ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-75123905439571256L)));
+        }
+
+        [Test]
+        public void TestNegativeEnumNoTag()
+        {
+            Assert.AreEqual(10, CodedOutputStream.ComputeInt32Size(-2));
+            Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) SampleEnum.NegativeValue));
+
+            byte[] bytes = new byte[10];
+            CodedOutputStream output = new CodedOutputStream(bytes);
+            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));
+        }
+
+        [Test]
+        public void TestCodedInputOutputPosition()
+        {
+            byte[] content = new byte[110];
+            for (int i = 0; i < content.Length; i++)
+                content[i] = (byte)i;
+
+            byte[] child = new byte[120];
+            {
+                MemoryStream ms = new MemoryStream(child);
+                CodedOutputStream cout = new CodedOutputStream(ms, 20);
+                // Field 11: numeric value: 500
+                cout.WriteTag(11, WireFormat.WireType.Varint);
+                Assert.AreEqual(1, cout.Position);
+                cout.WriteInt32(500);
+                Assert.AreEqual(3, cout.Position);
+                //Field 12: length delimited 120 bytes
+                cout.WriteTag(12, WireFormat.WireType.LengthDelimited);
+                Assert.AreEqual(4, cout.Position);
+                cout.WriteBytes(ByteString.CopyFrom(content));
+                Assert.AreEqual(115, cout.Position);
+                // Field 13: fixed numeric value: 501
+                cout.WriteTag(13, WireFormat.WireType.Fixed32);
+                Assert.AreEqual(116, cout.Position);
+                cout.WriteSFixed32(501);
+                Assert.AreEqual(120, cout.Position);
+                cout.Flush();
+            }
+
+            byte[] bytes = new byte[130];
+            {
+                CodedOutputStream cout = new CodedOutputStream(bytes);
+                // Field 1: numeric value: 500
+                cout.WriteTag(1, WireFormat.WireType.Varint);
+                Assert.AreEqual(1, cout.Position);
+                cout.WriteInt32(500);
+                Assert.AreEqual(3, cout.Position);
+                //Field 2: length delimited 120 bytes
+                cout.WriteTag(2, WireFormat.WireType.LengthDelimited);
+                Assert.AreEqual(4, cout.Position);
+                cout.WriteBytes(ByteString.CopyFrom(child));
+                Assert.AreEqual(125, cout.Position);
+                // Field 3: fixed numeric value: 500
+                cout.WriteTag(3, WireFormat.WireType.Fixed32);
+                Assert.AreEqual(126, cout.Position);
+                cout.WriteSFixed32(501);
+                Assert.AreEqual(130, cout.Position);
+                cout.Flush();
+            }
+            // Now test Input stream:
+            {
+                CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50], 0, 0, false);
+                Assert.AreEqual(0, cin.Position);
+                // Field 1:
+                uint tag = cin.ReadTag();
+                Assert.AreEqual(1, tag >> 3);
+                Assert.AreEqual(1, cin.Position);
+                Assert.AreEqual(500, cin.ReadInt32());
+                Assert.AreEqual(3, cin.Position);
+                //Field 2:
+                tag = cin.ReadTag();
+                Assert.AreEqual(2, tag >> 3);
+                Assert.AreEqual(4, cin.Position);
+                int childlen = cin.ReadLength();
+                Assert.AreEqual(120, childlen);
+                Assert.AreEqual(5, cin.Position);
+                int oldlimit = cin.PushLimit((int)childlen);
+                Assert.AreEqual(5, cin.Position);
+                // Now we are reading child message
+                {
+                    // Field 11: numeric value: 500
+                    tag = cin.ReadTag();
+                    Assert.AreEqual(11, tag >> 3);
+                    Assert.AreEqual(6, cin.Position);
+                    Assert.AreEqual(500, cin.ReadInt32());
+                    Assert.AreEqual(8, cin.Position);
+                    //Field 12: length delimited 120 bytes
+                    tag = cin.ReadTag();
+                    Assert.AreEqual(12, tag >> 3);
+                    Assert.AreEqual(9, cin.Position);
+                    ByteString bstr = cin.ReadBytes();
+                    Assert.AreEqual(110, bstr.Length);
+                    Assert.AreEqual((byte) 109, bstr[109]);
+                    Assert.AreEqual(120, cin.Position);
+                    // Field 13: fixed numeric value: 501
+                    tag = cin.ReadTag();
+                    Assert.AreEqual(13, tag >> 3);
+                    // ROK - Previously broken here, this returned 126 failing to account for bufferSizeAfterLimit
+                    Assert.AreEqual(121, cin.Position);
+                    Assert.AreEqual(501, cin.ReadSFixed32());
+                    Assert.AreEqual(125, cin.Position);
+                    Assert.IsTrue(cin.IsAtEnd);
+                }
+                cin.PopLimit(oldlimit);
+                Assert.AreEqual(125, cin.Position);
+                // Field 3: fixed numeric value: 501
+                tag = cin.ReadTag();
+                Assert.AreEqual(3, tag >> 3);
+                Assert.AreEqual(126, cin.Position);
+                Assert.AreEqual(501, cin.ReadSFixed32());
+                Assert.AreEqual(130, cin.Position);
+                Assert.IsTrue(cin.IsAtEnd);
+            }
+        }
+
+        [Test]
+        public void Dispose_DisposesUnderlyingStream()
+        {
+            var memoryStream = new MemoryStream();
+            Assert.IsTrue(memoryStream.CanWrite);
+            using (var cos = new CodedOutputStream(memoryStream))
+            {
+                cos.WriteRawBytes(new byte[] {0});
+                Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
+            }
+            Assert.AreEqual(1, memoryStream.ToArray().Length); // Flushed data from CodedOutputStream to MemoryStream
+            Assert.IsFalse(memoryStream.CanWrite); // Disposed
+        }
+
+        [Test]
+        public void Dispose_WithLeaveOpen()
+        {
+            var memoryStream = new MemoryStream();
+            Assert.IsTrue(memoryStream.CanWrite);
+            using (var cos = new CodedOutputStream(memoryStream, true))
+            {
+                cos.WriteRawBytes(new byte[] {0});
+                Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
+            }
+            Assert.AreEqual(1, memoryStream.Position); // Flushed data from CodedOutputStream to MemoryStream
+            Assert.IsTrue(memoryStream.CanWrite); // We left the stream open
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/DeprecatedMemberTest.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/DeprecatedMemberTest.cs
index 34d5b9f..fd041e0 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/DeprecatedMemberTest.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/DeprecatedMemberTest.cs
@@ -1,55 +1,55 @@
-#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.Reflection;

-using Google.Protobuf.TestProtos;

-using NUnit.Framework;

-

-namespace Google.Protobuf

-{

-    public class DeprecatedMemberTest

-    {

-        private static void AssertIsDeprecated(MemberInfo member)

-        {

-            Assert.NotNull(member);

-            Assert.IsTrue(member.IsDefined(typeof(ObsoleteAttribute), false), "Member not obsolete: " + member);

-        }

-

-        [Test]

-        public void TestDepreatedPrimitiveValue()

-        {

-            AssertIsDeprecated(typeof(TestDeprecatedFields).GetProperty("DeprecatedInt32"));

-        }

-

-    }

-}

+#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.Reflection;
+using Google.Protobuf.TestProtos;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+    public class DeprecatedMemberTest
+    {
+        private static void AssertIsDeprecated(MemberInfo member)
+        {
+            Assert.NotNull(member);
+            Assert.IsTrue(member.IsDefined(typeof(ObsoleteAttribute), false), "Member not obsolete: " + member);
+        }
+
+        [Test]
+        public void TestDepreatedPrimitiveValue()
+        {
+            AssertIsDeprecated(typeof(TestDeprecatedFields).GetProperty("DeprecatedInt32"));
+        }
+
+    }
+}
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/GeneratedMessageTest.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/GeneratedMessageTest.cs
index 429c51f..61f3abb 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/GeneratedMessageTest.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/GeneratedMessageTest.cs
@@ -1,725 +1,725 @@
-#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.IO;

-using Google.Protobuf.TestProtos;

-using NUnit.Framework;

-using System.Collections;

-using System.Collections.Generic;

-using System.Linq;

-using Google.Protobuf.WellKnownTypes;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Tests around the generated TestAllTypes message.

-    /// </summary>

-    public class GeneratedMessageTest

-    {

-        [Test]

-        public void EmptyMessageFieldDistinctFromMissingMessageField()

-        {

-            // This demonstrates what we're really interested in...

-            var message1 = new TestAllTypes { SingleForeignMessage = new ForeignMessage() };

-            var message2 = new TestAllTypes(); // SingleForeignMessage is null

-            EqualityTester.AssertInequality(message1, message2);

-        }

-

-        [Test]

-        public void DefaultValues()

-        {

-            // Single fields

-            var message = new TestAllTypes();

-            Assert.AreEqual(false, message.SingleBool);

-            Assert.AreEqual(ByteString.Empty, message.SingleBytes);

-            Assert.AreEqual(0.0, message.SingleDouble);

-            Assert.AreEqual(0, message.SingleFixed32);

-            Assert.AreEqual(0L, message.SingleFixed64);

-            Assert.AreEqual(0.0f, message.SingleFloat);

-            Assert.AreEqual(ForeignEnum.ForeignUnspecified, message.SingleForeignEnum);

-            Assert.IsNull(message.SingleForeignMessage);

-            Assert.AreEqual(ImportEnum.Unspecified, message.SingleImportEnum);

-            Assert.IsNull(message.SingleImportMessage);

-            Assert.AreEqual(0, message.SingleInt32);

-            Assert.AreEqual(0L, message.SingleInt64);

-            Assert.AreEqual(TestAllTypes.Types.NestedEnum.Unspecified, message.SingleNestedEnum);

-            Assert.IsNull(message.SingleNestedMessage);

-            Assert.IsNull(message.SinglePublicImportMessage);

-            Assert.AreEqual(0, message.SingleSfixed32);

-            Assert.AreEqual(0L, message.SingleSfixed64);

-            Assert.AreEqual(0, message.SingleSint32);

-            Assert.AreEqual(0L, message.SingleSint64);

-            Assert.AreEqual("", message.SingleString);

-            Assert.AreEqual(0U, message.SingleUint32);

-            Assert.AreEqual(0UL, message.SingleUint64);

-

-            // Repeated fields

-            Assert.AreEqual(0, message.RepeatedBool.Count);

-            Assert.AreEqual(0, message.RepeatedBytes.Count);

-            Assert.AreEqual(0, message.RepeatedDouble.Count);

-            Assert.AreEqual(0, message.RepeatedFixed32.Count);

-            Assert.AreEqual(0, message.RepeatedFixed64.Count);

-            Assert.AreEqual(0, message.RepeatedFloat.Count);

-            Assert.AreEqual(0, message.RepeatedForeignEnum.Count);

-            Assert.AreEqual(0, message.RepeatedForeignMessage.Count);

-            Assert.AreEqual(0, message.RepeatedImportEnum.Count);

-            Assert.AreEqual(0, message.RepeatedImportMessage.Count);

-            Assert.AreEqual(0, message.RepeatedNestedEnum.Count);

-            Assert.AreEqual(0, message.RepeatedNestedMessage.Count);

-            Assert.AreEqual(0, message.RepeatedPublicImportMessage.Count);

-            Assert.AreEqual(0, message.RepeatedSfixed32.Count);

-            Assert.AreEqual(0, message.RepeatedSfixed64.Count);

-            Assert.AreEqual(0, message.RepeatedSint32.Count);

-            Assert.AreEqual(0, message.RepeatedSint64.Count);

-            Assert.AreEqual(0, message.RepeatedString.Count);

-            Assert.AreEqual(0, message.RepeatedUint32.Count);

-            Assert.AreEqual(0, message.RepeatedUint64.Count);

-

-            // Oneof fields

-            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);

-            Assert.AreEqual(0, message.OneofUint32);

-            Assert.AreEqual("", message.OneofString);

-            Assert.AreEqual(ByteString.Empty, message.OneofBytes);

-            Assert.IsNull(message.OneofNestedMessage);

-        }

-

-        [Test]

-        public void NullStringAndBytesRejected()

-        {

-            var message = new TestAllTypes();

-            Assert.Throws<ArgumentNullException>(() => message.SingleString = null);

-            Assert.Throws<ArgumentNullException>(() => message.OneofString = null);

-            Assert.Throws<ArgumentNullException>(() => message.SingleBytes = null);

-            Assert.Throws<ArgumentNullException>(() => message.OneofBytes = null);

-        }

-

-        [Test]

-        public void RoundTrip_Empty()

-        {

-            var message = new TestAllTypes();

-            // Without setting any values, there's nothing to write.

-            byte[] bytes = message.ToByteArray();

-            Assert.AreEqual(0, bytes.Length);

-            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);

-            Assert.AreEqual(message, parsed);

-        }

-

-        [Test]

-        public void RoundTrip_SingleValues()

-        {

-            var message = new TestAllTypes

-            {

-                SingleBool = true,

-                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),

-                SingleDouble = 23.5,

-                SingleFixed32 = 23,

-                SingleFixed64 = 1234567890123,

-                SingleFloat = 12.25f,

-                SingleForeignEnum = ForeignEnum.ForeignBar,

-                SingleForeignMessage = new ForeignMessage { C = 10 },

-                SingleImportEnum = ImportEnum.ImportBaz,

-                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

-            };

-

-            byte[] bytes = message.ToByteArray();

-            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);

-            Assert.AreEqual(message, parsed);

-        }

-

-        [Test]

-        public void RoundTrip_RepeatedValues()

-        {

-            var message = new TestAllTypes

-            {

-                RepeatedBool = { true, false },

-                RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },

-                RepeatedDouble = { -12.25, 23.5 },

-                RepeatedFixed32 = { uint.MaxValue, 23 },

-                RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },

-                RepeatedFloat = { 100f, 12.25f },

-                RepeatedForeignEnum = { ForeignEnum.ForeignFoo, ForeignEnum.ForeignBar },

-                RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } },

-                RepeatedImportEnum = { ImportEnum.ImportBaz, ImportEnum.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 }

-            };

-

-            byte[] bytes = message.ToByteArray();

-            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);

-            Assert.AreEqual(message, parsed);

-        }

-

-        // Note that not every map within map_unittest_proto3 is used. They all go through very

-        // similar code paths. The fact that all maps are present is validation that we have codecs

-        // for every type.

-        [Test]

-        public void RoundTrip_Maps()

-        {

-            var message = new TestMap

-            {

-                MapBoolBool = {

-                    { false, true },

-                    { true, false }

-                },

-                MapInt32Bytes = {

-                    { 5, ByteString.CopyFrom(6, 7, 8) },

-                    { 25, ByteString.CopyFrom(1, 2, 3, 4, 5) },

-                    { 10, ByteString.Empty }

-                },

-                MapInt32ForeignMessage = {

-                    { 0, new ForeignMessage { C = 10 } },

-                    { 5, new ForeignMessage() },

-                },

-                MapInt32Enum = {

-                    { 1, MapEnum.Bar },

-                    { 2000, MapEnum.Foo }

-                }

-            };

-

-            byte[] bytes = message.ToByteArray();

-            TestMap parsed = TestMap.Parser.ParseFrom(bytes);

-            Assert.AreEqual(message, parsed);

-        }

-

-        [Test]

-        public void MapWithEmptyEntry()

-        {

-            var message = new TestMap

-            {

-                MapInt32Bytes = { { 0, ByteString.Empty } }

-            };

-

-            byte[] bytes = message.ToByteArray();

-            Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)

-

-            var parsed = TestMap.Parser.ParseFrom(bytes);

-            Assert.AreEqual(1, parsed.MapInt32Bytes.Count);

-            Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);

-        }

-

-        [Test]

-        public void MapWithOnlyValue()

-        {

-            // Hand-craft the stream to contain a single entry with just a value.

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-            output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);

-            var nestedMessage = new ForeignMessage { C = 20 };

-            // Size of the entry (tag, size written by WriteMessage, data written by WriteMessage)

-            output.WriteLength(2 + nestedMessage.CalculateSize());

-            output.WriteTag(2, WireFormat.WireType.LengthDelimited);

-            output.WriteMessage(nestedMessage);

-            output.Flush();

-

-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());

-            Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);

-        }

-

-        [Test]

-        public void MapWithOnlyKey_PrimitiveValue()

-        {

-            // Hand-craft the stream to contain a single entry with just a key.

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-            output.WriteTag(TestMap.MapInt32DoubleFieldNumber, WireFormat.WireType.LengthDelimited);

-            int key = 10;

-            output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key);

-            output.Flush();

-

-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());

-            Assert.AreEqual(0.0, parsed.MapInt32Double[key]);

-        }

-

-        [Test]

-        public void MapWithOnlyKey_MessageValue()

-        {

-            // Hand-craft the stream to contain a single entry with just a key.

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-            output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);

-            int key = 10;

-            output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key);

-            output.Flush();

-

-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());

-            Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);

-        }

-

-        [Test]

-        public void MapIgnoresExtraFieldsWithinEntryMessages()

-        {

-            // Hand-craft the stream to contain a single entry with three fields

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-

-            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);

-

-            var key = 10; // Field 1 

-            var value = 20; // Field 2

-            var extra = 30; // Field 3

-

-            // Each field can be represented in a single byte, with a single byte tag.

-            // Total message size: 6 bytes.

-            output.WriteLength(6);

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key);

-            output.WriteTag(2, WireFormat.WireType.Varint);

-            output.WriteInt32(value);

-            output.WriteTag(3, WireFormat.WireType.Varint);

-            output.WriteInt32(extra);

-            output.Flush();

-

-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());

-            Assert.AreEqual(value, parsed.MapInt32Int32[key]);

-        }

-

-        [Test]

-        public void MapFieldOrderIsIrrelevant()

-        {

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-

-            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);

-

-            var key = 10;

-            var value = 20;

-

-            // Each field can be represented in a single byte, with a single byte tag.

-            // Total message size: 4 bytes.

-            output.WriteLength(4);

-            output.WriteTag(2, WireFormat.WireType.Varint);

-            output.WriteInt32(value);

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key);

-            output.Flush();

-

-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());

-            Assert.AreEqual(value, parsed.MapInt32Int32[key]);

-        }

-

-        [Test]

-        public void MapNonContiguousEntries()

-        {

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-

-            // Message structure:

-            // Entry for MapInt32Int32

-            // Entry for MapStringString

-            // Entry for MapInt32Int32

-

-            // First entry

-            var key1 = 10;

-            var value1 = 20;

-            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteLength(4);

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key1);

-            output.WriteTag(2, WireFormat.WireType.Varint);

-            output.WriteInt32(value1);

-

-            // Second entry

-            var key2 = "a";

-            var value2 = "b";

-            output.WriteTag(TestMap.MapStringStringFieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteLength(6); // 3 bytes per entry: tag, size, character

-            output.WriteTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteString(key2);

-            output.WriteTag(2, WireFormat.WireType.LengthDelimited);

-            output.WriteString(value2);

-

-            // Third entry

-            var key3 = 15;

-            var value3 = 25;

-            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteLength(4);

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key3);

-            output.WriteTag(2, WireFormat.WireType.Varint);

-            output.WriteInt32(value3);

-

-            output.Flush();

-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());

-            var expected = new TestMap

-            {

-                MapInt32Int32 = { { key1, value1 }, { key3, value3 } },

-                MapStringString = { { key2, value2 } }

-            };

-            Assert.AreEqual(expected, parsed);

-        }

-

-        [Test]

-        public void DuplicateKeys_LastEntryWins()

-        {

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-

-            var key = 10;

-            var value1 = 20;

-            var value2 = 30;

-

-            // First entry

-            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteLength(4);

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key);

-            output.WriteTag(2, WireFormat.WireType.Varint);

-            output.WriteInt32(value1);

-

-            // Second entry - same key, different value

-            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteLength(4);

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key);

-            output.WriteTag(2, WireFormat.WireType.Varint);

-            output.WriteInt32(value2);

-            output.Flush();

-

-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());

-            Assert.AreEqual(value2, parsed.MapInt32Int32[key]);

-        }

-

-        [Test]

-        public void CloneSingleNonMessageValues()

-        {

-            var original = new TestAllTypes

-            {

-                SingleBool = true,

-                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),

-                SingleDouble = 23.5,

-                SingleFixed32 = 23,

-                SingleFixed64 = 1234567890123,

-                SingleFloat = 12.25f,

-                SingleInt32 = 100,

-                SingleInt64 = 3210987654321,

-                SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,

-                SingleSfixed32 = -123,

-                SingleSfixed64 = -12345678901234,

-                SingleSint32 = -456,

-                SingleSint64 = -12345678901235,

-                SingleString = "test",

-                SingleUint32 = uint.MaxValue,

-                SingleUint64 = ulong.MaxValue

-            };

-            var clone = original.Clone();

-            Assert.AreNotSame(original, clone);

-            Assert.AreEqual(original, clone);

-            // Just as a single example

-            clone.SingleInt32 = 150;

-            Assert.AreNotEqual(original, clone);

-        }

-

-        [Test]

-        public void CloneRepeatedNonMessageValues()

-        {

-            var original = new TestAllTypes

-            {

-                RepeatedBool = { true, false },

-                RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },

-                RepeatedDouble = { -12.25, 23.5 },

-                RepeatedFixed32 = { uint.MaxValue, 23 },

-                RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },

-                RepeatedFloat = { 100f, 12.25f },

-                RepeatedInt32 = { 100, 200 },

-                RepeatedInt64 = { 3210987654321, long.MaxValue },

-                RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },

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

-            };

-

-            var clone = original.Clone();

-            Assert.AreNotSame(original, clone);

-            Assert.AreEqual(original, clone);

-            // Just as a single example

-            clone.RepeatedDouble.Add(25.5);

-            Assert.AreNotEqual(original, clone);

-        }

-

-        [Test]

-        public void CloneSingleMessageField()

-        {

-            var original = new TestAllTypes

-            {

-                SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }

-            };

-

-            var clone = original.Clone();

-            Assert.AreNotSame(original, clone);

-            Assert.AreNotSame(original.SingleNestedMessage, clone.SingleNestedMessage);

-            Assert.AreEqual(original, clone);

-

-            clone.SingleNestedMessage.Bb = 30;

-            Assert.AreNotEqual(original, clone);

-        }

-

-        [Test]

-        public void CloneRepeatedMessageField()

-        {

-            var original = new TestAllTypes

-            {

-                RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 20 } }

-            };

-

-            var clone = original.Clone();

-            Assert.AreNotSame(original, clone);

-            Assert.AreNotSame(original.RepeatedNestedMessage, clone.RepeatedNestedMessage);

-            Assert.AreNotSame(original.RepeatedNestedMessage[0], clone.RepeatedNestedMessage[0]);

-            Assert.AreEqual(original, clone);

-

-            clone.RepeatedNestedMessage[0].Bb = 30;

-            Assert.AreNotEqual(original, clone);

-        }

-

-        [Test]

-        public void CloneOneofField()

-        {

-            var original = new TestAllTypes

-            {

-                OneofNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }

-            };

-

-            var clone = original.Clone();

-            Assert.AreNotSame(original, clone);

-            Assert.AreEqual(original, clone);

-

-            // We should have cloned the message

-            original.OneofNestedMessage.Bb = 30;

-            Assert.AreNotEqual(original, clone);

-        }

-

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

-        {

-            var message1 = new TestAllTypes { OneofString = "" };

-            var message2 = new TestAllTypes { OneofUint32 = 0 };

-            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message1.OneofFieldCase);

-            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);

-            Assert.AreNotEqual(message1, message2);

-        }

-

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

-        }

-

-        [Test]

-        public void DiscardUnknownFields_RealDataStillRead()

-        {

-            var message = SampleMessages.CreateFullTestAllTypes();

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-            var unusedFieldNumber = 23456;

-            Assert.IsFalse(TestAllTypes.Descriptor.Fields.InDeclarationOrder().Select(x => x.FieldNumber).Contains(unusedFieldNumber));

-            output.WriteTag(unusedFieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteString("ignore me");

-            message.WriteTo(output);

-            output.Flush();

-

-            stream.Position = 0;

-            var parsed = TestAllTypes.Parser.ParseFrom(stream);

-	    // TODO(jieluo): Add test back after DiscardUnknownFields is supported

-            // Assert.AreEqual(message, parsed);

-        }

-

-        [Test]

-        public void DiscardUnknownFields_AllTypes()

-        {

-            // Simple way of ensuring we can skip all kinds of fields.

-            var data = SampleMessages.CreateFullTestAllTypes().ToByteArray();

-            var empty = Empty.Parser.ParseFrom(data);

-	    // TODO(jieluo): Add test back after DiscardUnknownField is supported.

-            // Assert.AreEqual(new Empty(), empty);

-        }

-

-        // This was originally seen as a conformance test failure.

-        [Test]

-        public void TruncatedMessageFieldThrows()

-        {

-            // 130, 3 is the message tag

-            // 1 is the data length - but there's no data.

-            var data = new byte[] { 130, 3, 1 };

-            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(data));

-        }

-

-        /// <summary>

-        /// Demonstrates current behaviour with an extraneous end group tag - see issue 688

-        /// for details; we may want to change this.

-        /// </summary>

-        [Test]

-        public void ExtraEndGroupThrows()

-        {

-            var message = SampleMessages.CreateFullTestAllTypes();

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-

-            output.WriteTag(TestAllTypes.SingleFixed32FieldNumber, WireFormat.WireType.Fixed32);

-            output.WriteFixed32(123);

-            output.WriteTag(100, WireFormat.WireType.EndGroup);

-

-            output.Flush();

-

-            stream.Position = 0;

-            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(stream));

-        }

-

-        [Test]

-        public void CustomDiagnosticMessage_DirectToStringCall()

-        {

-            var message = new ForeignMessage { C = 31 };

-            Assert.AreEqual("{ \"c\": 31, \"@cInHex\": \"1f\" }", message.ToString());

-            Assert.AreEqual("{ \"c\": 31 }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void CustomDiagnosticMessage_Nested()

-        {

-            var message = new TestAllTypes { SingleForeignMessage = new ForeignMessage { C = 16 } };

-            Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16, \"@cInHex\": \"10\" } }", message.ToString());

-            Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16 } }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void CustomDiagnosticMessage_DirectToTextWriterCall()

-        {

-            var message = new ForeignMessage { C = 31 };

-            var writer = new StringWriter();

-            JsonFormatter.Default.Format(message, writer);

-            Assert.AreEqual("{ \"c\": 31 }", writer.ToString());

-        }

-    }

-}

+#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.IO;
+using Google.Protobuf.TestProtos;
+using NUnit.Framework;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Google.Protobuf.WellKnownTypes;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Tests around the generated TestAllTypes message.
+    /// </summary>
+    public class GeneratedMessageTest
+    {
+        [Test]
+        public void EmptyMessageFieldDistinctFromMissingMessageField()
+        {
+            // This demonstrates what we're really interested in...
+            var message1 = new TestAllTypes { SingleForeignMessage = new ForeignMessage() };
+            var message2 = new TestAllTypes(); // SingleForeignMessage is null
+            EqualityTester.AssertInequality(message1, message2);
+        }
+
+        [Test]
+        public void DefaultValues()
+        {
+            // Single fields
+            var message = new TestAllTypes();
+            Assert.AreEqual(false, message.SingleBool);
+            Assert.AreEqual(ByteString.Empty, message.SingleBytes);
+            Assert.AreEqual(0.0, message.SingleDouble);
+            Assert.AreEqual(0, message.SingleFixed32);
+            Assert.AreEqual(0L, message.SingleFixed64);
+            Assert.AreEqual(0.0f, message.SingleFloat);
+            Assert.AreEqual(ForeignEnum.ForeignUnspecified, message.SingleForeignEnum);
+            Assert.IsNull(message.SingleForeignMessage);
+            Assert.AreEqual(ImportEnum.Unspecified, message.SingleImportEnum);
+            Assert.IsNull(message.SingleImportMessage);
+            Assert.AreEqual(0, message.SingleInt32);
+            Assert.AreEqual(0L, message.SingleInt64);
+            Assert.AreEqual(TestAllTypes.Types.NestedEnum.Unspecified, message.SingleNestedEnum);
+            Assert.IsNull(message.SingleNestedMessage);
+            Assert.IsNull(message.SinglePublicImportMessage);
+            Assert.AreEqual(0, message.SingleSfixed32);
+            Assert.AreEqual(0L, message.SingleSfixed64);
+            Assert.AreEqual(0, message.SingleSint32);
+            Assert.AreEqual(0L, message.SingleSint64);
+            Assert.AreEqual("", message.SingleString);
+            Assert.AreEqual(0U, message.SingleUint32);
+            Assert.AreEqual(0UL, message.SingleUint64);
+
+            // Repeated fields
+            Assert.AreEqual(0, message.RepeatedBool.Count);
+            Assert.AreEqual(0, message.RepeatedBytes.Count);
+            Assert.AreEqual(0, message.RepeatedDouble.Count);
+            Assert.AreEqual(0, message.RepeatedFixed32.Count);
+            Assert.AreEqual(0, message.RepeatedFixed64.Count);
+            Assert.AreEqual(0, message.RepeatedFloat.Count);
+            Assert.AreEqual(0, message.RepeatedForeignEnum.Count);
+            Assert.AreEqual(0, message.RepeatedForeignMessage.Count);
+            Assert.AreEqual(0, message.RepeatedImportEnum.Count);
+            Assert.AreEqual(0, message.RepeatedImportMessage.Count);
+            Assert.AreEqual(0, message.RepeatedNestedEnum.Count);
+            Assert.AreEqual(0, message.RepeatedNestedMessage.Count);
+            Assert.AreEqual(0, message.RepeatedPublicImportMessage.Count);
+            Assert.AreEqual(0, message.RepeatedSfixed32.Count);
+            Assert.AreEqual(0, message.RepeatedSfixed64.Count);
+            Assert.AreEqual(0, message.RepeatedSint32.Count);
+            Assert.AreEqual(0, message.RepeatedSint64.Count);
+            Assert.AreEqual(0, message.RepeatedString.Count);
+            Assert.AreEqual(0, message.RepeatedUint32.Count);
+            Assert.AreEqual(0, message.RepeatedUint64.Count);
+
+            // Oneof fields
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
+            Assert.AreEqual(0, message.OneofUint32);
+            Assert.AreEqual("", message.OneofString);
+            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+            Assert.IsNull(message.OneofNestedMessage);
+        }
+
+        [Test]
+        public void NullStringAndBytesRejected()
+        {
+            var message = new TestAllTypes();
+            Assert.Throws<ArgumentNullException>(() => message.SingleString = null);
+            Assert.Throws<ArgumentNullException>(() => message.OneofString = null);
+            Assert.Throws<ArgumentNullException>(() => message.SingleBytes = null);
+            Assert.Throws<ArgumentNullException>(() => message.OneofBytes = null);
+        }
+
+        [Test]
+        public void RoundTrip_Empty()
+        {
+            var message = new TestAllTypes();
+            // Without setting any values, there's nothing to write.
+            byte[] bytes = message.ToByteArray();
+            Assert.AreEqual(0, bytes.Length);
+            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
+            Assert.AreEqual(message, parsed);
+        }
+
+        [Test]
+        public void RoundTrip_SingleValues()
+        {
+            var message = new TestAllTypes
+            {
+                SingleBool = true,
+                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
+                SingleDouble = 23.5,
+                SingleFixed32 = 23,
+                SingleFixed64 = 1234567890123,
+                SingleFloat = 12.25f,
+                SingleForeignEnum = ForeignEnum.ForeignBar,
+                SingleForeignMessage = new ForeignMessage { C = 10 },
+                SingleImportEnum = ImportEnum.ImportBaz,
+                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
+            };
+
+            byte[] bytes = message.ToByteArray();
+            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
+            Assert.AreEqual(message, parsed);
+        }
+
+        [Test]
+        public void RoundTrip_RepeatedValues()
+        {
+            var message = new TestAllTypes
+            {
+                RepeatedBool = { true, false },
+                RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
+                RepeatedDouble = { -12.25, 23.5 },
+                RepeatedFixed32 = { uint.MaxValue, 23 },
+                RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
+                RepeatedFloat = { 100f, 12.25f },
+                RepeatedForeignEnum = { ForeignEnum.ForeignFoo, ForeignEnum.ForeignBar },
+                RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } },
+                RepeatedImportEnum = { ImportEnum.ImportBaz, ImportEnum.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 }
+            };
+
+            byte[] bytes = message.ToByteArray();
+            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
+            Assert.AreEqual(message, parsed);
+        }
+
+        // Note that not every map within map_unittest_proto3 is used. They all go through very
+        // similar code paths. The fact that all maps are present is validation that we have codecs
+        // for every type.
+        [Test]
+        public void RoundTrip_Maps()
+        {
+            var message = new TestMap
+            {
+                MapBoolBool = {
+                    { false, true },
+                    { true, false }
+                },
+                MapInt32Bytes = {
+                    { 5, ByteString.CopyFrom(6, 7, 8) },
+                    { 25, ByteString.CopyFrom(1, 2, 3, 4, 5) },
+                    { 10, ByteString.Empty }
+                },
+                MapInt32ForeignMessage = {
+                    { 0, new ForeignMessage { C = 10 } },
+                    { 5, new ForeignMessage() },
+                },
+                MapInt32Enum = {
+                    { 1, MapEnum.Bar },
+                    { 2000, MapEnum.Foo }
+                }
+            };
+
+            byte[] bytes = message.ToByteArray();
+            TestMap parsed = TestMap.Parser.ParseFrom(bytes);
+            Assert.AreEqual(message, parsed);
+        }
+
+        [Test]
+        public void MapWithEmptyEntry()
+        {
+            var message = new TestMap
+            {
+                MapInt32Bytes = { { 0, ByteString.Empty } }
+            };
+
+            byte[] bytes = message.ToByteArray();
+            Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)
+
+            var parsed = TestMap.Parser.ParseFrom(bytes);
+            Assert.AreEqual(1, parsed.MapInt32Bytes.Count);
+            Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);
+        }
+
+        [Test]
+        public void MapWithOnlyValue()
+        {
+            // Hand-craft the stream to contain a single entry with just a value.
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+            output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
+            var nestedMessage = new ForeignMessage { C = 20 };
+            // Size of the entry (tag, size written by WriteMessage, data written by WriteMessage)
+            output.WriteLength(2 + nestedMessage.CalculateSize());
+            output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+            output.WriteMessage(nestedMessage);
+            output.Flush();
+
+            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+            Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
+        }
+
+        [Test]
+        public void MapWithOnlyKey_PrimitiveValue()
+        {
+            // Hand-craft the stream to contain a single entry with just a key.
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+            output.WriteTag(TestMap.MapInt32DoubleFieldNumber, WireFormat.WireType.LengthDelimited);
+            int key = 10;
+            output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key);
+            output.Flush();
+
+            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+            Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
+        }
+
+        [Test]
+        public void MapWithOnlyKey_MessageValue()
+        {
+            // Hand-craft the stream to contain a single entry with just a key.
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+            output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
+            int key = 10;
+            output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key);
+            output.Flush();
+
+            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+            Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
+        }
+
+        [Test]
+        public void MapIgnoresExtraFieldsWithinEntryMessages()
+        {
+            // Hand-craft the stream to contain a single entry with three fields
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+
+            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+
+            var key = 10; // Field 1 
+            var value = 20; // Field 2
+            var extra = 30; // Field 3
+
+            // Each field can be represented in a single byte, with a single byte tag.
+            // Total message size: 6 bytes.
+            output.WriteLength(6);
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key);
+            output.WriteTag(2, WireFormat.WireType.Varint);
+            output.WriteInt32(value);
+            output.WriteTag(3, WireFormat.WireType.Varint);
+            output.WriteInt32(extra);
+            output.Flush();
+
+            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+            Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+        }
+
+        [Test]
+        public void MapFieldOrderIsIrrelevant()
+        {
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+
+            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+
+            var key = 10;
+            var value = 20;
+
+            // Each field can be represented in a single byte, with a single byte tag.
+            // Total message size: 4 bytes.
+            output.WriteLength(4);
+            output.WriteTag(2, WireFormat.WireType.Varint);
+            output.WriteInt32(value);
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key);
+            output.Flush();
+
+            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+            Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+        }
+
+        [Test]
+        public void MapNonContiguousEntries()
+        {
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+
+            // Message structure:
+            // Entry for MapInt32Int32
+            // Entry for MapStringString
+            // Entry for MapInt32Int32
+
+            // First entry
+            var key1 = 10;
+            var value1 = 20;
+            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteLength(4);
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key1);
+            output.WriteTag(2, WireFormat.WireType.Varint);
+            output.WriteInt32(value1);
+
+            // Second entry
+            var key2 = "a";
+            var value2 = "b";
+            output.WriteTag(TestMap.MapStringStringFieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteLength(6); // 3 bytes per entry: tag, size, character
+            output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteString(key2);
+            output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+            output.WriteString(value2);
+
+            // Third entry
+            var key3 = 15;
+            var value3 = 25;
+            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteLength(4);
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key3);
+            output.WriteTag(2, WireFormat.WireType.Varint);
+            output.WriteInt32(value3);
+
+            output.Flush();
+            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+            var expected = new TestMap
+            {
+                MapInt32Int32 = { { key1, value1 }, { key3, value3 } },
+                MapStringString = { { key2, value2 } }
+            };
+            Assert.AreEqual(expected, parsed);
+        }
+
+        [Test]
+        public void DuplicateKeys_LastEntryWins()
+        {
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+
+            var key = 10;
+            var value1 = 20;
+            var value2 = 30;
+
+            // First entry
+            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteLength(4);
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key);
+            output.WriteTag(2, WireFormat.WireType.Varint);
+            output.WriteInt32(value1);
+
+            // Second entry - same key, different value
+            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteLength(4);
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key);
+            output.WriteTag(2, WireFormat.WireType.Varint);
+            output.WriteInt32(value2);
+            output.Flush();
+
+            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+            Assert.AreEqual(value2, parsed.MapInt32Int32[key]);
+        }
+
+        [Test]
+        public void CloneSingleNonMessageValues()
+        {
+            var original = new TestAllTypes
+            {
+                SingleBool = true,
+                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
+                SingleDouble = 23.5,
+                SingleFixed32 = 23,
+                SingleFixed64 = 1234567890123,
+                SingleFloat = 12.25f,
+                SingleInt32 = 100,
+                SingleInt64 = 3210987654321,
+                SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
+                SingleSfixed32 = -123,
+                SingleSfixed64 = -12345678901234,
+                SingleSint32 = -456,
+                SingleSint64 = -12345678901235,
+                SingleString = "test",
+                SingleUint32 = uint.MaxValue,
+                SingleUint64 = ulong.MaxValue
+            };
+            var clone = original.Clone();
+            Assert.AreNotSame(original, clone);
+            Assert.AreEqual(original, clone);
+            // Just as a single example
+            clone.SingleInt32 = 150;
+            Assert.AreNotEqual(original, clone);
+        }
+
+        [Test]
+        public void CloneRepeatedNonMessageValues()
+        {
+            var original = new TestAllTypes
+            {
+                RepeatedBool = { true, false },
+                RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
+                RepeatedDouble = { -12.25, 23.5 },
+                RepeatedFixed32 = { uint.MaxValue, 23 },
+                RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
+                RepeatedFloat = { 100f, 12.25f },
+                RepeatedInt32 = { 100, 200 },
+                RepeatedInt64 = { 3210987654321, long.MaxValue },
+                RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },
+                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 }
+            };
+
+            var clone = original.Clone();
+            Assert.AreNotSame(original, clone);
+            Assert.AreEqual(original, clone);
+            // Just as a single example
+            clone.RepeatedDouble.Add(25.5);
+            Assert.AreNotEqual(original, clone);
+        }
+
+        [Test]
+        public void CloneSingleMessageField()
+        {
+            var original = new TestAllTypes
+            {
+                SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
+            };
+
+            var clone = original.Clone();
+            Assert.AreNotSame(original, clone);
+            Assert.AreNotSame(original.SingleNestedMessage, clone.SingleNestedMessage);
+            Assert.AreEqual(original, clone);
+
+            clone.SingleNestedMessage.Bb = 30;
+            Assert.AreNotEqual(original, clone);
+        }
+
+        [Test]
+        public void CloneRepeatedMessageField()
+        {
+            var original = new TestAllTypes
+            {
+                RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 20 } }
+            };
+
+            var clone = original.Clone();
+            Assert.AreNotSame(original, clone);
+            Assert.AreNotSame(original.RepeatedNestedMessage, clone.RepeatedNestedMessage);
+            Assert.AreNotSame(original.RepeatedNestedMessage[0], clone.RepeatedNestedMessage[0]);
+            Assert.AreEqual(original, clone);
+
+            clone.RepeatedNestedMessage[0].Bb = 30;
+            Assert.AreNotEqual(original, clone);
+        }
+
+        [Test]
+        public void CloneOneofField()
+        {
+            var original = new TestAllTypes
+            {
+                OneofNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
+            };
+
+            var clone = original.Clone();
+            Assert.AreNotSame(original, clone);
+            Assert.AreEqual(original, clone);
+
+            // We should have cloned the message
+            original.OneofNestedMessage.Bb = 30;
+            Assert.AreNotEqual(original, clone);
+        }
+
+        [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 Oneof_DefaultValuesNotEqual()
+        {
+            var message1 = new TestAllTypes { OneofString = "" };
+            var message2 = new TestAllTypes { OneofUint32 = 0 };
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message1.OneofFieldCase);
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
+            Assert.AreNotEqual(message1, message2);
+        }
+
+        [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);
+        }
+
+        [Test]
+        public void DiscardUnknownFields_RealDataStillRead()
+        {
+            var message = SampleMessages.CreateFullTestAllTypes();
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+            var unusedFieldNumber = 23456;
+            Assert.IsFalse(TestAllTypes.Descriptor.Fields.InDeclarationOrder().Select(x => x.FieldNumber).Contains(unusedFieldNumber));
+            output.WriteTag(unusedFieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteString("ignore me");
+            message.WriteTo(output);
+            output.Flush();
+
+            stream.Position = 0;
+            var parsed = TestAllTypes.Parser.ParseFrom(stream);
+	    // TODO(jieluo): Add test back after DiscardUnknownFields is supported
+            // Assert.AreEqual(message, parsed);
+        }
+
+        [Test]
+        public void DiscardUnknownFields_AllTypes()
+        {
+            // Simple way of ensuring we can skip all kinds of fields.
+            var data = SampleMessages.CreateFullTestAllTypes().ToByteArray();
+            var empty = Empty.Parser.ParseFrom(data);
+	    // TODO(jieluo): Add test back after DiscardUnknownField is supported.
+            // Assert.AreEqual(new Empty(), empty);
+        }
+
+        // This was originally seen as a conformance test failure.
+        [Test]
+        public void TruncatedMessageFieldThrows()
+        {
+            // 130, 3 is the message tag
+            // 1 is the data length - but there's no data.
+            var data = new byte[] { 130, 3, 1 };
+            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(data));
+        }
+
+        /// <summary>
+        /// Demonstrates current behaviour with an extraneous end group tag - see issue 688
+        /// for details; we may want to change this.
+        /// </summary>
+        [Test]
+        public void ExtraEndGroupThrows()
+        {
+            var message = SampleMessages.CreateFullTestAllTypes();
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+
+            output.WriteTag(TestAllTypes.SingleFixed32FieldNumber, WireFormat.WireType.Fixed32);
+            output.WriteFixed32(123);
+            output.WriteTag(100, WireFormat.WireType.EndGroup);
+
+            output.Flush();
+
+            stream.Position = 0;
+            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(stream));
+        }
+
+        [Test]
+        public void CustomDiagnosticMessage_DirectToStringCall()
+        {
+            var message = new ForeignMessage { C = 31 };
+            Assert.AreEqual("{ \"c\": 31, \"@cInHex\": \"1f\" }", message.ToString());
+            Assert.AreEqual("{ \"c\": 31 }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void CustomDiagnosticMessage_Nested()
+        {
+            var message = new TestAllTypes { SingleForeignMessage = new ForeignMessage { C = 16 } };
+            Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16, \"@cInHex\": \"10\" } }", message.ToString());
+            Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16 } }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void CustomDiagnosticMessage_DirectToTextWriterCall()
+        {
+            var message = new ForeignMessage { C = 31 };
+            var writer = new StringWriter();
+            JsonFormatter.Default.Format(message, writer);
+            Assert.AreEqual("{ \"c\": 31 }", writer.ToString());
+        }
+    }
+}
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/IssuesTest.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/IssuesTest.cs
index a38d6b0..b4022a2 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/IssuesTest.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/IssuesTest.cs
@@ -1,82 +1,82 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using Google.Protobuf.Reflection;

-using UnitTest.Issues.TestProtos;

-using NUnit.Framework;

-

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Tests for issues which aren't easily compartmentalized into other unit tests.

-    /// </summary>

-    public class IssuesTest

-    {

-        // Issue 45

-        [Test]

-        public void FieldCalledItem()

-        {

-            ItemField message = new ItemField { Item = 3 };

-            FieldDescriptor field = ItemField.Descriptor.FindFieldByName("item");

-            Assert.NotNull(field);

-            Assert.AreEqual(3, (int)field.Accessor.GetValue(message));

-        }

-

-        [Test]

-        public void ReservedNames()

-        {

-            var message = new ReservedNames { Types_ = 10, Descriptor_ = 20 };

-            // Underscores aren't reflected in the JSON.

-            Assert.AreEqual("{ \"types\": 10, \"descriptor\": 20 }", message.ToString());

-        }

-

-        [Test]

-        public void JsonNameParseTest()

-        {

-            var settings = new JsonParser.Settings(10, TypeRegistry.FromFiles(UnittestIssuesReflection.Descriptor));

-            var parser = new JsonParser(settings);

-

-            // It is safe to use either original field name or explicitly specified json_name

-            Assert.AreEqual(new TestJsonName { Name = "test", Description = "test2", Guid = "test3" },

-                parser.Parse<TestJsonName>("{ \"name\": \"test\", \"desc\": \"test2\", \"guid\": \"test3\" }"));

-        }

-

-        [Test]

-        public void JsonNameFormatTest()

-        {

-            var message = new TestJsonName { Name = "test", Description = "test2", Guid = "test3" };

-            Assert.AreEqual("{ \"name\": \"test\", \"desc\": \"test2\", \"exid\": \"test3\" }",

-                JsonFormatter.Default.Format(message));

-        }

-    }

-}

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using Google.Protobuf.Reflection;
+using UnitTest.Issues.TestProtos;
+using NUnit.Framework;
+
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Tests for issues which aren't easily compartmentalized into other unit tests.
+    /// </summary>
+    public class IssuesTest
+    {
+        // Issue 45
+        [Test]
+        public void FieldCalledItem()
+        {
+            ItemField message = new ItemField { Item = 3 };
+            FieldDescriptor field = ItemField.Descriptor.FindFieldByName("item");
+            Assert.NotNull(field);
+            Assert.AreEqual(3, (int)field.Accessor.GetValue(message));
+        }
+
+        [Test]
+        public void ReservedNames()
+        {
+            var message = new ReservedNames { Types_ = 10, Descriptor_ = 20 };
+            // Underscores aren't reflected in the JSON.
+            Assert.AreEqual("{ \"types\": 10, \"descriptor\": 20 }", message.ToString());
+        }
+
+        [Test]
+        public void JsonNameParseTest()
+        {
+            var settings = new JsonParser.Settings(10, TypeRegistry.FromFiles(UnittestIssuesReflection.Descriptor));
+            var parser = new JsonParser(settings);
+
+            // It is safe to use either original field name or explicitly specified json_name
+            Assert.AreEqual(new TestJsonName { Name = "test", Description = "test2", Guid = "test3" },
+                parser.Parse<TestJsonName>("{ \"name\": \"test\", \"desc\": \"test2\", \"guid\": \"test3\" }"));
+        }
+
+        [Test]
+        public void JsonNameFormatTest()
+        {
+            var message = new TestJsonName { Name = "test", Description = "test2", Guid = "test3" };
+            Assert.AreEqual("{ \"name\": \"test\", \"desc\": \"test2\", \"exid\": \"test3\" }",
+                JsonFormatter.Default.Format(message));
+        }
+    }
+}
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/TestCornerCases.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/TestCornerCases.cs
index 248f5fa..859b49c 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/TestCornerCases.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/TestCornerCases.cs
@@ -1,62 +1,62 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-    

-using UnitTest.Issues.TestProtos;

-using NUnit.Framework;

-

-namespace Google.Protobuf

-{

-    public class TestCornerCases

-    {

-        [Test]

-        public void TestRoundTripNegativeEnums()

-        {

-            NegativeEnumMessage msg = new NegativeEnumMessage

-            {

-                Value = NegativeEnum.MinusOne,

-                Values = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow },

-                PackedValues = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow }

-            };

-

-            Assert.AreEqual(58, msg.CalculateSize());

-

-            byte[] bytes = new byte[58];

-            CodedOutputStream output = new CodedOutputStream(bytes);

-

-            msg.WriteTo(output);

-            Assert.AreEqual(0, output.SpaceLeft);

-

-            NegativeEnumMessage copy = NegativeEnumMessage.Parser.ParseFrom(bytes);

-            Assert.AreEqual(msg, copy);

-        }

-    }

-}

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+    
+using UnitTest.Issues.TestProtos;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+    public class TestCornerCases
+    {
+        [Test]
+        public void TestRoundTripNegativeEnums()
+        {
+            NegativeEnumMessage msg = new NegativeEnumMessage
+            {
+                Value = NegativeEnum.MinusOne,
+                Values = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow },
+                PackedValues = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow }
+            };
+
+            Assert.AreEqual(58, msg.CalculateSize());
+
+            byte[] bytes = new byte[58];
+            CodedOutputStream output = new CodedOutputStream(bytes);
+
+            msg.WriteTo(output);
+            Assert.AreEqual(0, output.SpaceLeft);
+
+            NegativeEnumMessage copy = NegativeEnumMessage.Parser.ParseFrom(bytes);
+            Assert.AreEqual(msg, copy);
+        }
+    }
+}
diff --git a/csharp/generate_protos.sh b/csharp/generate_protos.sh
index b663138..b021de2 100755
--- a/csharp/generate_protos.sh
+++ b/csharp/generate_protos.sh
@@ -13,7 +13,9 @@
 # Windows and Unix.
 if [ -z "$PROTOC" ]; then
   # TODO(jonskeet): Use an array and a for loop instead?
-  if [ -x cmake/build/Debug/protoc.exe ]; then
+  if [ -x solution/Debug/protoc.exe ]; then
+    PROTOC=solution/Debug/protoc.exe
+  elif [ -x cmake/build/Debug/protoc.exe ]; then
     PROTOC=cmake/build/Debug/protoc.exe
   elif [ -x cmake/build/Release/protoc.exe ]; then
     PROTOC=cmake/build/Release/protoc.exe
diff --git a/csharp/protos/unittest_issues.proto b/csharp/protos/unittest_issues.proto
index 388998f..f46c20e 100644
--- a/csharp/protos/unittest_issues.proto
+++ b/csharp/protos/unittest_issues.proto
@@ -156,3 +156,17 @@
   string regular_field = 1;
   optional string optional_field = 2;
 }
+
+message OneofWithNoneField {
+  oneof test {
+    string x = 1;
+    string none = 2;
+  }
+}
+
+message OneofWithNoneName {
+  oneof none {
+    string x = 1;
+    string y = 2;
+  }
+}
\ No newline at end of file
diff --git a/csharp/src/AddressBook/AddPerson.cs b/csharp/src/AddressBook/AddPerson.cs
index 62d1788..889d1d0 100644
--- a/csharp/src/AddressBook/AddPerson.cs
+++ b/csharp/src/AddressBook/AddPerson.cs
@@ -1,132 +1,132 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.IO;

-

-namespace Google.Protobuf.Examples.AddressBook

-{

-    internal class AddPerson

-    {

-        /// <summary>

-        /// Builds a person based on user input

-        /// </summary>

-        private static Person PromptForAddress(TextReader input, TextWriter output)

-        {

-            Person person = new Person();

-

-            output.Write("Enter person ID: ");

-            person.Id = int.Parse(input.ReadLine());

-

-            output.Write("Enter name: ");

-            person.Name = input.ReadLine();

-

-            output.Write("Enter email address (blank for none): ");

-            string email = input.ReadLine();

-            if (email.Length > 0)

-            {

-                person.Email = email;

-            }

-

-            while (true)

-            {

-                output.Write("Enter a phone number (or leave blank to finish): ");

-                string number = input.ReadLine();

-                if (number.Length == 0)

-                {

-                    break;

-                }

-

-                Person.Types.PhoneNumber phoneNumber = new Person.Types.PhoneNumber { Number = number };

-

-                output.Write("Is this a mobile, home, or work phone? ");

-                String type = input.ReadLine();

-                switch (type)

-                {

-                    case "mobile":

-                        phoneNumber.Type = Person.Types.PhoneType.Mobile;

-                        break;

-                    case "home":

-                        phoneNumber.Type = Person.Types.PhoneType.Home;

-                        break;

-                    case "work":

-                        phoneNumber.Type = Person.Types.PhoneType.Work;

-                        break;

-                    default:

-                        output.Write("Unknown phone type. Using default.");

-                        break;

-                }

-

-                person.Phones.Add(phoneNumber);

-            }

-            return person;

-        }

-

-        /// <summary>

-        /// Entry point - loads an existing addressbook or creates a new one,

-        /// then writes it back to the file.

-        /// </summary>

-        public static int Main(string[] args)

-        {

-            if (args.Length != 1)

-            {

-                Console.Error.WriteLine("Usage:  AddPerson ADDRESS_BOOK_FILE");

-                return -1;

-            }

-

-            AddressBook addressBook;

-

-            if (File.Exists(args[0]))

-            {

-                using (Stream file = File.OpenRead(args[0]))

-                {

-                    addressBook = AddressBook.Parser.ParseFrom(file);

-                }

-            }

-            else

-            {

-                Console.WriteLine("{0}: File not found. Creating a new file.", args[0]);

-                addressBook = new AddressBook();

-            }

-

-            // Add an address.

-            addressBook.People.Add(PromptForAddress(Console.In, Console.Out));

-

-            // Write the new address book back to disk.

-            using (Stream output = File.OpenWrite(args[0]))

-            {

-                addressBook.WriteTo(output);

-            }

-            return 0;

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.IO;
+
+namespace Google.Protobuf.Examples.AddressBook
+{
+    internal class AddPerson
+    {
+        /// <summary>
+        /// Builds a person based on user input
+        /// </summary>
+        private static Person PromptForAddress(TextReader input, TextWriter output)
+        {
+            Person person = new Person();
+
+            output.Write("Enter person ID: ");
+            person.Id = int.Parse(input.ReadLine());
+
+            output.Write("Enter name: ");
+            person.Name = input.ReadLine();
+
+            output.Write("Enter email address (blank for none): ");
+            string email = input.ReadLine();
+            if (email.Length > 0)
+            {
+                person.Email = email;
+            }
+
+            while (true)
+            {
+                output.Write("Enter a phone number (or leave blank to finish): ");
+                string number = input.ReadLine();
+                if (number.Length == 0)
+                {
+                    break;
+                }
+
+                Person.Types.PhoneNumber phoneNumber = new Person.Types.PhoneNumber { Number = number };
+
+                output.Write("Is this a mobile, home, or work phone? ");
+                String type = input.ReadLine();
+                switch (type)
+                {
+                    case "mobile":
+                        phoneNumber.Type = Person.Types.PhoneType.Mobile;
+                        break;
+                    case "home":
+                        phoneNumber.Type = Person.Types.PhoneType.Home;
+                        break;
+                    case "work":
+                        phoneNumber.Type = Person.Types.PhoneType.Work;
+                        break;
+                    default:
+                        output.Write("Unknown phone type. Using default.");
+                        break;
+                }
+
+                person.Phones.Add(phoneNumber);
+            }
+            return person;
+        }
+
+        /// <summary>
+        /// Entry point - loads an existing addressbook or creates a new one,
+        /// then writes it back to the file.
+        /// </summary>
+        public static int Main(string[] args)
+        {
+            if (args.Length != 1)
+            {
+                Console.Error.WriteLine("Usage:  AddPerson ADDRESS_BOOK_FILE");
+                return -1;
+            }
+
+            AddressBook addressBook;
+
+            if (File.Exists(args[0]))
+            {
+                using (Stream file = File.OpenRead(args[0]))
+                {
+                    addressBook = AddressBook.Parser.ParseFrom(file);
+                }
+            }
+            else
+            {
+                Console.WriteLine("{0}: File not found. Creating a new file.", args[0]);
+                addressBook = new AddressBook();
+            }
+
+            // Add an address.
+            addressBook.People.Add(PromptForAddress(Console.In, Console.Out));
+
+            // Write the new address book back to disk.
+            using (Stream output = File.OpenWrite(args[0]))
+            {
+                addressBook.WriteTo(output);
+            }
+            return 0;
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/AddressBook/ListPeople.cs b/csharp/src/AddressBook/ListPeople.cs
index 3758c1b..fdcd64d 100644
--- a/csharp/src/AddressBook/ListPeople.cs
+++ b/csharp/src/AddressBook/ListPeople.cs
@@ -1,99 +1,99 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.IO;

-

-namespace Google.Protobuf.Examples.AddressBook

-{

-    internal class ListPeople

-    {

-        /// <summary>

-        /// Iterates though all people in the AddressBook and prints info about them.

-        /// </summary>

-        private static void Print(AddressBook addressBook)

-        {

-            foreach (Person person in addressBook.People)

-            {

-                Console.WriteLine("Person ID: {0}", person.Id);

-                Console.WriteLine("  Name: {0}", person.Name);

-                if (person.Email != "")

-                {

-                    Console.WriteLine("  E-mail address: {0}", person.Email);

-                }

-

-                foreach (Person.Types.PhoneNumber phoneNumber in person.Phones)

-                {

-                    switch (phoneNumber.Type)

-                    {

-                        case Person.Types.PhoneType.Mobile:

-                            Console.Write("  Mobile phone #: ");

-                            break;

-                        case Person.Types.PhoneType.Home:

-                            Console.Write("  Home phone #: ");

-                            break;

-                        case Person.Types.PhoneType.Work:

-                            Console.Write("  Work phone #: ");

-                            break;

-                    }

-                    Console.WriteLine(phoneNumber.Number);

-                }

-            }

-        }

-

-        /// <summary>

-        /// Entry point - loads the addressbook and then displays it.

-        /// </summary>

-        public static int Main(string[] args)

-        {

-            if (args.Length != 1)

-            {

-                Console.Error.WriteLine("Usage:  ListPeople ADDRESS_BOOK_FILE");

-                return 1;

-            }

-

-            if (!File.Exists(args[0]))

-            {

-                Console.WriteLine("{0} doesn't exist. Add a person to create the file first.", args[0]);

-                return 0;

-            }

-

-            // Read the existing address book.

-            using (Stream stream = File.OpenRead(args[0]))

-            {

-                AddressBook addressBook = AddressBook.Parser.ParseFrom(stream);

-                Print(addressBook);

-            }

-            return 0;

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.IO;
+
+namespace Google.Protobuf.Examples.AddressBook
+{
+    internal class ListPeople
+    {
+        /// <summary>
+        /// Iterates though all people in the AddressBook and prints info about them.
+        /// </summary>
+        private static void Print(AddressBook addressBook)
+        {
+            foreach (Person person in addressBook.People)
+            {
+                Console.WriteLine("Person ID: {0}", person.Id);
+                Console.WriteLine("  Name: {0}", person.Name);
+                if (person.Email != "")
+                {
+                    Console.WriteLine("  E-mail address: {0}", person.Email);
+                }
+
+                foreach (Person.Types.PhoneNumber phoneNumber in person.Phones)
+                {
+                    switch (phoneNumber.Type)
+                    {
+                        case Person.Types.PhoneType.Mobile:
+                            Console.Write("  Mobile phone #: ");
+                            break;
+                        case Person.Types.PhoneType.Home:
+                            Console.Write("  Home phone #: ");
+                            break;
+                        case Person.Types.PhoneType.Work:
+                            Console.Write("  Work phone #: ");
+                            break;
+                    }
+                    Console.WriteLine(phoneNumber.Number);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Entry point - loads the addressbook and then displays it.
+        /// </summary>
+        public static int Main(string[] args)
+        {
+            if (args.Length != 1)
+            {
+                Console.Error.WriteLine("Usage:  ListPeople ADDRESS_BOOK_FILE");
+                return 1;
+            }
+
+            if (!File.Exists(args[0]))
+            {
+                Console.WriteLine("{0} doesn't exist. Add a person to create the file first.", args[0]);
+                return 0;
+            }
+
+            // Read the existing address book.
+            using (Stream stream = File.OpenRead(args[0]))
+            {
+                AddressBook addressBook = AddressBook.Parser.ParseFrom(stream);
+                Print(addressBook);
+            }
+            return 0;
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/AddressBook/Program.cs b/csharp/src/AddressBook/Program.cs
index de4867a..5bbe84c 100644
--- a/csharp/src/AddressBook/Program.cs
+++ b/csharp/src/AddressBook/Program.cs
@@ -1,95 +1,95 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-

-namespace Google.Protobuf.Examples.AddressBook

-{

-    /// <summary>

-    /// Entry point. Repeatedly prompts user for an action to take, delegating actual behaviour

-    /// to individual actions. Each action has its own Main method, so that it can be used as an

-    /// individual complete program.

-    /// </summary>

-    internal class Program

-    {

-        private static int Main(string[] args)

-        {

-            if (args.Length > 1)

-            {

-                Console.Error.WriteLine("Usage: AddressBook [file]");

-                Console.Error.WriteLine("If the filename isn't specified, \"addressbook.data\" is used instead.");

-                return 1;

-            }

-            string addressBookFile = args.Length > 0 ? args[0] : "addressbook.data";

-

-            bool stopping = false;

-            while (!stopping)

-            {

-                Console.WriteLine("Options:");

-                Console.WriteLine("  L: List contents");

-                Console.WriteLine("  A: Add new person");

-                Console.WriteLine("  Q: Quit");

-                Console.Write("Action? ");

-                Console.Out.Flush();

-                char choice = Console.ReadKey().KeyChar;

-                Console.WriteLine();

-                try

-                {

-                    switch (choice)

-                    {

-                        case 'A':

-                        case 'a':

-                            AddPerson.Main(new string[] {addressBookFile});

-                            break;

-                        case 'L':

-                        case 'l':

-                            ListPeople.Main(new string[] {addressBookFile});

-                            break;

-                        case 'Q':

-                        case 'q':

-                            stopping = true;

-                            break;

-                        default:

-                            Console.WriteLine("Unknown option: {0}", choice);

-                            break;

-                    }

-                }

-                catch (Exception e)

-                {

-                    Console.WriteLine("Exception executing action: {0}", e);

-                }

-                Console.WriteLine();

-            }

-            return 0;

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+
+namespace Google.Protobuf.Examples.AddressBook
+{
+    /// <summary>
+    /// Entry point. Repeatedly prompts user for an action to take, delegating actual behaviour
+    /// to individual actions. Each action has its own Main method, so that it can be used as an
+    /// individual complete program.
+    /// </summary>
+    internal class Program
+    {
+        private static int Main(string[] args)
+        {
+            if (args.Length > 1)
+            {
+                Console.Error.WriteLine("Usage: AddressBook [file]");
+                Console.Error.WriteLine("If the filename isn't specified, \"addressbook.data\" is used instead.");
+                return 1;
+            }
+            string addressBookFile = args.Length > 0 ? args[0] : "addressbook.data";
+
+            bool stopping = false;
+            while (!stopping)
+            {
+                Console.WriteLine("Options:");
+                Console.WriteLine("  L: List contents");
+                Console.WriteLine("  A: Add new person");
+                Console.WriteLine("  Q: Quit");
+                Console.Write("Action? ");
+                Console.Out.Flush();
+                char choice = Console.ReadKey().KeyChar;
+                Console.WriteLine();
+                try
+                {
+                    switch (choice)
+                    {
+                        case 'A':
+                        case 'a':
+                            AddPerson.Main(new string[] {addressBookFile});
+                            break;
+                        case 'L':
+                        case 'l':
+                            ListPeople.Main(new string[] {addressBookFile});
+                            break;
+                        case 'Q':
+                        case 'q':
+                            stopping = true;
+                            break;
+                        default:
+                            Console.WriteLine("Unknown option: {0}", choice);
+                            break;
+                    }
+                }
+                catch (Exception e)
+                {
+                    Console.WriteLine("Exception executing action: {0}", e);
+                }
+                Console.WriteLine();
+            }
+            return 0;
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/AddressBook/SampleUsage.cs b/csharp/src/AddressBook/SampleUsage.cs
index 941d865..8f08f0f 100644
--- a/csharp/src/AddressBook/SampleUsage.cs
+++ b/csharp/src/AddressBook/SampleUsage.cs
@@ -1,73 +1,73 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-    

-using System;

-using System.IO;

-

-namespace Google.Protobuf.Examples.AddressBook

-{

-    internal class SampleUsage

-    {

-        private static void Main()

-        {

-            byte[] bytes;

-            // Create a new person

-            Person person = new Person

-            {

-                Id = 1,

-                Name = "Foo",

-                Email = "foo@bar",

-                Phones = { new Person.Types.PhoneNumber { Number = "555-1212" } }

-            };

-            using (MemoryStream stream = new MemoryStream())

-            {

-                // Save the person to a stream

-                person.WriteTo(stream);

-                bytes = stream.ToArray();

-            }

-            Person copy = Person.Parser.ParseFrom(bytes);

-

-            AddressBook book = new AddressBook

-            {

-                People = { copy }

-            };

-            bytes = book.ToByteArray();

-            // And read the address book back again

-            AddressBook restored = AddressBook.Parser.ParseFrom(bytes);

-            // The message performs a deep-comparison on equality:

-            if (restored.People.Count != 1 || !person.Equals(restored.People[0]))

-            {

-                throw new Exception("There is a bad person in here!");

-            }

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+    
+using System;
+using System.IO;
+
+namespace Google.Protobuf.Examples.AddressBook
+{
+    internal class SampleUsage
+    {
+        private static void Main()
+        {
+            byte[] bytes;
+            // Create a new person
+            Person person = new Person
+            {
+                Id = 1,
+                Name = "Foo",
+                Email = "foo@bar",
+                Phones = { new Person.Types.PhoneNumber { Number = "555-1212" } }
+            };
+            using (MemoryStream stream = new MemoryStream())
+            {
+                // Save the person to a stream
+                person.WriteTo(stream);
+                bytes = stream.ToArray();
+            }
+            Person copy = Person.Parser.ParseFrom(bytes);
+
+            AddressBook book = new AddressBook
+            {
+                People = { copy }
+            };
+            bytes = book.ToByteArray();
+            // And read the address book back again
+            AddressBook restored = AddressBook.Parser.ParseFrom(bytes);
+            // The message performs a deep-comparison on equality:
+            if (restored.People.Count != 1 || !person.Equals(restored.People[0]))
+            {
+                throw new Exception("There is a bad person in here!");
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj b/csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj
index ac8e009a..fee5f65 100644
--- a/csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj
+++ b/csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj
@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
     <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
     <IsPackable>False</IsPackable>
@@ -15,7 +15,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
     <ProjectReference Include="..\Google.Protobuf\Google.Protobuf.csproj" />
   </ItemGroup>
 
diff --git a/csharp/src/Google.Protobuf.JsonDump/Program.cs b/csharp/src/Google.Protobuf.JsonDump/Program.cs
index 296b2f3..56e32ad 100644
--- a/csharp/src/Google.Protobuf.JsonDump/Program.cs
+++ b/csharp/src/Google.Protobuf.JsonDump/Program.cs
@@ -1,73 +1,73 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.IO;

-using System.Reflection;

-

-namespace Google.Protobuf.ProtoDump

-{

-    /// <summary>

-    /// Small utility to load a binary message and dump it in JSON format.

-    /// </summary>

-    internal class Program

-    {

-        private static int Main(string[] args)

-        {

-            if (args.Length != 2)

-            {

-                Console.Error.WriteLine("Usage: Google.Protobuf.JsonDump <descriptor type name> <input data>");

-                Console.Error.WriteLine("The descriptor type name is the fully-qualified message name,");

-                Console.Error.WriteLine("including assembly e.g. ProjectNamespace.Message,Company.Project");

-                return 1;

-            }

-            Type type = Type.GetType(args[0]);

-            if (type == null)

-            {

-                Console.Error.WriteLine("Unable to load type {0}.", args[0]);

-                return 1;

-            }

-            if (!typeof(IMessage).GetTypeInfo().IsAssignableFrom(type))

-            {

-                Console.Error.WriteLine("Type {0} doesn't implement IMessage.", args[0]);

-                return 1;

-            }

-            IMessage message = (IMessage) Activator.CreateInstance(type);

-            using (var input = File.OpenRead(args[1]))

-            {

-                message.MergeFrom(input);

-            }

-            Console.WriteLine(message);

-            return 0;

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.IO;
+using System.Reflection;
+
+namespace Google.Protobuf.ProtoDump
+{
+    /// <summary>
+    /// Small utility to load a binary message and dump it in JSON format.
+    /// </summary>
+    internal class Program
+    {
+        private static int Main(string[] args)
+        {
+            if (args.Length != 2)
+            {
+                Console.Error.WriteLine("Usage: Google.Protobuf.JsonDump <descriptor type name> <input data>");
+                Console.Error.WriteLine("The descriptor type name is the fully-qualified message name,");
+                Console.Error.WriteLine("including assembly e.g. ProjectNamespace.Message,Company.Project");
+                return 1;
+            }
+            Type type = Type.GetType(args[0]);
+            if (type == null)
+            {
+                Console.Error.WriteLine("Unable to load type {0}.", args[0]);
+                return 1;
+            }
+            if (!typeof(IMessage).GetTypeInfo().IsAssignableFrom(type))
+            {
+                Console.Error.WriteLine("Type {0} doesn't implement IMessage.", args[0]);
+                return 1;
+            }
+            IMessage message = (IMessage) Activator.CreateInstance(type);
+            using (var input = File.OpenRead(args[1]))
+            {
+                message.MergeFrom(input);
+            }
+            Console.WriteLine(message);
+            return 0;
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/Google.Protobuf.Test.TestProtos.csproj b/csharp/src/Google.Protobuf.Test.TestProtos/Google.Protobuf.Test.TestProtos.csproj
index ad8445b..8d9d64b 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/Google.Protobuf.Test.TestProtos.csproj
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/Google.Protobuf.Test.TestProtos.csproj
@@ -1,25 +1,25 @@
-<Project Sdk="Microsoft.NET.Sdk">

-

-  <!--

-    This TestProtos project is kept separate from the original test project for many reasons.

-    It allows us to make sure code can compile on a separate compiler version, different frameworks,

-    and without the internal visibility from the test project (all of which have caused issues in the past).

-  -->

-  <PropertyGroup>

-    <TargetFrameworks>net462;netstandard1.1;netstandard2.0</TargetFrameworks>

-    <LangVersion>3.0</LangVersion>

-    <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>

-    <SignAssembly>true</SignAssembly>

-    <IsPackable>False</IsPackable>

-  </PropertyGroup>

-

-  <!-- Needed for the net45 build to work on Unix. See https://github.com/dotnet/designs/pull/33 -->

-  <ItemGroup>

-    <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />

-  </ItemGroup>

-  

-  <ItemGroup>

-    <ProjectReference Include="..\Google.Protobuf\Google.Protobuf.csproj" />

-  </ItemGroup>

-

-</Project>

+<Project Sdk="Microsoft.NET.Sdk">
+
+  <!--
+    This TestProtos project is kept separate from the original test project for many reasons.
+    It allows us to make sure code can compile on a separate compiler version, different frameworks,
+    and without the internal visibility from the test project (all of which have caused issues in the past).
+  -->
+  <PropertyGroup>
+    <TargetFrameworks>net462;netstandard1.1;netstandard2.0</TargetFrameworks>
+    <LangVersion>3.0</LangVersion>
+    <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
+    <SignAssembly>true</SignAssembly>
+    <IsPackable>False</IsPackable>
+  </PropertyGroup>
+
+  <!-- Needed for the net45 build to work on Unix. See https://github.com/dotnet/designs/pull/33 -->
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.2" PrivateAssets="All" />
+  </ItemGroup>
+  
+  <ItemGroup>
+    <ProjectReference Include="..\Google.Protobuf\Google.Protobuf.csproj" />
+  </ItemGroup>
+
+</Project>
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs
index 71e803c..5a2aa3b 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs
@@ -226,7 +226,7 @@
             "dG8zLkZvcmVpZ25FbnVtOgI4ASI5CgpOZXN0ZWRFbnVtEgcKA0ZPTxAAEgcK",
             "A0JBUhABEgcKA0JBWhACEhAKA05FRxD///////////8BIlkKC0FsaWFzZWRF",
             "bnVtEg0KCUFMSUFTX0ZPTxAAEg0KCUFMSUFTX0JBUhABEg0KCUFMSUFTX0JB",
-            "WhACEgcKA1FVWBACEgcKA3F1eBACEgcKA2JBehACGgIQAUINCgtvbmVvZl9m",
+            "WhACEgcKA01PTxACEgcKA21vbxACEgcKA2JBehACGgIQAUINCgtvbmVvZl9m",
             "aWVsZEoGCPUDEP8DIhsKDkZvcmVpZ25NZXNzYWdlEgkKAWMYASABKAUiFgoU",
             "TnVsbEh5cG90aGVzaXNQcm90bzMiLwoORW51bU9ubHlQcm90bzMiHQoEQm9v",
             "bBIKCgZrRmFsc2UQABIJCgVrVHJ1ZRABKkAKC0ZvcmVpZ25FbnVtEg8KC0ZP",
@@ -5432,8 +5432,8 @@
         [pbr::OriginalName("ALIAS_FOO")] AliasFoo = 0,
         [pbr::OriginalName("ALIAS_BAR")] AliasBar = 1,
         [pbr::OriginalName("ALIAS_BAZ")] AliasBaz = 2,
-        [pbr::OriginalName("QUX", PreferredAlias = false)] Qux = 2,
-        [pbr::OriginalName("qux", PreferredAlias = false)] Qux_ = 2,
+        [pbr::OriginalName("MOO", PreferredAlias = false)] Moo = 2,
+        [pbr::OriginalName("moo", PreferredAlias = false)] Moo_ = 2,
         [pbr::OriginalName("bAz", PreferredAlias = false)] BAz = 2,
       }
 
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs
index ec4c07b..3440105 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs
@@ -54,11 +54,13 @@
             "dEluT25lb2YSLgoKbnVsbF92YWx1ZRgCIAEoDjIaLmdvb2dsZS5wcm90b2J1",
             "Zi5OdWxsVmFsdWUiYAoXTWl4ZWRSZWd1bGFyQW5kT3B0aW9uYWwSFQoNcmVn",
             "dWxhcl9maWVsZBgBIAEoCRIbCg5vcHRpb25hbF9maWVsZBgCIAEoCUgAiAEB",
-            "QhEKD19vcHRpb25hbF9maWVsZCpVCgxOZWdhdGl2ZUVudW0SFgoSTkVHQVRJ",
-            "VkVfRU5VTV9aRVJPEAASFgoJRml2ZUJlbG93EPv//////////wESFQoITWlu",
-            "dXNPbmUQ////////////ASouCg5EZXByZWNhdGVkRW51bRITCg9ERVBSRUNB",
-            "VEVEX1pFUk8QABIHCgNvbmUQAUIdqgIaVW5pdFRlc3QuSXNzdWVzLlRlc3RQ",
-            "cm90b3NiBnByb3RvMw=="));
+            "QhEKD19vcHRpb25hbF9maWVsZCI5ChJPbmVvZldpdGhOb25lRmllbGQSCwoB",
+            "eBgBIAEoCUgAEg4KBG5vbmUYAiABKAlIAEIGCgR0ZXN0IjUKEU9uZW9mV2l0",
+            "aE5vbmVOYW1lEgsKAXgYASABKAlIABILCgF5GAIgASgJSABCBgoEbm9uZSpV",
+            "CgxOZWdhdGl2ZUVudW0SFgoSTkVHQVRJVkVfRU5VTV9aRVJPEAASFgoJRml2",
+            "ZUJlbG93EPv//////////wESFQoITWludXNPbmUQ////////////ASouCg5E",
+            "ZXByZWNhdGVkRW51bRITCg9ERVBSRUNBVEVEX1pFUk8QABIHCgNvbmUQAUId",
+            "qgIaVW5pdFRlc3QuSXNzdWVzLlRlc3RQcm90b3NiBnByb3RvMw=="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, },
           new pbr::GeneratedClrTypeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.NegativeEnum), typeof(global::UnitTest.Issues.TestProtos.DeprecatedEnum), }, null, new pbr::GeneratedClrTypeInfo[] {
@@ -73,7 +75,9 @@
             new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofMerging), global::UnitTest.Issues.TestProtos.OneofMerging.Parser, new[]{ "Text", "Nested" }, new[]{ "Value" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested), global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested.Parser, new[]{ "X", "Y" }, null, null, null, null)}),
             new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NullValueOutsideStruct), global::UnitTest.Issues.TestProtos.NullValueOutsideStruct.Parser, new[]{ "StringValue", "NullValue" }, new[]{ "Value" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NullValueNotInOneof), global::UnitTest.Issues.TestProtos.NullValueNotInOneof.Parser, new[]{ "NullValue" }, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.MixedRegularAndOptional), global::UnitTest.Issues.TestProtos.MixedRegularAndOptional.Parser, new[]{ "RegularField", "OptionalField" }, new[]{ "OptionalField" }, null, null, null)
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.MixedRegularAndOptional), global::UnitTest.Issues.TestProtos.MixedRegularAndOptional.Parser, new[]{ "RegularField", "OptionalField" }, new[]{ "OptionalField" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofWithNoneField), global::UnitTest.Issues.TestProtos.OneofWithNoneField.Parser, new[]{ "X", "None" }, new[]{ "Test" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofWithNoneName), global::UnitTest.Issues.TestProtos.OneofWithNoneName.Parser, new[]{ "X", "Y" }, new[]{ "None" }, null, null, null)
           }));
     }
     #endregion
@@ -3825,6 +3829,524 @@
 
   }
 
+  public sealed partial class OneofWithNoneField : pb::IMessage<OneofWithNoneField>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<OneofWithNoneField> _parser = new pb::MessageParser<OneofWithNoneField>(() => new OneofWithNoneField());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<OneofWithNoneField> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::UnitTest.Issues.TestProtos.UnittestIssuesReflection.Descriptor.MessageTypes[12]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OneofWithNoneField() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OneofWithNoneField(OneofWithNoneField other) : this() {
+      switch (other.TestCase) {
+        case TestOneofCase.X:
+          X = other.X;
+          break;
+        case TestOneofCase.None_:
+          None = other.None;
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OneofWithNoneField Clone() {
+      return new OneofWithNoneField(this);
+    }
+
+    /// <summary>Field number for the "x" field.</summary>
+    public const int XFieldNumber = 1;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string X {
+      get { return testCase_ == TestOneofCase.X ? (string) test_ : ""; }
+      set {
+        test_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        testCase_ = TestOneofCase.X;
+      }
+    }
+
+    /// <summary>Field number for the "none" field.</summary>
+    public const int NoneFieldNumber = 2;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string None {
+      get { return testCase_ == TestOneofCase.None_ ? (string) test_ : ""; }
+      set {
+        test_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        testCase_ = TestOneofCase.None_;
+      }
+    }
+
+    private object test_;
+    /// <summary>Enum of possible cases for the "test" oneof.</summary>
+    public enum TestOneofCase {
+      None = 0,
+      X = 1,
+      None_ = 2,
+    }
+    private TestOneofCase testCase_ = TestOneofCase.None;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TestOneofCase TestCase {
+      get { return testCase_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearTest() {
+      testCase_ = TestOneofCase.None;
+      test_ = null;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as OneofWithNoneField);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(OneofWithNoneField other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (X != other.X) return false;
+      if (None != other.None) return false;
+      if (TestCase != other.TestCase) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (testCase_ == TestOneofCase.X) hash ^= X.GetHashCode();
+      if (testCase_ == TestOneofCase.None_) hash ^= None.GetHashCode();
+      hash ^= (int) testCase_;
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (testCase_ == TestOneofCase.X) {
+        output.WriteRawTag(10);
+        output.WriteString(X);
+      }
+      if (testCase_ == TestOneofCase.None_) {
+        output.WriteRawTag(18);
+        output.WriteString(None);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (testCase_ == TestOneofCase.X) {
+        output.WriteRawTag(10);
+        output.WriteString(X);
+      }
+      if (testCase_ == TestOneofCase.None_) {
+        output.WriteRawTag(18);
+        output.WriteString(None);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (testCase_ == TestOneofCase.X) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(X);
+      }
+      if (testCase_ == TestOneofCase.None_) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(None);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(OneofWithNoneField other) {
+      if (other == null) {
+        return;
+      }
+      switch (other.TestCase) {
+        case TestOneofCase.X:
+          X = other.X;
+          break;
+        case TestOneofCase.None_:
+          None = other.None;
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            X = input.ReadString();
+            break;
+          }
+          case 18: {
+            None = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            X = input.ReadString();
+            break;
+          }
+          case 18: {
+            None = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class OneofWithNoneName : pb::IMessage<OneofWithNoneName>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<OneofWithNoneName> _parser = new pb::MessageParser<OneofWithNoneName>(() => new OneofWithNoneName());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<OneofWithNoneName> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::UnitTest.Issues.TestProtos.UnittestIssuesReflection.Descriptor.MessageTypes[13]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OneofWithNoneName() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OneofWithNoneName(OneofWithNoneName other) : this() {
+      switch (other.NoneCase) {
+        case NoneOneofCase.X:
+          X = other.X;
+          break;
+        case NoneOneofCase.Y:
+          Y = other.Y;
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OneofWithNoneName Clone() {
+      return new OneofWithNoneName(this);
+    }
+
+    /// <summary>Field number for the "x" field.</summary>
+    public const int XFieldNumber = 1;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string X {
+      get { return noneCase_ == NoneOneofCase.X ? (string) none_ : ""; }
+      set {
+        none_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        noneCase_ = NoneOneofCase.X;
+      }
+    }
+
+    /// <summary>Field number for the "y" field.</summary>
+    public const int YFieldNumber = 2;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Y {
+      get { return noneCase_ == NoneOneofCase.Y ? (string) none_ : ""; }
+      set {
+        none_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        noneCase_ = NoneOneofCase.Y;
+      }
+    }
+
+    private object none_;
+    /// <summary>Enum of possible cases for the "none" oneof.</summary>
+    public enum NoneOneofCase {
+      None = 0,
+      X = 1,
+      Y = 2,
+    }
+    private NoneOneofCase noneCase_ = NoneOneofCase.None;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public NoneOneofCase NoneCase {
+      get { return noneCase_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearNone() {
+      noneCase_ = NoneOneofCase.None;
+      none_ = null;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as OneofWithNoneName);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(OneofWithNoneName other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (X != other.X) return false;
+      if (Y != other.Y) return false;
+      if (NoneCase != other.NoneCase) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (noneCase_ == NoneOneofCase.X) hash ^= X.GetHashCode();
+      if (noneCase_ == NoneOneofCase.Y) hash ^= Y.GetHashCode();
+      hash ^= (int) noneCase_;
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (noneCase_ == NoneOneofCase.X) {
+        output.WriteRawTag(10);
+        output.WriteString(X);
+      }
+      if (noneCase_ == NoneOneofCase.Y) {
+        output.WriteRawTag(18);
+        output.WriteString(Y);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (noneCase_ == NoneOneofCase.X) {
+        output.WriteRawTag(10);
+        output.WriteString(X);
+      }
+      if (noneCase_ == NoneOneofCase.Y) {
+        output.WriteRawTag(18);
+        output.WriteString(Y);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (noneCase_ == NoneOneofCase.X) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(X);
+      }
+      if (noneCase_ == NoneOneofCase.Y) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Y);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(OneofWithNoneName other) {
+      if (other == null) {
+        return;
+      }
+      switch (other.NoneCase) {
+        case NoneOneofCase.X:
+          X = other.X;
+          break;
+        case NoneOneofCase.Y:
+          Y = other.Y;
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            X = input.ReadString();
+            break;
+          }
+          case 18: {
+            Y = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            X = input.ReadString();
+            break;
+          }
+          case 18: {
+            Y = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
   #endregion
 
 }
diff --git a/csharp/src/Google.Protobuf.Test/ByteStringTest.cs b/csharp/src/Google.Protobuf.Test/ByteStringTest.cs
index 04d68b5..4876e09 100644
--- a/csharp/src/Google.Protobuf.Test/ByteStringTest.cs
+++ b/csharp/src/Google.Protobuf.Test/ByteStringTest.cs
@@ -1,439 +1,439 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.Text;

-using NUnit.Framework;

-using System.IO;

-using System.Collections.Generic;

-using System.Collections;

-using System.Linq;

-using System.Buffers;

-using System.Runtime.InteropServices;

-using System.Threading;

-using System.Runtime.CompilerServices;

-#if !NET35

-using System.Threading.Tasks;

-#endif

-

-namespace Google.Protobuf

-{

-    public class ByteStringTest

-    {

-        [Test]

-        public void Equality()

-        {

-            ByteString b1 = ByteString.CopyFrom(1, 2, 3);

-            ByteString b2 = ByteString.CopyFrom(1, 2, 3);

-            ByteString b3 = ByteString.CopyFrom(1, 2, 4);

-            ByteString b4 = ByteString.CopyFrom(1, 2, 3, 4);

-            EqualityTester.AssertEquality(b1, b1);

-            EqualityTester.AssertEquality(b1, b2);

-            EqualityTester.AssertInequality(b1, b3);

-            EqualityTester.AssertInequality(b1, b4);

-            EqualityTester.AssertInequality(b1, null);

-            EqualityTester.AssertEquality(ByteString.Empty, ByteString.Empty);

-#pragma warning disable 1718 // Deliberately calling ==(b1, b1) and !=(b1, b1)

-            Assert.IsTrue(b1 == b1);

-            Assert.IsTrue(b1 == b2);

-            Assert.IsFalse(b1 == b3);

-            Assert.IsFalse(b1 == b4);

-            Assert.IsFalse(b1 == null);

-            Assert.IsTrue((ByteString) null == null);

-            Assert.IsFalse(b1 != b1);

-            Assert.IsFalse(b1 != b2);

-            Assert.IsTrue(ByteString.Empty == ByteString.Empty);

-#pragma warning disable 1718

-            Assert.IsTrue(b1 != b3);

-            Assert.IsTrue(b1 != b4);

-            Assert.IsTrue(b1 != null);

-            Assert.IsFalse((ByteString) null != null);

-        }

-

-        [Test]

-        public void EmptyByteStringHasZeroSize()

-        {

-            Assert.AreEqual(0, ByteString.Empty.Length);

-        }

-

-        [Test]

-        public void CopyFromStringWithExplicitEncoding()

-        {

-            ByteString bs = ByteString.CopyFrom("AB", Encoding.Unicode);

-            Assert.AreEqual(4, bs.Length);

-            Assert.AreEqual(65, bs[0]);

-            Assert.AreEqual(0, bs[1]);

-            Assert.AreEqual(66, bs[2]);

-            Assert.AreEqual(0, bs[3]);

-        }

-

-        [Test]

-        public void IsEmptyWhenEmpty()

-        {

-            Assert.IsTrue(ByteString.CopyFromUtf8("").IsEmpty);

-        }

-

-        [Test]

-        public void IsEmptyWhenNotEmpty()

-        {

-            Assert.IsFalse(ByteString.CopyFromUtf8("X").IsEmpty);

-        }

-

-        [Test]

-        public void CopyFromByteArrayCopiesContents()

-        {

-            byte[] data = new byte[1];

-            data[0] = 10;

-            ByteString bs = ByteString.CopyFrom(data);

-            Assert.AreEqual(10, bs[0]);

-            data[0] = 5;

-            Assert.AreEqual(10, bs[0]);

-        }

-

-        [Test]

-        public void CopyFromReadOnlySpanCopiesContents()

-        {

-            byte[] data = new byte[1];

-            data[0] = 10;

-            ReadOnlySpan<byte> byteSpan = data;

-            var bs = ByteString.CopyFrom(byteSpan);

-            Assert.AreEqual(10, bs[0]);

-            data[0] = 5;

-            Assert.AreEqual(10, bs[0]);

-        }

-

-        [Test]

-        public void ToByteArrayCopiesContents()

-        {

-            ByteString bs = ByteString.CopyFromUtf8("Hello");

-            byte[] data = bs.ToByteArray();

-            Assert.AreEqual((byte)'H', data[0]);

-            Assert.AreEqual((byte)'H', bs[0]);

-            data[0] = 0;

-            Assert.AreEqual(0, data[0]);

-            Assert.AreEqual((byte)'H', bs[0]);

-        }

-

-        [Test]

-        public void CopyFromUtf8UsesUtf8()

-        {

-            ByteString bs = ByteString.CopyFromUtf8("\u20ac");

-            Assert.AreEqual(3, bs.Length);

-            Assert.AreEqual(0xe2, bs[0]);

-            Assert.AreEqual(0x82, bs[1]);

-            Assert.AreEqual(0xac, bs[2]);

-        }

-

-        [Test]

-        public void CopyFromPortion()

-        {

-            byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};

-            ByteString bs = ByteString.CopyFrom(data, 2, 3);

-            Assert.AreEqual(3, bs.Length);

-            Assert.AreEqual(2, bs[0]);

-            Assert.AreEqual(3, bs[1]);

-        }

-

-        [Test]

-        public void CopyTo()

-        {

-            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

-            ByteString bs = ByteString.CopyFrom(data);

-

-            byte[] dest = new byte[data.Length];

-            bs.CopyTo(dest, 0);

-

-            CollectionAssert.AreEqual(data, dest);

-        }

-

-        [Test]

-        public void GetEnumerator()

-        {

-            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

-            ByteString bs = ByteString.CopyFrom(data);

-

-            IEnumerator<byte> genericEnumerator = bs.GetEnumerator();

-            Assert.IsTrue(genericEnumerator.MoveNext());

-            Assert.AreEqual(0, genericEnumerator.Current);

-

-            IEnumerator enumerator = ((IEnumerable)bs).GetEnumerator();

-            Assert.IsTrue(enumerator.MoveNext());

-            Assert.AreEqual(0, enumerator.Current);

-

-            // Call via LINQ

-            CollectionAssert.AreEqual(bs.Span.ToArray(), bs.ToArray());

-        }

-

-        [Test]

-        public void UnsafeWrap()

-        {

-            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

-            ByteString bs = UnsafeByteOperations.UnsafeWrap(data.AsMemory(2, 3));

-            ReadOnlySpan<byte> s = bs.Span;

-

-            Assert.AreEqual(3, s.Length);

-            Assert.AreEqual(2, s[0]);

-            Assert.AreEqual(3, s[1]);

-            Assert.AreEqual(4, s[2]);

-

-            // Check that the value is not a copy

-            data[2] = byte.MaxValue;

-            Assert.AreEqual(byte.MaxValue, s[0]);

-        }

-

-        [Test]

-        public void CreateCodedInput_FromArraySegment()

-        {

-            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

-            ByteString bs = UnsafeByteOperations.UnsafeWrap(data.AsMemory(2, 3));

-            CodedInputStream codedInputStream = bs.CreateCodedInput();

-

-            byte[] bytes = codedInputStream.ReadRawBytes(3);

-

-            Assert.AreEqual(3, bytes.Length);

-            Assert.AreEqual(2, bytes[0]);

-            Assert.AreEqual(3, bytes[1]);

-            Assert.AreEqual(4, bytes[2]);

-            Assert.IsTrue(codedInputStream.IsAtEnd);

-        }

-

-        [Test]

-        public void WriteToStream()

-        {

-            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

-            ByteString bs = ByteString.CopyFrom(data);

-

-            MemoryStream ms = new MemoryStream();

-            bs.WriteTo(ms);

-

-            CollectionAssert.AreEqual(data, ms.ToArray());

-        }

-

-        [Test]

-        public void WriteToStream_Stackalloc()

-        {

-            byte[] data = Encoding.UTF8.GetBytes("Hello world");

-            Span<byte> s = stackalloc byte[data.Length];

-            data.CopyTo(s);

-

-            MemoryStream ms = new MemoryStream();

-

-            using (UnmanagedMemoryManager<byte> manager = new UnmanagedMemoryManager<byte>(s))

-            {

-                ByteString bs = ByteString.AttachBytes(manager.Memory);

-

-                bs.WriteTo(ms);

-            }

-

-            CollectionAssert.AreEqual(data, ms.ToArray());

-        }

-

-        [Test]

-        public void ToStringUtf8()

-        {

-            ByteString bs = ByteString.CopyFromUtf8("\u20ac");

-            Assert.AreEqual("\u20ac", bs.ToStringUtf8());

-        }

-

-        [Test]

-        public void ToStringWithExplicitEncoding()

-        {

-            ByteString bs = ByteString.CopyFrom("\u20ac", Encoding.Unicode);

-            Assert.AreEqual("\u20ac", bs.ToString(Encoding.Unicode));

-        }

-

-        [Test]

-        public void ToString_Stackalloc()

-        {

-            byte[] data = Encoding.UTF8.GetBytes("Hello world");

-            Span<byte> s = stackalloc byte[data.Length];

-            data.CopyTo(s);

-

-            using (UnmanagedMemoryManager<byte> manager = new UnmanagedMemoryManager<byte>(s))

-            {

-                ByteString bs = ByteString.AttachBytes(manager.Memory);

-

-                Assert.AreEqual("Hello world", bs.ToString(Encoding.UTF8));

-            }

-        }

-

-        [Test]

-        public void FromBase64_WithText()

-        {

-            byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};

-            string base64 = Convert.ToBase64String(data);

-            ByteString bs = ByteString.FromBase64(base64);

-            Assert.AreEqual(data, bs.ToByteArray());

-        }

-

-        [Test]

-        public void FromBase64_Empty()

-        {

-            // Optimization which also fixes issue 61.

-            Assert.AreSame(ByteString.Empty, ByteString.FromBase64(""));

-        }

-

-        [Test]

-        public void ToBase64_Array()

-        {

-            ByteString bs = ByteString.CopyFrom(Encoding.UTF8.GetBytes("Hello world"));

-

-            Assert.AreEqual("SGVsbG8gd29ybGQ=", bs.ToBase64());

-        }

-

-        [Test]

-        public void ToBase64_Stackalloc()

-        {

-            byte[] data = Encoding.UTF8.GetBytes("Hello world");

-            Span<byte> s = stackalloc byte[data.Length];

-            data.CopyTo(s);

-

-            using (UnmanagedMemoryManager<byte> manager = new UnmanagedMemoryManager<byte>(s))

-            {

-                ByteString bs = ByteString.AttachBytes(manager.Memory);

-

-                Assert.AreEqual("SGVsbG8gd29ybGQ=", bs.ToBase64());

-            }

-        }

-

-        [Test]

-        public void FromStream_Seekable()

-        {

-            var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });

-            // Consume the first byte, just to test that it's "from current position"

-            stream.ReadByte();

-            var actual = ByteString.FromStream(stream);

-            ByteString expected = ByteString.CopyFrom(2, 3, 4, 5);

-            Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");

-        }

-

-        [Test]

-        public void FromStream_NotSeekable()

-        {

-            var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });

-            // Consume the first byte, just to test that it's "from current position"

-            stream.ReadByte();

-            // Wrap the original stream in LimitedInputStream, which has CanSeek=false

-            var limitedStream = new LimitedInputStream(stream, 3);

-            var actual = ByteString.FromStream(limitedStream);

-            ByteString expected = ByteString.CopyFrom(2, 3, 4);

-            Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");

-        }

-

-#if !NET35

-        [Test]

-        public async Task FromStreamAsync_Seekable()

-        {

-            var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });

-            // Consume the first byte, just to test that it's "from current position"

-            stream.ReadByte();

-            var actual = await ByteString.FromStreamAsync(stream);

-            ByteString expected = ByteString.CopyFrom(2, 3, 4, 5);

-            Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");

-        }

-

-        [Test]

-        public async Task FromStreamAsync_NotSeekable()

-        {

-            var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });

-            // Consume the first byte, just to test that it's "from current position"

-            stream.ReadByte();

-            // Wrap the original stream in LimitedInputStream, which has CanSeek=false

-            var limitedStream = new LimitedInputStream(stream, 3);

-            var actual = await ByteString.FromStreamAsync(limitedStream);

-            ByteString expected = ByteString.CopyFrom(2, 3, 4);

-            Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");

-        }

-#endif

-

-        [Test]

-        public void GetHashCode_Regression()

-        {

-            // We used to have an awful hash algorithm where only the last four

-            // bytes were relevant. This is a regression test for

-            // https://github.com/protocolbuffers/protobuf/issues/2511

-

-            ByteString b1 = ByteString.CopyFrom(100, 1, 2, 3, 4);

-            ByteString b2 = ByteString.CopyFrom(200, 1, 2, 3, 4);

-            Assert.AreNotEqual(b1.GetHashCode(), b2.GetHashCode());

-        }

-

-        [Test]

-        public void GetContentsAsReadOnlySpan()

-        {

-            var byteString = ByteString.CopyFrom(1, 2, 3, 4, 5);

-            var copied = byteString.Span.ToArray();

-            CollectionAssert.AreEqual(byteString, copied);

-        }

-

-        [Test]

-        public void GetContentsAsReadOnlyMemory()

-        {

-            var byteString = ByteString.CopyFrom(1, 2, 3, 4, 5);

-            var copied = byteString.Memory.ToArray();

-            CollectionAssert.AreEqual(byteString, copied);

-        }

-

-        // Create Memory<byte> from non-array source.

-        // Use by ByteString tests that have optimized path for array backed Memory<byte>.

-        private sealed unsafe class UnmanagedMemoryManager<T> : MemoryManager<T> where T : unmanaged

-        {

-            private readonly T* _pointer;

-            private readonly int _length;

-

-            public UnmanagedMemoryManager(Span<T> span)

-            {

-                fixed (T* ptr = &MemoryMarshal.GetReference(span))

-                {

-                    _pointer = ptr;

-                    _length = span.Length;

-                }

-            }

-

-            public override Span<T> GetSpan() => new Span<T>(_pointer, _length);

-

-            public override MemoryHandle Pin(int elementIndex = 0)

-            {

-                if (elementIndex < 0 || elementIndex >= _length)

-                {

-                    throw new ArgumentOutOfRangeException(nameof(elementIndex));

-                }

-

-                return new MemoryHandle(_pointer + elementIndex);

-            }

-

-            public override void Unpin() { }

-

-            protected override void Dispose(bool disposing) { }

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Text;
+using NUnit.Framework;
+using System.IO;
+using System.Collections.Generic;
+using System.Collections;
+using System.Linq;
+using System.Buffers;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Runtime.CompilerServices;
+#if !NET35
+using System.Threading.Tasks;
+#endif
+
+namespace Google.Protobuf
+{
+    public class ByteStringTest
+    {
+        [Test]
+        public void Equality()
+        {
+            ByteString b1 = ByteString.CopyFrom(1, 2, 3);
+            ByteString b2 = ByteString.CopyFrom(1, 2, 3);
+            ByteString b3 = ByteString.CopyFrom(1, 2, 4);
+            ByteString b4 = ByteString.CopyFrom(1, 2, 3, 4);
+            EqualityTester.AssertEquality(b1, b1);
+            EqualityTester.AssertEquality(b1, b2);
+            EqualityTester.AssertInequality(b1, b3);
+            EqualityTester.AssertInequality(b1, b4);
+            EqualityTester.AssertInequality(b1, null);
+            EqualityTester.AssertEquality(ByteString.Empty, ByteString.Empty);
+#pragma warning disable 1718 // Deliberately calling ==(b1, b1) and !=(b1, b1)
+            Assert.IsTrue(b1 == b1);
+            Assert.IsTrue(b1 == b2);
+            Assert.IsFalse(b1 == b3);
+            Assert.IsFalse(b1 == b4);
+            Assert.IsFalse(b1 == null);
+            Assert.IsTrue((ByteString) null == null);
+            Assert.IsFalse(b1 != b1);
+            Assert.IsFalse(b1 != b2);
+            Assert.IsTrue(ByteString.Empty == ByteString.Empty);
+#pragma warning disable 1718
+            Assert.IsTrue(b1 != b3);
+            Assert.IsTrue(b1 != b4);
+            Assert.IsTrue(b1 != null);
+            Assert.IsFalse((ByteString) null != null);
+        }
+
+        [Test]
+        public void EmptyByteStringHasZeroSize()
+        {
+            Assert.AreEqual(0, ByteString.Empty.Length);
+        }
+
+        [Test]
+        public void CopyFromStringWithExplicitEncoding()
+        {
+            ByteString bs = ByteString.CopyFrom("AB", Encoding.Unicode);
+            Assert.AreEqual(4, bs.Length);
+            Assert.AreEqual(65, bs[0]);
+            Assert.AreEqual(0, bs[1]);
+            Assert.AreEqual(66, bs[2]);
+            Assert.AreEqual(0, bs[3]);
+        }
+
+        [Test]
+        public void IsEmptyWhenEmpty()
+        {
+            Assert.IsTrue(ByteString.CopyFromUtf8("").IsEmpty);
+        }
+
+        [Test]
+        public void IsEmptyWhenNotEmpty()
+        {
+            Assert.IsFalse(ByteString.CopyFromUtf8("X").IsEmpty);
+        }
+
+        [Test]
+        public void CopyFromByteArrayCopiesContents()
+        {
+            byte[] data = new byte[1];
+            data[0] = 10;
+            ByteString bs = ByteString.CopyFrom(data);
+            Assert.AreEqual(10, bs[0]);
+            data[0] = 5;
+            Assert.AreEqual(10, bs[0]);
+        }
+
+        [Test]
+        public void CopyFromReadOnlySpanCopiesContents()
+        {
+            byte[] data = new byte[1];
+            data[0] = 10;
+            ReadOnlySpan<byte> byteSpan = data;
+            var bs = ByteString.CopyFrom(byteSpan);
+            Assert.AreEqual(10, bs[0]);
+            data[0] = 5;
+            Assert.AreEqual(10, bs[0]);
+        }
+
+        [Test]
+        public void ToByteArrayCopiesContents()
+        {
+            ByteString bs = ByteString.CopyFromUtf8("Hello");
+            byte[] data = bs.ToByteArray();
+            Assert.AreEqual((byte)'H', data[0]);
+            Assert.AreEqual((byte)'H', bs[0]);
+            data[0] = 0;
+            Assert.AreEqual(0, data[0]);
+            Assert.AreEqual((byte)'H', bs[0]);
+        }
+
+        [Test]
+        public void CopyFromUtf8UsesUtf8()
+        {
+            ByteString bs = ByteString.CopyFromUtf8("\u20ac");
+            Assert.AreEqual(3, bs.Length);
+            Assert.AreEqual(0xe2, bs[0]);
+            Assert.AreEqual(0x82, bs[1]);
+            Assert.AreEqual(0xac, bs[2]);
+        }
+
+        [Test]
+        public void CopyFromPortion()
+        {
+            byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
+            ByteString bs = ByteString.CopyFrom(data, 2, 3);
+            Assert.AreEqual(3, bs.Length);
+            Assert.AreEqual(2, bs[0]);
+            Assert.AreEqual(3, bs[1]);
+        }
+
+        [Test]
+        public void CopyTo()
+        {
+            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
+            ByteString bs = ByteString.CopyFrom(data);
+
+            byte[] dest = new byte[data.Length];
+            bs.CopyTo(dest, 0);
+
+            CollectionAssert.AreEqual(data, dest);
+        }
+
+        [Test]
+        public void GetEnumerator()
+        {
+            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
+            ByteString bs = ByteString.CopyFrom(data);
+
+            IEnumerator<byte> genericEnumerator = bs.GetEnumerator();
+            Assert.IsTrue(genericEnumerator.MoveNext());
+            Assert.AreEqual(0, genericEnumerator.Current);
+
+            IEnumerator enumerator = ((IEnumerable)bs).GetEnumerator();
+            Assert.IsTrue(enumerator.MoveNext());
+            Assert.AreEqual(0, enumerator.Current);
+
+            // Call via LINQ
+            CollectionAssert.AreEqual(bs.Span.ToArray(), bs.ToArray());
+        }
+
+        [Test]
+        public void UnsafeWrap()
+        {
+            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
+            ByteString bs = UnsafeByteOperations.UnsafeWrap(data.AsMemory(2, 3));
+            ReadOnlySpan<byte> s = bs.Span;
+
+            Assert.AreEqual(3, s.Length);
+            Assert.AreEqual(2, s[0]);
+            Assert.AreEqual(3, s[1]);
+            Assert.AreEqual(4, s[2]);
+
+            // Check that the value is not a copy
+            data[2] = byte.MaxValue;
+            Assert.AreEqual(byte.MaxValue, s[0]);
+        }
+
+        [Test]
+        public void CreateCodedInput_FromArraySegment()
+        {
+            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
+            ByteString bs = UnsafeByteOperations.UnsafeWrap(data.AsMemory(2, 3));
+            CodedInputStream codedInputStream = bs.CreateCodedInput();
+
+            byte[] bytes = codedInputStream.ReadRawBytes(3);
+
+            Assert.AreEqual(3, bytes.Length);
+            Assert.AreEqual(2, bytes[0]);
+            Assert.AreEqual(3, bytes[1]);
+            Assert.AreEqual(4, bytes[2]);
+            Assert.IsTrue(codedInputStream.IsAtEnd);
+        }
+
+        [Test]
+        public void WriteToStream()
+        {
+            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
+            ByteString bs = ByteString.CopyFrom(data);
+
+            MemoryStream ms = new MemoryStream();
+            bs.WriteTo(ms);
+
+            CollectionAssert.AreEqual(data, ms.ToArray());
+        }
+
+        [Test]
+        public void WriteToStream_Stackalloc()
+        {
+            byte[] data = Encoding.UTF8.GetBytes("Hello world");
+            Span<byte> s = stackalloc byte[data.Length];
+            data.CopyTo(s);
+
+            MemoryStream ms = new MemoryStream();
+
+            using (UnmanagedMemoryManager<byte> manager = new UnmanagedMemoryManager<byte>(s))
+            {
+                ByteString bs = ByteString.AttachBytes(manager.Memory);
+
+                bs.WriteTo(ms);
+            }
+
+            CollectionAssert.AreEqual(data, ms.ToArray());
+        }
+
+        [Test]
+        public void ToStringUtf8()
+        {
+            ByteString bs = ByteString.CopyFromUtf8("\u20ac");
+            Assert.AreEqual("\u20ac", bs.ToStringUtf8());
+        }
+
+        [Test]
+        public void ToStringWithExplicitEncoding()
+        {
+            ByteString bs = ByteString.CopyFrom("\u20ac", Encoding.Unicode);
+            Assert.AreEqual("\u20ac", bs.ToString(Encoding.Unicode));
+        }
+
+        [Test]
+        public void ToString_Stackalloc()
+        {
+            byte[] data = Encoding.UTF8.GetBytes("Hello world");
+            Span<byte> s = stackalloc byte[data.Length];
+            data.CopyTo(s);
+
+            using (UnmanagedMemoryManager<byte> manager = new UnmanagedMemoryManager<byte>(s))
+            {
+                ByteString bs = ByteString.AttachBytes(manager.Memory);
+
+                Assert.AreEqual("Hello world", bs.ToString(Encoding.UTF8));
+            }
+        }
+
+        [Test]
+        public void FromBase64_WithText()
+        {
+            byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
+            string base64 = Convert.ToBase64String(data);
+            ByteString bs = ByteString.FromBase64(base64);
+            Assert.AreEqual(data, bs.ToByteArray());
+        }
+
+        [Test]
+        public void FromBase64_Empty()
+        {
+            // Optimization which also fixes issue 61.
+            Assert.AreSame(ByteString.Empty, ByteString.FromBase64(""));
+        }
+
+        [Test]
+        public void ToBase64_Array()
+        {
+            ByteString bs = ByteString.CopyFrom(Encoding.UTF8.GetBytes("Hello world"));
+
+            Assert.AreEqual("SGVsbG8gd29ybGQ=", bs.ToBase64());
+        }
+
+        [Test]
+        public void ToBase64_Stackalloc()
+        {
+            byte[] data = Encoding.UTF8.GetBytes("Hello world");
+            Span<byte> s = stackalloc byte[data.Length];
+            data.CopyTo(s);
+
+            using (UnmanagedMemoryManager<byte> manager = new UnmanagedMemoryManager<byte>(s))
+            {
+                ByteString bs = ByteString.AttachBytes(manager.Memory);
+
+                Assert.AreEqual("SGVsbG8gd29ybGQ=", bs.ToBase64());
+            }
+        }
+
+        [Test]
+        public void FromStream_Seekable()
+        {
+            var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
+            // Consume the first byte, just to test that it's "from current position"
+            stream.ReadByte();
+            var actual = ByteString.FromStream(stream);
+            ByteString expected = ByteString.CopyFrom(2, 3, 4, 5);
+            Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
+        }
+
+        [Test]
+        public void FromStream_NotSeekable()
+        {
+            var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
+            // Consume the first byte, just to test that it's "from current position"
+            stream.ReadByte();
+            // Wrap the original stream in LimitedInputStream, which has CanSeek=false
+            var limitedStream = new LimitedInputStream(stream, 3);
+            var actual = ByteString.FromStream(limitedStream);
+            ByteString expected = ByteString.CopyFrom(2, 3, 4);
+            Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
+        }
+
+#if !NET35
+        [Test]
+        public async Task FromStreamAsync_Seekable()
+        {
+            var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
+            // Consume the first byte, just to test that it's "from current position"
+            stream.ReadByte();
+            var actual = await ByteString.FromStreamAsync(stream);
+            ByteString expected = ByteString.CopyFrom(2, 3, 4, 5);
+            Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
+        }
+
+        [Test]
+        public async Task FromStreamAsync_NotSeekable()
+        {
+            var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
+            // Consume the first byte, just to test that it's "from current position"
+            stream.ReadByte();
+            // Wrap the original stream in LimitedInputStream, which has CanSeek=false
+            var limitedStream = new LimitedInputStream(stream, 3);
+            var actual = await ByteString.FromStreamAsync(limitedStream);
+            ByteString expected = ByteString.CopyFrom(2, 3, 4);
+            Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
+        }
+#endif
+
+        [Test]
+        public void GetHashCode_Regression()
+        {
+            // We used to have an awful hash algorithm where only the last four
+            // bytes were relevant. This is a regression test for
+            // https://github.com/protocolbuffers/protobuf/issues/2511
+
+            ByteString b1 = ByteString.CopyFrom(100, 1, 2, 3, 4);
+            ByteString b2 = ByteString.CopyFrom(200, 1, 2, 3, 4);
+            Assert.AreNotEqual(b1.GetHashCode(), b2.GetHashCode());
+        }
+
+        [Test]
+        public void GetContentsAsReadOnlySpan()
+        {
+            var byteString = ByteString.CopyFrom(1, 2, 3, 4, 5);
+            var copied = byteString.Span.ToArray();
+            CollectionAssert.AreEqual(byteString, copied);
+        }
+
+        [Test]
+        public void GetContentsAsReadOnlyMemory()
+        {
+            var byteString = ByteString.CopyFrom(1, 2, 3, 4, 5);
+            var copied = byteString.Memory.ToArray();
+            CollectionAssert.AreEqual(byteString, copied);
+        }
+
+        // Create Memory<byte> from non-array source.
+        // Use by ByteString tests that have optimized path for array backed Memory<byte>.
+        private sealed unsafe class UnmanagedMemoryManager<T> : MemoryManager<T> where T : unmanaged
+        {
+            private readonly T* _pointer;
+            private readonly int _length;
+
+            public UnmanagedMemoryManager(Span<T> span)
+            {
+                fixed (T* ptr = &MemoryMarshal.GetReference(span))
+                {
+                    _pointer = ptr;
+                    _length = span.Length;
+                }
+            }
+
+            public override Span<T> GetSpan() => new Span<T>(_pointer, _length);
+
+            public override MemoryHandle Pin(int elementIndex = 0)
+            {
+                if (elementIndex < 0 || elementIndex >= _length)
+                {
+                    throw new ArgumentOutOfRangeException(nameof(elementIndex));
+                }
+
+                return new MemoryHandle(_pointer + elementIndex);
+            }
+
+            public override void Unpin() { }
+
+            protected override void Dispose(bool disposing) { }
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
index 2341559..b84a1b7 100644
--- a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
+++ b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
@@ -1,1008 +1,1008 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.Buffers;

-using System.IO;

-using Google.Protobuf.TestProtos;

-using Proto2 = Google.Protobuf.TestProtos.Proto2;

-using NUnit.Framework;

-

-namespace Google.Protobuf

-{

-    public class CodedInputStreamTest

-    {

-        /// <summary>

-        /// Helper to construct a byte array from a bunch of bytes.  The inputs are

-        /// actually ints so that I can use hex notation and not get stupid errors

-        /// about precision.

-        /// </summary>

-        private static byte[] Bytes(params int[] bytesAsInts)

-        {

-            byte[] bytes = new byte[bytesAsInts.Length];

-            for (int i = 0; i < bytesAsInts.Length; i++)

-            {

-                bytes[i] = (byte) bytesAsInts[i];

-            }

-            return bytes;

-        }

-

-        /// <summary>

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

-        /// </summary>

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

-        {

-            CodedInputStream input = new CodedInputStream(data);

-            Assert.AreEqual((uint) value, input.ReadRawVarint32());

-            Assert.IsTrue(input.IsAtEnd);

-

-            input = new CodedInputStream(data);

-            Assert.AreEqual(value, input.ReadRawVarint64());

-            Assert.IsTrue(input.IsAtEnd);

-

-            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>

-            {

-                Assert.AreEqual((uint) value, ctx.ReadUInt32());

-            }, true);

-

-            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>

-            {

-                Assert.AreEqual(value, ctx.ReadUInt64());

-            }, true);

-

-            // Try different block sizes.

-            for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)

-            {

-                input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));

-                Assert.AreEqual((uint) value, input.ReadRawVarint32());

-

-                input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));

-                Assert.AreEqual(value, input.ReadRawVarint64());

-                Assert.IsTrue(input.IsAtEnd);

-

-                AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, bufferSize), (ref ParseContext ctx) =>

-                {

-                    Assert.AreEqual((uint) value, ctx.ReadUInt32());

-                }, true);

-

-                AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, bufferSize), (ref ParseContext ctx) =>

-                {

-                    Assert.AreEqual(value, ctx.ReadUInt64());

-                }, true);

-            }

-

-            // Try reading directly from a MemoryStream. We want to verify that it

-            // doesn't read past the end of the input, so write an extra byte - this

-            // lets us test the position at the end.

-            MemoryStream memoryStream = new MemoryStream();

-            memoryStream.Write(data, 0, data.Length);

-            memoryStream.WriteByte(0);

-            memoryStream.Position = 0;

-            Assert.AreEqual((uint) value, CodedInputStream.ReadRawVarint32(memoryStream));

-            Assert.AreEqual(data.Length, memoryStream.Position);

-        }

-

-        /// <summary>

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

-        /// expects them to fail with an InvalidProtocolBufferException whose

-        /// description matches the given one.

-        /// </summary>

-        private static void AssertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)

-        {

-            CodedInputStream input = new CodedInputStream(data);

-            var exception = Assert.Throws<InvalidProtocolBufferException>(() => input.ReadRawVarint32());

-            Assert.AreEqual(expected.Message, exception.Message);

-

-            input = new CodedInputStream(data);

-            exception = Assert.Throws<InvalidProtocolBufferException>(() => input.ReadRawVarint64());

-            Assert.AreEqual(expected.Message, exception.Message);

-

-            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>

-            {

-                try

-                {

-                    ctx.ReadUInt32();

-                    Assert.Fail();

-                }

-                catch (InvalidProtocolBufferException ex)

-                {

-                    Assert.AreEqual(expected.Message, ex.Message);

-                }

-            }, false);

-

-            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>

-            {

-                try

-                {

-                    ctx.ReadUInt64();

-                    Assert.Fail();

-                }

-                catch (InvalidProtocolBufferException ex)

-                {

-                    Assert.AreEqual(expected.Message, ex.Message);

-                }

-            }, false);

-

-            // Make sure we get the same error when reading directly from a Stream.

-            exception = Assert.Throws<InvalidProtocolBufferException>(() => CodedInputStream.ReadRawVarint32(new MemoryStream(data)));

-            Assert.AreEqual(expected.Message, exception.Message);

-        }

-

-        private delegate void ParseContextAssertAction(ref ParseContext ctx);

-

-        private static void AssertReadFromParseContext(ReadOnlySequence<byte> input, ParseContextAssertAction assertAction, bool assertIsAtEnd)

-        {

-            // Check as ReadOnlySequence<byte>

-            ParseContext.Initialize(input, out ParseContext parseCtx);

-            assertAction(ref parseCtx);

-            if (assertIsAtEnd)

-            {

-                Assert.IsTrue(SegmentedBufferHelper.IsAtEnd(ref parseCtx.buffer, ref parseCtx.state));

-            }

-

-            // Check as ReadOnlySpan<byte>

-            ParseContext.Initialize(input.ToArray().AsSpan(), out ParseContext spanParseContext);

-            assertAction(ref spanParseContext);

-            if (assertIsAtEnd)

-            {

-                Assert.IsTrue(SegmentedBufferHelper.IsAtEnd(ref spanParseContext.buffer, ref spanParseContext.state));

-            }

-        }

-

-        [Test]

-        public void ReadVarint()

-        {

-            AssertReadVarint(Bytes(0x00), 0);

-            AssertReadVarint(Bytes(0x01), 1);

-            AssertReadVarint(Bytes(0x7f), 127);

-            // 14882

-            AssertReadVarint(Bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));

-            // 2961488830

-            AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),

-                             (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |

-                             (0x0bL << 28));

-

-            // 64-bit

-            // 7256456126

-            AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),

-                             (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |

-                             (0x1bL << 28));

-            // 41256202580718336

-            AssertReadVarint(Bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),

-                             (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |

-                             (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));

-            // 11964378330978735131

-            AssertReadVarint(Bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),

-                             (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |

-                             (0x3bUL << 28) | (0x56UL << 35) | (0x00UL << 42) |

-                             (0x05UL << 49) | (0x26UL << 56) | (0x01UL << 63));

-

-            // Failures

-            AssertReadVarintFailure(

-                InvalidProtocolBufferException.MalformedVarint(),

-                Bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,

-                      0x00));

-            AssertReadVarintFailure(

-                InvalidProtocolBufferException.TruncatedMessage(),

-                Bytes(0x80));

-        }

-

-        /// <summary>

-        /// Parses the given bytes using ReadRawLittleEndian32() and checks

-        /// that the result matches the given value.

-        /// </summary>

-        private static void AssertReadLittleEndian32(byte[] data, uint value)

-        {

-            CodedInputStream input = new CodedInputStream(data);

-            Assert.AreEqual(value, input.ReadRawLittleEndian32());

-            Assert.IsTrue(input.IsAtEnd);

-

-            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>

-            {

-                Assert.AreEqual(value, ctx.ReadFixed32());

-            }, true);

-

-            // Try different block sizes.

-            for (int blockSize = 1; blockSize <= 16; blockSize *= 2)

-            {

-                input = new CodedInputStream(

-                    new SmallBlockInputStream(data, blockSize));

-                Assert.AreEqual(value, input.ReadRawLittleEndian32());

-                Assert.IsTrue(input.IsAtEnd);

-

-                AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, blockSize), (ref ParseContext ctx) =>

-                {

-                    Assert.AreEqual(value, ctx.ReadFixed32());

-                }, true);

-            }

-        }

-

-        /// <summary>

-        /// Parses the given bytes using ReadRawLittleEndian64() and checks

-        /// that the result matches the given value.

-        /// </summary>

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

-        {

-            CodedInputStream input = new CodedInputStream(data);

-            Assert.AreEqual(value, input.ReadRawLittleEndian64());

-            Assert.IsTrue(input.IsAtEnd);

-

-            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>

-            {

-                Assert.AreEqual(value, ctx.ReadFixed64());

-            }, true);

-

-            // Try different block sizes.

-            for (int blockSize = 1; blockSize <= 16; blockSize *= 2)

-            {

-                input = new CodedInputStream(

-                    new SmallBlockInputStream(data, blockSize));

-                Assert.AreEqual(value, input.ReadRawLittleEndian64());

-                Assert.IsTrue(input.IsAtEnd);

-

-                AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, blockSize), (ref ParseContext ctx) =>

-                {

-                    Assert.AreEqual(value, ctx.ReadFixed64());

-                }, true);

-            }

-        }

-

-        [Test]

-        public void ReadLittleEndian()

-        {

-            AssertReadLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);

-            AssertReadLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);

-

-            AssertReadLittleEndian64(Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),

-                                     0x123456789abcdef0L);

-            AssertReadLittleEndian64(

-                Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678UL);

-        }

-

-        [Test]

-        public void DecodeZigZag32()

-        {

-            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(0));

-            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(1));

-            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(2));

-            Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag32(3));

-            Assert.AreEqual(0x3FFFFFFF, ParsingPrimitives.DecodeZigZag32(0x7FFFFFFE));

-            Assert.AreEqual(unchecked((int) 0xC0000000), ParsingPrimitives.DecodeZigZag32(0x7FFFFFFF));

-            Assert.AreEqual(0x7FFFFFFF, ParsingPrimitives.DecodeZigZag32(0xFFFFFFFE));

-            Assert.AreEqual(unchecked((int) 0x80000000), ParsingPrimitives.DecodeZigZag32(0xFFFFFFFF));

-        }

-

-        [Test]

-        public void DecodeZigZag64()

-        {

-            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(0));

-            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(1));

-            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(2));

-            Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag64(3));

-            Assert.AreEqual(0x000000003FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFEL));

-            Assert.AreEqual(unchecked((long) 0xFFFFFFFFC0000000L), ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFFL));

-            Assert.AreEqual(0x000000007FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFEL));

-            Assert.AreEqual(unchecked((long) 0xFFFFFFFF80000000L), ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFFL));

-            Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));

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

-        }

-        

-        [Test]

-        public void ReadWholeMessage_VaryingBlockSizes()

-        {

-            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();

-

-            byte[] rawBytes = message.ToByteArray();

-            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.Parser.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));

-                Assert.AreEqual(message, message2);

-            }

-        }

-

-        [Test]

-        public void ReadWholeMessage_VaryingBlockSizes_FromSequence()

-        {

-            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();

-

-            byte[] rawBytes = message.ToByteArray();

-            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.Parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(rawBytes, blockSize));

-                Assert.AreEqual(message, message2);

-            }

-        }

-

-        [Test]

-        public void ReadInt32Wrapper_VariableBlockSizes()

-        {

-            byte[] rawBytes = new byte[] { 202, 1, 11, 8, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1 };

-

-            for (int blockSize = 1; blockSize <= rawBytes.Length; blockSize++)

-            {

-                ReadOnlySequence<byte> data = ReadOnlySequenceFactory.CreateWithContent(rawBytes, blockSize);

-                AssertReadFromParseContext(data, (ref ParseContext ctx) =>

-                {

-                    ctx.ReadTag();

-

-                    var value = ParsingPrimitivesWrappers.ReadInt32Wrapper(ref ctx);

-

-                    Assert.AreEqual(-2, value);

-                }, true);

-            }

-        }

-

-        [Test]

-        public void ReadHugeBlob()

-        {

-            // Allocate and initialize a 1MB blob.

-            byte[] blob = new byte[1 << 20];

-            for (int i = 0; i < blob.Length; i++)

-            {

-                blob[i] = (byte) i;

-            }

-

-            // Make a message containing it.

-            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.Parser.ParseFrom(message.ToByteString());

-

-            Assert.AreEqual(message, message2);

-        }

-

-        [Test]

-        public void ReadMaliciouslyLargeBlob()

-        {

-            MemoryStream ms = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(ms);

-

-            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteRawVarint32(tag);

-            output.WriteRawVarint32(0x7FFFFFFF);

-            output.WriteRawBytes(new byte[32]); // Pad with a few random bytes.

-            output.Flush();

-            ms.Position = 0;

-

-            CodedInputStream input = new CodedInputStream(ms);

-            Assert.AreEqual(tag, input.ReadTag());

-

-            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadBytes());

-        }

-

-        [Test]

-        public void ReadBlobGreaterThanCurrentLimit()

-        {

-            MemoryStream ms = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(ms);

-            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteRawVarint32(tag);

-            output.WriteRawVarint32(4);

-            output.WriteRawBytes(new byte[4]); // Pad with a few random bytes.

-            output.Flush();

-            ms.Position = 0;

-

-            CodedInputStream input = new CodedInputStream(ms);

-            Assert.AreEqual(tag, input.ReadTag());

-

-            // Specify limit smaller than data length

-            input.PushLimit(3);

-            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadBytes());

-

-            AssertReadFromParseContext(new ReadOnlySequence<byte>(ms.ToArray()), (ref ParseContext ctx) =>

-            {

-                Assert.AreEqual(tag, ctx.ReadTag());

-                SegmentedBufferHelper.PushLimit(ref ctx.state, 3);

-                try

-                {

-                    ctx.ReadBytes();

-                    Assert.Fail();

-                }

-                catch (InvalidProtocolBufferException) {}

-            }, true);

-        }

-

-        [Test]

-        public void ReadStringGreaterThanCurrentLimit()

-        {

-            MemoryStream ms = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(ms);

-            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteRawVarint32(tag);

-            output.WriteRawVarint32(4);

-            output.WriteRawBytes(new byte[4]); // Pad with a few random bytes.

-            output.Flush();

-            ms.Position = 0;

-

-            CodedInputStream input = new CodedInputStream(ms.ToArray());

-            Assert.AreEqual(tag, input.ReadTag());

-

-            // Specify limit smaller than data length

-            input.PushLimit(3);

-            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadString());

-

-            AssertReadFromParseContext(new ReadOnlySequence<byte>(ms.ToArray()), (ref ParseContext ctx) =>

-            {

-                Assert.AreEqual(tag, ctx.ReadTag());

-                SegmentedBufferHelper.PushLimit(ref ctx.state, 3);

-                try

-                {

-                    ctx.ReadString();

-                    Assert.Fail();

-                }

-                catch (InvalidProtocolBufferException) { }

-            }, true);

-        }

-

-        // Representations of a tag for field 0 with various wire types

-        [Test]

-        [TestCase(0)]

-        [TestCase(1)]

-        [TestCase(2)]

-        [TestCase(3)]

-        [TestCase(4)]

-        [TestCase(5)]

-        public void ReadTag_ZeroFieldRejected(byte tag)

-        {

-            CodedInputStream cis = new CodedInputStream(new byte[] { tag });

-            Assert.Throws<InvalidProtocolBufferException>(() => cis.ReadTag());

-        }

-

-        internal static TestRecursiveMessage MakeRecursiveMessage(int depth)

-        {

-            if (depth == 0)

-            {

-                return new TestRecursiveMessage { I = 5 };

-            }

-            else

-            {

-                return new TestRecursiveMessage { A = MakeRecursiveMessage(depth - 1) };

-            }

-        }

-

-        internal static void AssertMessageDepth(TestRecursiveMessage message, int depth)

-        {

-            if (depth == 0)

-            {

-                Assert.IsNull(message.A);

-                Assert.AreEqual(5, message.I);

-            }

-            else

-            {

-                Assert.IsNotNull(message.A);

-                AssertMessageDepth(message.A, depth - 1);

-            }

-        }

-

-        [Test]

-        public void MaliciousRecursion()

-        {

-            ByteString atRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit).ToByteString();

-            ByteString beyondRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit + 1).ToByteString();

-

-            AssertMessageDepth(TestRecursiveMessage.Parser.ParseFrom(atRecursiveLimit), CodedInputStream.DefaultRecursionLimit);

-

-            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(beyondRecursiveLimit));

-

-            CodedInputStream input = CodedInputStream.CreateWithLimits(new MemoryStream(atRecursiveLimit.ToByteArray()), 1000000, CodedInputStream.DefaultRecursionLimit - 1);

-            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(input));

-        }

-        

-        private static byte[] MakeMaliciousRecursionUnknownFieldsPayload(int recursionDepth)

-        {

-            // generate recursively nested groups that will be parsed as unknown fields

-            int unknownFieldNumber = 14;  // an unused field number

-            MemoryStream ms = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(ms);

-            for (int i = 0; i < recursionDepth; i++)

-            {

-                output.WriteTag(WireFormat.MakeTag(unknownFieldNumber, WireFormat.WireType.StartGroup));

-            }

-            for (int i = 0; i < recursionDepth; i++)

-            {

-                output.WriteTag(WireFormat.MakeTag(unknownFieldNumber, WireFormat.WireType.EndGroup));

-            }

-            output.Flush();

-            return ms.ToArray();

-        }

-

-        [Test]

-        public void MaliciousRecursion_UnknownFields()

-        {

-            byte[] payloadAtRecursiveLimit = MakeMaliciousRecursionUnknownFieldsPayload(CodedInputStream.DefaultRecursionLimit);

-            byte[] payloadBeyondRecursiveLimit = MakeMaliciousRecursionUnknownFieldsPayload(CodedInputStream.DefaultRecursionLimit + 1);

-            

-            Assert.DoesNotThrow(() => TestRecursiveMessage.Parser.ParseFrom(payloadAtRecursiveLimit));

-            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(payloadBeyondRecursiveLimit));

-        }

-

-        [Test]

-        public void ReadGroup_WrongEndGroupTag()

-        {

-            int groupFieldNumber = Proto2.TestAllTypes.OptionalGroupFieldNumber;

-

-            // write Proto2.TestAllTypes with "optional_group" set, but use wrong EndGroup closing tag

-            MemoryStream ms = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(ms);

-            output.WriteTag(WireFormat.MakeTag(groupFieldNumber, WireFormat.WireType.StartGroup));

-            output.WriteGroup(new Proto2.TestAllTypes.Types.OptionalGroup { A = 12345 });

-            // end group with different field number

-            output.WriteTag(WireFormat.MakeTag(groupFieldNumber + 1, WireFormat.WireType.EndGroup));

-            output.Flush();

-            var payload = ms.ToArray();

-

-            Assert.Throws<InvalidProtocolBufferException>(() => Proto2.TestAllTypes.Parser.ParseFrom(payload));

-        }

-

-        [Test]

-        public void ReadGroup_UnknownFields_WrongEndGroupTag()

-        {

-            MemoryStream ms = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(ms);

-            output.WriteTag(WireFormat.MakeTag(14, WireFormat.WireType.StartGroup));

-            // end group with different field number

-            output.WriteTag(WireFormat.MakeTag(15, WireFormat.WireType.EndGroup));

-            output.Flush();

-            var payload = ms.ToArray();

-

-            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(payload));

-        }

-

-        [Test]

-        public void SizeLimit()

-        {

-            // Have to use a Stream rather than ByteString.CreateCodedInput as SizeLimit doesn't

-            // apply to the latter case.

-            MemoryStream ms = new MemoryStream(SampleMessages.CreateFullTestAllTypes().ToByteArray());

-            CodedInputStream input = CodedInputStream.CreateWithLimits(ms, 16, 100);

-            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(input));

-        }

-

-        /// <summary>

-        /// Tests that if we read an string that contains invalid UTF-8, no exception

-        /// is thrown.  Instead, the invalid bytes are replaced with the Unicode

-        /// "replacement character" U+FFFD.

-        /// </summary>

-        [Test]

-        public void ReadInvalidUtf8()

-        {

-            MemoryStream ms = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(ms);

-

-            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteRawVarint32(tag);

-            output.WriteRawVarint32(1);

-            output.WriteRawBytes(new byte[] {0x80});

-            output.Flush();

-            ms.Position = 0;

-

-            CodedInputStream input = new CodedInputStream(ms);

-

-            Assert.AreEqual(tag, input.ReadTag());

-            string text = input.ReadString();

-            Assert.AreEqual('\ufffd', text[0]);

-        }

-

-        [Test]

-        public void ReadNegativeSizedStringThrowsInvalidProtocolBufferException()

-        {

-            MemoryStream ms = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(ms);

-

-            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteRawVarint32(tag);

-            output.WriteLength(-1);

-            output.Flush();

-            ms.Position = 0;

-

-            CodedInputStream input = new CodedInputStream(ms);

-

-            Assert.AreEqual(tag, input.ReadTag());

-            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadString());

-        }

-

-        [Test]

-        public void ReadNegativeSizedBytesThrowsInvalidProtocolBufferException()

-        {

-            MemoryStream ms = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(ms);

-

-            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteRawVarint32(tag);

-            output.WriteLength(-1);

-            output.Flush();

-            ms.Position = 0;

-

-            CodedInputStream input = new CodedInputStream(ms);

-

-            Assert.AreEqual(tag, input.ReadTag());

-            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadBytes());

-        }

-

-        /// <summary>

-        /// A stream which limits the number of bytes it reads at a time.

-        /// We use this to make sure that CodedInputStream doesn't screw up when

-        /// reading in small blocks.

-        /// </summary>

-        private sealed class SmallBlockInputStream : MemoryStream

-        {

-            private readonly int blockSize;

-

-            public SmallBlockInputStream(byte[] data, int blockSize)

-                : base(data)

-            {

-                this.blockSize = blockSize;

-            }

-

-            public override int Read(byte[] buffer, int offset, int count)

-            {

-                return base.Read(buffer, offset, Math.Min(count, blockSize));

-            }

-        }

-

-        [Test]

-        public void TestNegativeEnum()

-        {

-            byte[] bytes = { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };

-            CodedInputStream input = new CodedInputStream(bytes);

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

-            Assert.IsTrue(input.IsAtEnd);

-        }

-

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

-        [Test]

-        public void TestSlowPathAvoidance()

-        {

-            using (var ms = new MemoryStream())

-            {

-                CodedOutputStream output = new CodedOutputStream(ms);

-                output.WriteTag(1, WireFormat.WireType.LengthDelimited);

-                output.WriteBytes(ByteString.CopyFrom(new byte[100]));

-                output.WriteTag(2, WireFormat.WireType.LengthDelimited);

-                output.WriteBytes(ByteString.CopyFrom(new byte[100]));

-                output.Flush();

-

-                ms.Position = 0;

-                CodedInputStream input = new CodedInputStream(ms, new byte[ms.Length / 2], 0, 0, false);

-

-                uint tag = input.ReadTag();

-                Assert.AreEqual(1, WireFormat.GetTagFieldNumber(tag));

-                Assert.AreEqual(100, input.ReadBytes().Length);

-

-                tag = input.ReadTag();

-                Assert.AreEqual(2, WireFormat.GetTagFieldNumber(tag));

-                Assert.AreEqual(100, input.ReadBytes().Length);

-            }

-        }

-

-        [Test]

-        public void MaximumFieldNumber()

-        {

-            MemoryStream ms = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(ms);

-

-            int fieldNumber = 0x1FFFFFFF;

-            uint tag = WireFormat.MakeTag(fieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteRawVarint32(tag);

-            output.WriteString("field 1");

-            output.Flush();

-            ms.Position = 0;

-

-            CodedInputStream input = new CodedInputStream(ms);

-

-            Assert.AreEqual(tag, input.ReadTag());

-            Assert.AreEqual(fieldNumber, WireFormat.GetTagFieldNumber(tag));

-        }

-

-        [Test]

-        public void Tag0Throws()

-        {

-            var input = new CodedInputStream(new byte[] { 0 });

-            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadTag());

-        }

-

-        [Test]

-        public void SkipGroup()

-        {

-            // Create an output stream with a group in:

-            // Field 1: string "field 1"

-            // Field 2: group containing:

-            //   Field 1: fixed int32 value 100

-            //   Field 2: string "ignore me"

-            //   Field 3: nested group containing

-            //      Field 1: fixed int64 value 1000

-            // Field 3: string "field 3"

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-            output.WriteTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteString("field 1");

-            

-            // The outer group...

-            output.WriteTag(2, WireFormat.WireType.StartGroup);

-            output.WriteTag(1, WireFormat.WireType.Fixed32);

-            output.WriteFixed32(100);

-            output.WriteTag(2, WireFormat.WireType.LengthDelimited);

-            output.WriteString("ignore me");

-            // The nested group...

-            output.WriteTag(3, WireFormat.WireType.StartGroup);

-            output.WriteTag(1, WireFormat.WireType.Fixed64);

-            output.WriteFixed64(1000);

-            // Note: Not sure the field number is relevant for end group...

-            output.WriteTag(3, WireFormat.WireType.EndGroup);

-

-            // End the outer group

-            output.WriteTag(2, WireFormat.WireType.EndGroup);

-

-            output.WriteTag(3, WireFormat.WireType.LengthDelimited);

-            output.WriteString("field 3");

-            output.Flush();

-            stream.Position = 0;

-

-            // Now act like a generated client

-            var input = new CodedInputStream(stream);

-            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());

-            Assert.AreEqual("field 1", input.ReadString());

-            Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());

-            input.SkipLastField(); // Should consume the whole group, including the nested one.

-            Assert.AreEqual(WireFormat.MakeTag(3, WireFormat.WireType.LengthDelimited), input.ReadTag());

-            Assert.AreEqual("field 3", input.ReadString());

-        }

-

-        [Test]

-        public void SkipGroup_WrongEndGroupTag()

-        {

-            // Create an output stream with:

-            // Field 1: string "field 1"

-            // Start group 2

-            //   Field 3: fixed int32

-            // End group 4 (should give an error)

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-            output.WriteTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteString("field 1");

-

-            // The outer group...

-            output.WriteTag(2, WireFormat.WireType.StartGroup);

-            output.WriteTag(3, WireFormat.WireType.Fixed32);

-            output.WriteFixed32(100);

-            output.WriteTag(4, WireFormat.WireType.EndGroup);

-            output.Flush();

-            stream.Position = 0;

-

-            // Now act like a generated client

-            var input = new CodedInputStream(stream);

-            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());

-            Assert.AreEqual("field 1", input.ReadString());

-            Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());

-            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);

-        }

-

-        [Test]

-        public void RogueEndGroupTag()

-        {

-            // If we have an end-group tag without a leading start-group tag, generated

-            // code will just call SkipLastField... so that should fail.

-

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-            output.WriteTag(1, WireFormat.WireType.EndGroup);

-            output.Flush();

-            stream.Position = 0;

-

-            var input = new CodedInputStream(stream);

-            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.EndGroup), input.ReadTag());

-            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);

-        }

-

-        [Test]

-        public void EndOfStreamReachedWhileSkippingGroup()

-        {

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-            output.WriteTag(1, WireFormat.WireType.StartGroup);

-            output.WriteTag(2, WireFormat.WireType.StartGroup);

-            output.WriteTag(2, WireFormat.WireType.EndGroup);

-

-            output.Flush();

-            stream.Position = 0;

-

-            // Now act like a generated client

-            var input = new CodedInputStream(stream);

-            input.ReadTag();

-            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);

-        }

-

-        [Test]

-        public void RecursionLimitAppliedWhileSkippingGroup()

-        {

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-            for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)

-            {

-                output.WriteTag(1, WireFormat.WireType.StartGroup);

-            }

-            for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)

-            {

-                output.WriteTag(1, WireFormat.WireType.EndGroup);

-            }

-            output.Flush();

-            stream.Position = 0;

-

-            // Now act like a generated client

-            var input = new CodedInputStream(stream);

-            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.StartGroup), input.ReadTag());

-            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);

-        }

-

-        [Test]

-        public void Construction_Invalid()

-        {

-            Assert.Throws<ArgumentNullException>(() => new CodedInputStream((byte[]) null));

-            Assert.Throws<ArgumentNullException>(() => new CodedInputStream(null, 0, 0));

-            Assert.Throws<ArgumentNullException>(() => new CodedInputStream((Stream) null));

-            Assert.Throws<ArgumentOutOfRangeException>(() => new CodedInputStream(new byte[10], 100, 0));

-            Assert.Throws<ArgumentOutOfRangeException>(() => new CodedInputStream(new byte[10], 5, 10));

-        }

-

-        [Test]

-        public void CreateWithLimits_InvalidLimits()

-        {

-            var stream = new MemoryStream();

-            Assert.Throws<ArgumentOutOfRangeException>(() => CodedInputStream.CreateWithLimits(stream, 0, 1));

-            Assert.Throws<ArgumentOutOfRangeException>(() => CodedInputStream.CreateWithLimits(stream, 1, 0));

-        }

-

-        [Test]

-        public void Dispose_DisposesUnderlyingStream()

-        {

-            var memoryStream = new MemoryStream();

-            Assert.IsTrue(memoryStream.CanRead);

-            using (var cis = new CodedInputStream(memoryStream))

-            {

-            }

-            Assert.IsFalse(memoryStream.CanRead); // Disposed

-        }

-

-        [Test]

-        public void Dispose_WithLeaveOpen()

-        {

-            var memoryStream = new MemoryStream();

-            Assert.IsTrue(memoryStream.CanRead);

-            using (var cis = new CodedInputStream(memoryStream, true))

-            {

-            }

-            Assert.IsTrue(memoryStream.CanRead); // We left the stream open

-        }

-

-        [Test]

-        public void Dispose_FromByteArray()

-        {

-            var stream = new CodedInputStream(new byte[10]);

-            stream.Dispose();

-        }

-

-        [Test]

-        public void TestParseMessagesCloseTo2G()

-        {

-            byte[] serializedMessage = GenerateBigSerializedMessage();

-            // How many of these big messages do we need to take us near our 2GB limit?

-            int count = Int32.MaxValue / serializedMessage.Length;

-            // Now make a MemoryStream that will fake a near-2GB stream of messages by returning

-            // our big serialized message 'count' times.

-            using (RepeatingMemoryStream stream = new RepeatingMemoryStream(serializedMessage, count))

-            {

-                Assert.DoesNotThrow(()=>TestAllTypes.Parser.ParseFrom(stream));

-            }

-        }

-

-        [Test]

-        public void TestParseMessagesOver2G()

-        {

-            byte[] serializedMessage = GenerateBigSerializedMessage();

-            // How many of these big messages do we need to take us near our 2GB limit?

-            int count = Int32.MaxValue / serializedMessage.Length;

-            // Now add one to take us over the 2GB limit

-            count++;

-            // Now make a MemoryStream that will fake a near-2GB stream of messages by returning

-            // our big serialized message 'count' times.

-            using (RepeatingMemoryStream stream = new RepeatingMemoryStream(serializedMessage, count))

-            {

-                Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(stream),

-                    "Protocol message was too large.  May be malicious.  " +

-                    "Use CodedInputStream.SetSizeLimit() to increase the size limit.");

-            }

-        }

-

-        /// <returns>A serialized big message</returns>

-        private static byte[] GenerateBigSerializedMessage()

-        {

-            byte[] value = new byte[16 * 1024 * 1024];

-            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();

-            message.SingleBytes = ByteString.CopyFrom(value);

-            return message.ToByteArray();

-        }

-

-        /// <summary>

-        /// A MemoryStream that repeats a byte arrays' content a number of times.

-        /// Simulates really large input without consuming loads of memory. Used above

-        /// to test the parsing behavior when the input size exceeds 2GB or close to it.

-        /// </summary>

-        private class RepeatingMemoryStream: MemoryStream

-        {

-            private readonly byte[] bytes;

-            private readonly int maxIterations;

-            private int index = 0;

-

-            public RepeatingMemoryStream(byte[] bytes, int maxIterations)

-            {

-                this.bytes = bytes;

-                this.maxIterations = maxIterations;

-            }

-

-            public override int Read(byte[] buffer, int offset, int count)

-            {

-                if (bytes.Length == 0)

-                {

-                    return 0;

-                }

-                int numBytesCopiedTotal = 0;

-                while (numBytesCopiedTotal < count && index < maxIterations)

-                {

-                    int numBytesToCopy = Math.Min(bytes.Length - (int)Position, count);

-                    Array.Copy(bytes, (int)Position, buffer, offset, numBytesToCopy);

-                    numBytesCopiedTotal += numBytesToCopy;

-                    offset += numBytesToCopy;

-                    count -= numBytesCopiedTotal;

-                    Position += numBytesToCopy;

-                    if (Position >= bytes.Length)

-                    {

-                        Position = 0;

-                        index++;

-                    }

-                }

-                return numBytesCopiedTotal;

-            }

-        }

-    }

-}

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Buffers;
+using System.IO;
+using Google.Protobuf.TestProtos;
+using Proto2 = Google.Protobuf.TestProtos.Proto2;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+    public class CodedInputStreamTest
+    {
+        /// <summary>
+        /// Helper to construct a byte array from a bunch of bytes.  The inputs are
+        /// actually ints so that I can use hex notation and not get stupid errors
+        /// about precision.
+        /// </summary>
+        private static byte[] Bytes(params int[] bytesAsInts)
+        {
+            byte[] bytes = new byte[bytesAsInts.Length];
+            for (int i = 0; i < bytesAsInts.Length; i++)
+            {
+                bytes[i] = (byte) bytesAsInts[i];
+            }
+            return bytes;
+        }
+
+        /// <summary>
+        /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64()
+        /// </summary>
+        private static void AssertReadVarint(byte[] data, ulong value)
+        {
+            CodedInputStream input = new CodedInputStream(data);
+            Assert.AreEqual((uint) value, input.ReadRawVarint32());
+            Assert.IsTrue(input.IsAtEnd);
+
+            input = new CodedInputStream(data);
+            Assert.AreEqual(value, input.ReadRawVarint64());
+            Assert.IsTrue(input.IsAtEnd);
+
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>
+            {
+                Assert.AreEqual((uint) value, ctx.ReadUInt32());
+            }, true);
+
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>
+            {
+                Assert.AreEqual(value, ctx.ReadUInt64());
+            }, true);
+
+            // Try different block sizes.
+            for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
+            {
+                input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
+                Assert.AreEqual((uint) value, input.ReadRawVarint32());
+
+                input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
+                Assert.AreEqual(value, input.ReadRawVarint64());
+                Assert.IsTrue(input.IsAtEnd);
+
+                AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, bufferSize), (ref ParseContext ctx) =>
+                {
+                    Assert.AreEqual((uint) value, ctx.ReadUInt32());
+                }, true);
+
+                AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, bufferSize), (ref ParseContext ctx) =>
+                {
+                    Assert.AreEqual(value, ctx.ReadUInt64());
+                }, true);
+            }
+
+            // Try reading directly from a MemoryStream. We want to verify that it
+            // doesn't read past the end of the input, so write an extra byte - this
+            // lets us test the position at the end.
+            MemoryStream memoryStream = new MemoryStream();
+            memoryStream.Write(data, 0, data.Length);
+            memoryStream.WriteByte(0);
+            memoryStream.Position = 0;
+            Assert.AreEqual((uint) value, CodedInputStream.ReadRawVarint32(memoryStream));
+            Assert.AreEqual(data.Length, memoryStream.Position);
+        }
+
+        /// <summary>
+        /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and
+        /// expects them to fail with an InvalidProtocolBufferException whose
+        /// description matches the given one.
+        /// </summary>
+        private static void AssertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)
+        {
+            CodedInputStream input = new CodedInputStream(data);
+            var exception = Assert.Throws<InvalidProtocolBufferException>(() => input.ReadRawVarint32());
+            Assert.AreEqual(expected.Message, exception.Message);
+
+            input = new CodedInputStream(data);
+            exception = Assert.Throws<InvalidProtocolBufferException>(() => input.ReadRawVarint64());
+            Assert.AreEqual(expected.Message, exception.Message);
+
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>
+            {
+                try
+                {
+                    ctx.ReadUInt32();
+                    Assert.Fail();
+                }
+                catch (InvalidProtocolBufferException ex)
+                {
+                    Assert.AreEqual(expected.Message, ex.Message);
+                }
+            }, false);
+
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>
+            {
+                try
+                {
+                    ctx.ReadUInt64();
+                    Assert.Fail();
+                }
+                catch (InvalidProtocolBufferException ex)
+                {
+                    Assert.AreEqual(expected.Message, ex.Message);
+                }
+            }, false);
+
+            // Make sure we get the same error when reading directly from a Stream.
+            exception = Assert.Throws<InvalidProtocolBufferException>(() => CodedInputStream.ReadRawVarint32(new MemoryStream(data)));
+            Assert.AreEqual(expected.Message, exception.Message);
+        }
+
+        private delegate void ParseContextAssertAction(ref ParseContext ctx);
+
+        private static void AssertReadFromParseContext(ReadOnlySequence<byte> input, ParseContextAssertAction assertAction, bool assertIsAtEnd)
+        {
+            // Check as ReadOnlySequence<byte>
+            ParseContext.Initialize(input, out ParseContext parseCtx);
+            assertAction(ref parseCtx);
+            if (assertIsAtEnd)
+            {
+                Assert.IsTrue(SegmentedBufferHelper.IsAtEnd(ref parseCtx.buffer, ref parseCtx.state));
+            }
+
+            // Check as ReadOnlySpan<byte>
+            ParseContext.Initialize(input.ToArray().AsSpan(), out ParseContext spanParseContext);
+            assertAction(ref spanParseContext);
+            if (assertIsAtEnd)
+            {
+                Assert.IsTrue(SegmentedBufferHelper.IsAtEnd(ref spanParseContext.buffer, ref spanParseContext.state));
+            }
+        }
+
+        [Test]
+        public void ReadVarint()
+        {
+            AssertReadVarint(Bytes(0x00), 0);
+            AssertReadVarint(Bytes(0x01), 1);
+            AssertReadVarint(Bytes(0x7f), 127);
+            // 14882
+            AssertReadVarint(Bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
+            // 2961488830
+            AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
+                             (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+                             (0x0bL << 28));
+
+            // 64-bit
+            // 7256456126
+            AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
+                             (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+                             (0x1bL << 28));
+            // 41256202580718336
+            AssertReadVarint(Bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
+                             (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
+                             (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
+            // 11964378330978735131
+            AssertReadVarint(Bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
+                             (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
+                             (0x3bUL << 28) | (0x56UL << 35) | (0x00UL << 42) |
+                             (0x05UL << 49) | (0x26UL << 56) | (0x01UL << 63));
+
+            // Failures
+            AssertReadVarintFailure(
+                InvalidProtocolBufferException.MalformedVarint(),
+                Bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+                      0x00));
+            AssertReadVarintFailure(
+                InvalidProtocolBufferException.TruncatedMessage(),
+                Bytes(0x80));
+        }
+
+        /// <summary>
+        /// Parses the given bytes using ReadRawLittleEndian32() and checks
+        /// that the result matches the given value.
+        /// </summary>
+        private static void AssertReadLittleEndian32(byte[] data, uint value)
+        {
+            CodedInputStream input = new CodedInputStream(data);
+            Assert.AreEqual(value, input.ReadRawLittleEndian32());
+            Assert.IsTrue(input.IsAtEnd);
+
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>
+            {
+                Assert.AreEqual(value, ctx.ReadFixed32());
+            }, true);
+
+            // Try different block sizes.
+            for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
+            {
+                input = new CodedInputStream(
+                    new SmallBlockInputStream(data, blockSize));
+                Assert.AreEqual(value, input.ReadRawLittleEndian32());
+                Assert.IsTrue(input.IsAtEnd);
+
+                AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, blockSize), (ref ParseContext ctx) =>
+                {
+                    Assert.AreEqual(value, ctx.ReadFixed32());
+                }, true);
+            }
+        }
+
+        /// <summary>
+        /// Parses the given bytes using ReadRawLittleEndian64() and checks
+        /// that the result matches the given value.
+        /// </summary>
+        private static void AssertReadLittleEndian64(byte[] data, ulong value)
+        {
+            CodedInputStream input = new CodedInputStream(data);
+            Assert.AreEqual(value, input.ReadRawLittleEndian64());
+            Assert.IsTrue(input.IsAtEnd);
+
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(data), (ref ParseContext ctx) =>
+            {
+                Assert.AreEqual(value, ctx.ReadFixed64());
+            }, true);
+
+            // Try different block sizes.
+            for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
+            {
+                input = new CodedInputStream(
+                    new SmallBlockInputStream(data, blockSize));
+                Assert.AreEqual(value, input.ReadRawLittleEndian64());
+                Assert.IsTrue(input.IsAtEnd);
+
+                AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, blockSize), (ref ParseContext ctx) =>
+                {
+                    Assert.AreEqual(value, ctx.ReadFixed64());
+                }, true);
+            }
+        }
+
+        [Test]
+        public void ReadLittleEndian()
+        {
+            AssertReadLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
+            AssertReadLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
+
+            AssertReadLittleEndian64(Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
+                                     0x123456789abcdef0L);
+            AssertReadLittleEndian64(
+                Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678UL);
+        }
+
+        [Test]
+        public void DecodeZigZag32()
+        {
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(0));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(1));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(2));
+            Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag32(3));
+            Assert.AreEqual(0x3FFFFFFF, ParsingPrimitives.DecodeZigZag32(0x7FFFFFFE));
+            Assert.AreEqual(unchecked((int) 0xC0000000), ParsingPrimitives.DecodeZigZag32(0x7FFFFFFF));
+            Assert.AreEqual(0x7FFFFFFF, ParsingPrimitives.DecodeZigZag32(0xFFFFFFFE));
+            Assert.AreEqual(unchecked((int) 0x80000000), ParsingPrimitives.DecodeZigZag32(0xFFFFFFFF));
+        }
+
+        [Test]
+        public void DecodeZigZag64()
+        {
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(0));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(1));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(2));
+            Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag64(3));
+            Assert.AreEqual(0x000000003FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFEL));
+            Assert.AreEqual(unchecked((long) 0xFFFFFFFFC0000000L), ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFFL));
+            Assert.AreEqual(0x000000007FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFEL));
+            Assert.AreEqual(unchecked((long) 0xFFFFFFFF80000000L), ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFFL));
+            Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
+            Assert.AreEqual(unchecked((long) 0x8000000000000000L), ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
+        }
+        
+        [Test]
+        public void ReadWholeMessage_VaryingBlockSizes()
+        {
+            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+            byte[] rawBytes = message.ToByteArray();
+            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.Parser.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));
+                Assert.AreEqual(message, message2);
+            }
+        }
+
+        [Test]
+        public void ReadWholeMessage_VaryingBlockSizes_FromSequence()
+        {
+            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+            byte[] rawBytes = message.ToByteArray();
+            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.Parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(rawBytes, blockSize));
+                Assert.AreEqual(message, message2);
+            }
+        }
+
+        [Test]
+        public void ReadInt32Wrapper_VariableBlockSizes()
+        {
+            byte[] rawBytes = new byte[] { 202, 1, 11, 8, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1 };
+
+            for (int blockSize = 1; blockSize <= rawBytes.Length; blockSize++)
+            {
+                ReadOnlySequence<byte> data = ReadOnlySequenceFactory.CreateWithContent(rawBytes, blockSize);
+                AssertReadFromParseContext(data, (ref ParseContext ctx) =>
+                {
+                    ctx.ReadTag();
+
+                    var value = ParsingPrimitivesWrappers.ReadInt32Wrapper(ref ctx);
+
+                    Assert.AreEqual(-2, value);
+                }, true);
+            }
+        }
+
+        [Test]
+        public void ReadHugeBlob()
+        {
+            // Allocate and initialize a 1MB blob.
+            byte[] blob = new byte[1 << 20];
+            for (int i = 0; i < blob.Length; i++)
+            {
+                blob[i] = (byte) i;
+            }
+
+            // Make a message containing it.
+            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.Parser.ParseFrom(message.ToByteString());
+
+            Assert.AreEqual(message, message2);
+        }
+
+        [Test]
+        public void ReadMaliciouslyLargeBlob()
+        {
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+
+            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteRawVarint32(tag);
+            output.WriteRawVarint32(0x7FFFFFFF);
+            output.WriteRawBytes(new byte[32]); // Pad with a few random bytes.
+            output.Flush();
+            ms.Position = 0;
+
+            CodedInputStream input = new CodedInputStream(ms);
+            Assert.AreEqual(tag, input.ReadTag());
+
+            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadBytes());
+        }
+
+        [Test]
+        public void ReadBlobGreaterThanCurrentLimit()
+        {
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteRawVarint32(tag);
+            output.WriteRawVarint32(4);
+            output.WriteRawBytes(new byte[4]); // Pad with a few random bytes.
+            output.Flush();
+            ms.Position = 0;
+
+            CodedInputStream input = new CodedInputStream(ms);
+            Assert.AreEqual(tag, input.ReadTag());
+
+            // Specify limit smaller than data length
+            input.PushLimit(3);
+            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadBytes());
+
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(ms.ToArray()), (ref ParseContext ctx) =>
+            {
+                Assert.AreEqual(tag, ctx.ReadTag());
+                SegmentedBufferHelper.PushLimit(ref ctx.state, 3);
+                try
+                {
+                    ctx.ReadBytes();
+                    Assert.Fail();
+                }
+                catch (InvalidProtocolBufferException) {}
+            }, true);
+        }
+
+        [Test]
+        public void ReadStringGreaterThanCurrentLimit()
+        {
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteRawVarint32(tag);
+            output.WriteRawVarint32(4);
+            output.WriteRawBytes(new byte[4]); // Pad with a few random bytes.
+            output.Flush();
+            ms.Position = 0;
+
+            CodedInputStream input = new CodedInputStream(ms.ToArray());
+            Assert.AreEqual(tag, input.ReadTag());
+
+            // Specify limit smaller than data length
+            input.PushLimit(3);
+            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadString());
+
+            AssertReadFromParseContext(new ReadOnlySequence<byte>(ms.ToArray()), (ref ParseContext ctx) =>
+            {
+                Assert.AreEqual(tag, ctx.ReadTag());
+                SegmentedBufferHelper.PushLimit(ref ctx.state, 3);
+                try
+                {
+                    ctx.ReadString();
+                    Assert.Fail();
+                }
+                catch (InvalidProtocolBufferException) { }
+            }, true);
+        }
+
+        // Representations of a tag for field 0 with various wire types
+        [Test]
+        [TestCase(0)]
+        [TestCase(1)]
+        [TestCase(2)]
+        [TestCase(3)]
+        [TestCase(4)]
+        [TestCase(5)]
+        public void ReadTag_ZeroFieldRejected(byte tag)
+        {
+            CodedInputStream cis = new CodedInputStream(new byte[] { tag });
+            Assert.Throws<InvalidProtocolBufferException>(() => cis.ReadTag());
+        }
+
+        internal static TestRecursiveMessage MakeRecursiveMessage(int depth)
+        {
+            if (depth == 0)
+            {
+                return new TestRecursiveMessage { I = 5 };
+            }
+            else
+            {
+                return new TestRecursiveMessage { A = MakeRecursiveMessage(depth - 1) };
+            }
+        }
+
+        internal static void AssertMessageDepth(TestRecursiveMessage message, int depth)
+        {
+            if (depth == 0)
+            {
+                Assert.IsNull(message.A);
+                Assert.AreEqual(5, message.I);
+            }
+            else
+            {
+                Assert.IsNotNull(message.A);
+                AssertMessageDepth(message.A, depth - 1);
+            }
+        }
+
+        [Test]
+        public void MaliciousRecursion()
+        {
+            ByteString atRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit).ToByteString();
+            ByteString beyondRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit + 1).ToByteString();
+
+            AssertMessageDepth(TestRecursiveMessage.Parser.ParseFrom(atRecursiveLimit), CodedInputStream.DefaultRecursionLimit);
+
+            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(beyondRecursiveLimit));
+
+            CodedInputStream input = CodedInputStream.CreateWithLimits(new MemoryStream(atRecursiveLimit.ToByteArray()), 1000000, CodedInputStream.DefaultRecursionLimit - 1);
+            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(input));
+        }
+        
+        private static byte[] MakeMaliciousRecursionUnknownFieldsPayload(int recursionDepth)
+        {
+            // generate recursively nested groups that will be parsed as unknown fields
+            int unknownFieldNumber = 14;  // an unused field number
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+            for (int i = 0; i < recursionDepth; i++)
+            {
+                output.WriteTag(WireFormat.MakeTag(unknownFieldNumber, WireFormat.WireType.StartGroup));
+            }
+            for (int i = 0; i < recursionDepth; i++)
+            {
+                output.WriteTag(WireFormat.MakeTag(unknownFieldNumber, WireFormat.WireType.EndGroup));
+            }
+            output.Flush();
+            return ms.ToArray();
+        }
+
+        [Test]
+        public void MaliciousRecursion_UnknownFields()
+        {
+            byte[] payloadAtRecursiveLimit = MakeMaliciousRecursionUnknownFieldsPayload(CodedInputStream.DefaultRecursionLimit);
+            byte[] payloadBeyondRecursiveLimit = MakeMaliciousRecursionUnknownFieldsPayload(CodedInputStream.DefaultRecursionLimit + 1);
+            
+            Assert.DoesNotThrow(() => TestRecursiveMessage.Parser.ParseFrom(payloadAtRecursiveLimit));
+            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(payloadBeyondRecursiveLimit));
+        }
+
+        [Test]
+        public void ReadGroup_WrongEndGroupTag()
+        {
+            int groupFieldNumber = Proto2.TestAllTypes.OptionalGroupFieldNumber;
+
+            // write Proto2.TestAllTypes with "optional_group" set, but use wrong EndGroup closing tag
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+            output.WriteTag(WireFormat.MakeTag(groupFieldNumber, WireFormat.WireType.StartGroup));
+            output.WriteGroup(new Proto2.TestAllTypes.Types.OptionalGroup { A = 12345 });
+            // end group with different field number
+            output.WriteTag(WireFormat.MakeTag(groupFieldNumber + 1, WireFormat.WireType.EndGroup));
+            output.Flush();
+            var payload = ms.ToArray();
+
+            Assert.Throws<InvalidProtocolBufferException>(() => Proto2.TestAllTypes.Parser.ParseFrom(payload));
+        }
+
+        [Test]
+        public void ReadGroup_UnknownFields_WrongEndGroupTag()
+        {
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+            output.WriteTag(WireFormat.MakeTag(14, WireFormat.WireType.StartGroup));
+            // end group with different field number
+            output.WriteTag(WireFormat.MakeTag(15, WireFormat.WireType.EndGroup));
+            output.Flush();
+            var payload = ms.ToArray();
+
+            Assert.Throws<InvalidProtocolBufferException>(() => TestRecursiveMessage.Parser.ParseFrom(payload));
+        }
+
+        [Test]
+        public void SizeLimit()
+        {
+            // Have to use a Stream rather than ByteString.CreateCodedInput as SizeLimit doesn't
+            // apply to the latter case.
+            MemoryStream ms = new MemoryStream(SampleMessages.CreateFullTestAllTypes().ToByteArray());
+            CodedInputStream input = CodedInputStream.CreateWithLimits(ms, 16, 100);
+            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(input));
+        }
+
+        /// <summary>
+        /// Tests that if we read an string that contains invalid UTF-8, no exception
+        /// is thrown.  Instead, the invalid bytes are replaced with the Unicode
+        /// "replacement character" U+FFFD.
+        /// </summary>
+        [Test]
+        public void ReadInvalidUtf8()
+        {
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+
+            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteRawVarint32(tag);
+            output.WriteRawVarint32(1);
+            output.WriteRawBytes(new byte[] {0x80});
+            output.Flush();
+            ms.Position = 0;
+
+            CodedInputStream input = new CodedInputStream(ms);
+
+            Assert.AreEqual(tag, input.ReadTag());
+            string text = input.ReadString();
+            Assert.AreEqual('\ufffd', text[0]);
+        }
+
+        [Test]
+        public void ReadNegativeSizedStringThrowsInvalidProtocolBufferException()
+        {
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+
+            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteRawVarint32(tag);
+            output.WriteLength(-1);
+            output.Flush();
+            ms.Position = 0;
+
+            CodedInputStream input = new CodedInputStream(ms);
+
+            Assert.AreEqual(tag, input.ReadTag());
+            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadString());
+        }
+
+        [Test]
+        public void ReadNegativeSizedBytesThrowsInvalidProtocolBufferException()
+        {
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+
+            uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteRawVarint32(tag);
+            output.WriteLength(-1);
+            output.Flush();
+            ms.Position = 0;
+
+            CodedInputStream input = new CodedInputStream(ms);
+
+            Assert.AreEqual(tag, input.ReadTag());
+            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadBytes());
+        }
+
+        /// <summary>
+        /// A stream which limits the number of bytes it reads at a time.
+        /// We use this to make sure that CodedInputStream doesn't screw up when
+        /// reading in small blocks.
+        /// </summary>
+        private sealed class SmallBlockInputStream : MemoryStream
+        {
+            private readonly int blockSize;
+
+            public SmallBlockInputStream(byte[] data, int blockSize)
+                : base(data)
+            {
+                this.blockSize = blockSize;
+            }
+
+            public override int Read(byte[] buffer, int offset, int count)
+            {
+                return base.Read(buffer, offset, Math.Min(count, blockSize));
+            }
+        }
+
+        [Test]
+        public void TestNegativeEnum()
+        {
+            byte[] bytes = { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };
+            CodedInputStream input = new CodedInputStream(bytes);
+            Assert.AreEqual((int)SampleEnum.NegativeValue, input.ReadEnum());
+            Assert.IsTrue(input.IsAtEnd);
+        }
+
+        //Issue 71:	CodedInputStream.ReadBytes go to slow path unnecessarily
+        [Test]
+        public void TestSlowPathAvoidance()
+        {
+            using (var ms = new MemoryStream())
+            {
+                CodedOutputStream output = new CodedOutputStream(ms);
+                output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+                output.WriteBytes(ByteString.CopyFrom(new byte[100]));
+                output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+                output.WriteBytes(ByteString.CopyFrom(new byte[100]));
+                output.Flush();
+
+                ms.Position = 0;
+                CodedInputStream input = new CodedInputStream(ms, new byte[ms.Length / 2], 0, 0, false);
+
+                uint tag = input.ReadTag();
+                Assert.AreEqual(1, WireFormat.GetTagFieldNumber(tag));
+                Assert.AreEqual(100, input.ReadBytes().Length);
+
+                tag = input.ReadTag();
+                Assert.AreEqual(2, WireFormat.GetTagFieldNumber(tag));
+                Assert.AreEqual(100, input.ReadBytes().Length);
+            }
+        }
+
+        [Test]
+        public void MaximumFieldNumber()
+        {
+            MemoryStream ms = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(ms);
+
+            int fieldNumber = 0x1FFFFFFF;
+            uint tag = WireFormat.MakeTag(fieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteRawVarint32(tag);
+            output.WriteString("field 1");
+            output.Flush();
+            ms.Position = 0;
+
+            CodedInputStream input = new CodedInputStream(ms);
+
+            Assert.AreEqual(tag, input.ReadTag());
+            Assert.AreEqual(fieldNumber, WireFormat.GetTagFieldNumber(tag));
+        }
+
+        [Test]
+        public void Tag0Throws()
+        {
+            var input = new CodedInputStream(new byte[] { 0 });
+            Assert.Throws<InvalidProtocolBufferException>(() => input.ReadTag());
+        }
+
+        [Test]
+        public void SkipGroup()
+        {
+            // Create an output stream with a group in:
+            // Field 1: string "field 1"
+            // Field 2: group containing:
+            //   Field 1: fixed int32 value 100
+            //   Field 2: string "ignore me"
+            //   Field 3: nested group containing
+            //      Field 1: fixed int64 value 1000
+            // Field 3: string "field 3"
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+            output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteString("field 1");
+            
+            // The outer group...
+            output.WriteTag(2, WireFormat.WireType.StartGroup);
+            output.WriteTag(1, WireFormat.WireType.Fixed32);
+            output.WriteFixed32(100);
+            output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+            output.WriteString("ignore me");
+            // The nested group...
+            output.WriteTag(3, WireFormat.WireType.StartGroup);
+            output.WriteTag(1, WireFormat.WireType.Fixed64);
+            output.WriteFixed64(1000);
+            // Note: Not sure the field number is relevant for end group...
+            output.WriteTag(3, WireFormat.WireType.EndGroup);
+
+            // End the outer group
+            output.WriteTag(2, WireFormat.WireType.EndGroup);
+
+            output.WriteTag(3, WireFormat.WireType.LengthDelimited);
+            output.WriteString("field 3");
+            output.Flush();
+            stream.Position = 0;
+
+            // Now act like a generated client
+            var input = new CodedInputStream(stream);
+            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());
+            Assert.AreEqual("field 1", input.ReadString());
+            Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());
+            input.SkipLastField(); // Should consume the whole group, including the nested one.
+            Assert.AreEqual(WireFormat.MakeTag(3, WireFormat.WireType.LengthDelimited), input.ReadTag());
+            Assert.AreEqual("field 3", input.ReadString());
+        }
+
+        [Test]
+        public void SkipGroup_WrongEndGroupTag()
+        {
+            // Create an output stream with:
+            // Field 1: string "field 1"
+            // Start group 2
+            //   Field 3: fixed int32
+            // End group 4 (should give an error)
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+            output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteString("field 1");
+
+            // The outer group...
+            output.WriteTag(2, WireFormat.WireType.StartGroup);
+            output.WriteTag(3, WireFormat.WireType.Fixed32);
+            output.WriteFixed32(100);
+            output.WriteTag(4, WireFormat.WireType.EndGroup);
+            output.Flush();
+            stream.Position = 0;
+
+            // Now act like a generated client
+            var input = new CodedInputStream(stream);
+            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());
+            Assert.AreEqual("field 1", input.ReadString());
+            Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());
+            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);
+        }
+
+        [Test]
+        public void RogueEndGroupTag()
+        {
+            // If we have an end-group tag without a leading start-group tag, generated
+            // code will just call SkipLastField... so that should fail.
+
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+            output.WriteTag(1, WireFormat.WireType.EndGroup);
+            output.Flush();
+            stream.Position = 0;
+
+            var input = new CodedInputStream(stream);
+            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.EndGroup), input.ReadTag());
+            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);
+        }
+
+        [Test]
+        public void EndOfStreamReachedWhileSkippingGroup()
+        {
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+            output.WriteTag(1, WireFormat.WireType.StartGroup);
+            output.WriteTag(2, WireFormat.WireType.StartGroup);
+            output.WriteTag(2, WireFormat.WireType.EndGroup);
+
+            output.Flush();
+            stream.Position = 0;
+
+            // Now act like a generated client
+            var input = new CodedInputStream(stream);
+            input.ReadTag();
+            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);
+        }
+
+        [Test]
+        public void RecursionLimitAppliedWhileSkippingGroup()
+        {
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+            for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)
+            {
+                output.WriteTag(1, WireFormat.WireType.StartGroup);
+            }
+            for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)
+            {
+                output.WriteTag(1, WireFormat.WireType.EndGroup);
+            }
+            output.Flush();
+            stream.Position = 0;
+
+            // Now act like a generated client
+            var input = new CodedInputStream(stream);
+            Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.StartGroup), input.ReadTag());
+            Assert.Throws<InvalidProtocolBufferException>(input.SkipLastField);
+        }
+
+        [Test]
+        public void Construction_Invalid()
+        {
+            Assert.Throws<ArgumentNullException>(() => new CodedInputStream((byte[]) null));
+            Assert.Throws<ArgumentNullException>(() => new CodedInputStream(null, 0, 0));
+            Assert.Throws<ArgumentNullException>(() => new CodedInputStream((Stream) null));
+            Assert.Throws<ArgumentOutOfRangeException>(() => new CodedInputStream(new byte[10], 100, 0));
+            Assert.Throws<ArgumentOutOfRangeException>(() => new CodedInputStream(new byte[10], 5, 10));
+        }
+
+        [Test]
+        public void CreateWithLimits_InvalidLimits()
+        {
+            var stream = new MemoryStream();
+            Assert.Throws<ArgumentOutOfRangeException>(() => CodedInputStream.CreateWithLimits(stream, 0, 1));
+            Assert.Throws<ArgumentOutOfRangeException>(() => CodedInputStream.CreateWithLimits(stream, 1, 0));
+        }
+
+        [Test]
+        public void Dispose_DisposesUnderlyingStream()
+        {
+            var memoryStream = new MemoryStream();
+            Assert.IsTrue(memoryStream.CanRead);
+            using (var cis = new CodedInputStream(memoryStream))
+            {
+            }
+            Assert.IsFalse(memoryStream.CanRead); // Disposed
+        }
+
+        [Test]
+        public void Dispose_WithLeaveOpen()
+        {
+            var memoryStream = new MemoryStream();
+            Assert.IsTrue(memoryStream.CanRead);
+            using (var cis = new CodedInputStream(memoryStream, true))
+            {
+            }
+            Assert.IsTrue(memoryStream.CanRead); // We left the stream open
+        }
+
+        [Test]
+        public void Dispose_FromByteArray()
+        {
+            var stream = new CodedInputStream(new byte[10]);
+            stream.Dispose();
+        }
+
+        [Test]
+        public void TestParseMessagesCloseTo2G()
+        {
+            byte[] serializedMessage = GenerateBigSerializedMessage();
+            // How many of these big messages do we need to take us near our 2GB limit?
+            int count = Int32.MaxValue / serializedMessage.Length;
+            // Now make a MemoryStream that will fake a near-2GB stream of messages by returning
+            // our big serialized message 'count' times.
+            using (RepeatingMemoryStream stream = new RepeatingMemoryStream(serializedMessage, count))
+            {
+                Assert.DoesNotThrow(()=>TestAllTypes.Parser.ParseFrom(stream));
+            }
+        }
+
+        [Test]
+        public void TestParseMessagesOver2G()
+        {
+            byte[] serializedMessage = GenerateBigSerializedMessage();
+            // How many of these big messages do we need to take us near our 2GB limit?
+            int count = Int32.MaxValue / serializedMessage.Length;
+            // Now add one to take us over the 2GB limit
+            count++;
+            // Now make a MemoryStream that will fake a near-2GB stream of messages by returning
+            // our big serialized message 'count' times.
+            using (RepeatingMemoryStream stream = new RepeatingMemoryStream(serializedMessage, count))
+            {
+                Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(stream),
+                    "Protocol message was too large.  May be malicious.  " +
+                    "Use CodedInputStream.SetSizeLimit() to increase the size limit.");
+            }
+        }
+
+        /// <returns>A serialized big message</returns>
+        private static byte[] GenerateBigSerializedMessage()
+        {
+            byte[] value = new byte[16 * 1024 * 1024];
+            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+            message.SingleBytes = ByteString.CopyFrom(value);
+            return message.ToByteArray();
+        }
+
+        /// <summary>
+        /// A MemoryStream that repeats a byte arrays' content a number of times.
+        /// Simulates really large input without consuming loads of memory. Used above
+        /// to test the parsing behavior when the input size exceeds 2GB or close to it.
+        /// </summary>
+        private class RepeatingMemoryStream: MemoryStream
+        {
+            private readonly byte[] bytes;
+            private readonly int maxIterations;
+            private int index = 0;
+
+            public RepeatingMemoryStream(byte[] bytes, int maxIterations)
+            {
+                this.bytes = bytes;
+                this.maxIterations = maxIterations;
+            }
+
+            public override int Read(byte[] buffer, int offset, int count)
+            {
+                if (bytes.Length == 0)
+                {
+                    return 0;
+                }
+                int numBytesCopiedTotal = 0;
+                while (numBytesCopiedTotal < count && index < maxIterations)
+                {
+                    int numBytesToCopy = Math.Min(bytes.Length - (int)Position, count);
+                    Array.Copy(bytes, (int)Position, buffer, offset, numBytesToCopy);
+                    numBytesCopiedTotal += numBytesToCopy;
+                    offset += numBytesToCopy;
+                    count -= numBytesCopiedTotal;
+                    Position += numBytesToCopy;
+                    if (Position >= bytes.Length)
+                    {
+                        Position = 0;
+                        index++;
+                    }
+                }
+                return numBytesCopiedTotal;
+            }
+        }
+    }
+}
diff --git a/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs b/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
index 1444009..13f83a2 100644
--- a/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
+++ b/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
@@ -1,583 +1,583 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.IO;

-using Google.Protobuf.TestProtos;

-using Google.Protobuf.Buffers;

-using NUnit.Framework;

-using System.Text;

-

-namespace Google.Protobuf

-{

-    public class CodedOutputStreamTest

-    {

-        /// <summary>

-        /// Writes the given value using WriteRawVarint32() and WriteRawVarint64() and

-        /// checks that the result matches the given bytes

-        /// </summary>

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

-        {

-            // Only do 32-bit write if the value fits in 32 bits.

-            if ((value >> 32) == 0)

-            {

-                // CodedOutputStream

-                MemoryStream rawOutput = new MemoryStream();

-                CodedOutputStream output = new CodedOutputStream(rawOutput);

-                output.WriteRawVarint32((uint) value);

-                output.Flush();

-                Assert.AreEqual(data, rawOutput.ToArray());

-

-                // IBufferWriter

-                var bufferWriter = new TestArrayBufferWriter<byte>();

-                WriteContext.Initialize(bufferWriter, out WriteContext ctx);

-                ctx.WriteUInt32((uint) value);

-                ctx.Flush();

-                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());

-

-                // Also try computing size.

-                Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint32Size((uint) value));

-            }

-

-            {

-                // CodedOutputStream

-                MemoryStream rawOutput = new MemoryStream();

-                CodedOutputStream output = new CodedOutputStream(rawOutput);

-                output.WriteRawVarint64(value);

-                output.Flush();

-                Assert.AreEqual(data, rawOutput.ToArray());

-

-                // IBufferWriter

-                var bufferWriter = new TestArrayBufferWriter<byte>();

-                WriteContext.Initialize(bufferWriter, out WriteContext ctx);

-                ctx.WriteUInt64(value);

-                ctx.Flush();

-                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());

-

-                // Also try computing size.

-                Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint64Size(value));

-            }

-

-            // Try different buffer sizes.

-            for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)

-            {

-                // Only do 32-bit write if the value fits in 32 bits.

-                if ((value >> 32) == 0)

-                {

-                    MemoryStream rawOutput = new MemoryStream();

-                    CodedOutputStream output =

-                        new CodedOutputStream(rawOutput, bufferSize);

-                    output.WriteRawVarint32((uint) value);

-                    output.Flush();

-                    Assert.AreEqual(data, rawOutput.ToArray());

-

-                    var bufferWriter = new TestArrayBufferWriter<byte>();

-                    bufferWriter.MaxGrowBy = bufferSize;

-                    WriteContext.Initialize(bufferWriter, out WriteContext ctx);

-                    ctx.WriteUInt32((uint) value);

-                    ctx.Flush();

-                    Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());

-                }

-

-                {

-                    MemoryStream rawOutput = new MemoryStream();

-                    CodedOutputStream output = new CodedOutputStream(rawOutput, bufferSize);

-                    output.WriteRawVarint64(value);

-                    output.Flush();

-                    Assert.AreEqual(data, rawOutput.ToArray());

-

-                    var bufferWriter = new TestArrayBufferWriter<byte>();

-                    bufferWriter.MaxGrowBy = bufferSize;

-                    WriteContext.Initialize(bufferWriter, out WriteContext ctx);

-                    ctx.WriteUInt64(value);

-                    ctx.Flush();

-                    Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());

-                }

-

-            }

-        }

-

-        /// <summary>

-        /// Tests WriteRawVarint32() and WriteRawVarint64()

-        /// </summary>

-        [Test]

-        public void WriteVarint()

-        {

-            AssertWriteVarint(new byte[] {0x00}, 0);

-            AssertWriteVarint(new byte[] {0x01}, 1);

-            AssertWriteVarint(new byte[] {0x7f}, 127);

-            // 14882

-            AssertWriteVarint(new byte[] {0xa2, 0x74}, (0x22 << 0) | (0x74 << 7));

-            // 2961488830

-            AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x0b},

-                              (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |

-                              (0x0bL << 28));

-

-            // 64-bit

-            // 7256456126

-            AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x1b},

-                              (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |

-                              (0x1bL << 28));

-            // 41256202580718336

-            AssertWriteVarint(

-                new byte[] {0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49},

-                (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |

-                (0x43UL << 28) | (0x49L << 35) | (0x24UL << 42) | (0x49UL << 49));

-            // 11964378330978735131

-            AssertWriteVarint(

-                new byte[] {0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01},

-                unchecked((ulong)

-                          ((0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |

-                           (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |

-                           (0x05L << 49) | (0x26L << 56) | (0x01L << 63))));

-        }

-

-        /// <summary>

-        /// Parses the given bytes using WriteRawLittleEndian32() and checks

-        /// that the result matches the given value.

-        /// </summary>

-        private static void AssertWriteLittleEndian32(byte[] data, uint value)

-        {

-            {

-                var rawOutput = new MemoryStream();

-                var output = new CodedOutputStream(rawOutput);

-                output.WriteRawLittleEndian32(value);

-                output.Flush();

-                Assert.AreEqual(data, rawOutput.ToArray());

-

-                var bufferWriter = new TestArrayBufferWriter<byte>();

-                WriteContext.Initialize(bufferWriter, out WriteContext ctx);

-                ctx.WriteFixed32(value);

-                ctx.Flush();

-                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());

-            }

-

-            // Try different buffer sizes.

-            for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)

-            {

-                var rawOutput = new MemoryStream();

-                var output = new CodedOutputStream(rawOutput, bufferSize);

-                output.WriteRawLittleEndian32(value);

-                output.Flush();

-                Assert.AreEqual(data, rawOutput.ToArray());

-

-                var bufferWriter = new TestArrayBufferWriter<byte>();

-                bufferWriter.MaxGrowBy = bufferSize;

-                WriteContext.Initialize(bufferWriter, out WriteContext ctx);

-                ctx.WriteFixed32(value);

-                ctx.Flush();

-                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());

-            }

-        }

-

-        /// <summary>

-        /// Parses the given bytes using WriteRawLittleEndian64() and checks

-        /// that the result matches the given value.

-        /// </summary>

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

-        {

-            {

-                var rawOutput = new MemoryStream();

-                var output = new CodedOutputStream(rawOutput);

-                output.WriteRawLittleEndian64(value);

-                output.Flush();

-                Assert.AreEqual(data, rawOutput.ToArray());

-

-                var bufferWriter = new TestArrayBufferWriter<byte>();

-                WriteContext.Initialize(bufferWriter, out WriteContext ctx);

-                ctx.WriteFixed64(value);

-                ctx.Flush();

-                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());

-            }

-

-            // Try different block sizes.

-            for (int blockSize = 1; blockSize <= 16; blockSize *= 2)

-            {

-                var rawOutput = new MemoryStream();

-                var output = new CodedOutputStream(rawOutput, blockSize);

-                output.WriteRawLittleEndian64(value);

-                output.Flush();

-                Assert.AreEqual(data, rawOutput.ToArray());

-

-                var bufferWriter = new TestArrayBufferWriter<byte>();

-                bufferWriter.MaxGrowBy = blockSize;

-                WriteContext.Initialize(bufferWriter, out WriteContext ctx);

-                ctx.WriteFixed64(value);

-                ctx.Flush();

-                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());

-            }

-        }

-

-        /// <summary>

-        /// Tests writeRawLittleEndian32() and writeRawLittleEndian64().

-        /// </summary>

-        [Test]

-        public void WriteLittleEndian()

-        {

-            AssertWriteLittleEndian32(new byte[] {0x78, 0x56, 0x34, 0x12}, 0x12345678);

-            AssertWriteLittleEndian32(new byte[] {0xf0, 0xde, 0xbc, 0x9a}, 0x9abcdef0);

-

-            AssertWriteLittleEndian64(

-                new byte[] {0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12},

-                0x123456789abcdef0L);

-            AssertWriteLittleEndian64(

-                new byte[] {0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a},

-                0x9abcdef012345678UL);

-        }

-

-        [Test]

-        public void WriteWholeMessage_VaryingBlockSizes()

-        {

-            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();

-

-            byte[] rawBytes = message.ToByteArray();

-

-            // Try different block sizes.

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

-            {

-                MemoryStream rawOutput = new MemoryStream();

-                CodedOutputStream output = new CodedOutputStream(rawOutput, blockSize);

-                message.WriteTo(output);

-                output.Flush();

-                Assert.AreEqual(rawBytes, rawOutput.ToArray());

-

-                var bufferWriter = new TestArrayBufferWriter<byte>();

-                bufferWriter.MaxGrowBy = blockSize;

-                message.WriteTo(bufferWriter);

-                Assert.AreEqual(rawBytes, bufferWriter.WrittenSpan.ToArray()); 

-            }

-        }

-

-        [Test]

-        public void WriteContext_WritesWithFlushes()

-        {

-            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();

-

-            MemoryStream expectedOutput = new MemoryStream();

-            CodedOutputStream output = new CodedOutputStream(expectedOutput);

-            output.WriteMessage(message);

-            output.Flush();

-            byte[] expectedBytes1 = expectedOutput.ToArray();

-

-            output.WriteMessage(message);

-            output.Flush();

-            byte[] expectedBytes2 = expectedOutput.ToArray();

-

-            var bufferWriter = new TestArrayBufferWriter<byte>();

-            WriteContext.Initialize(bufferWriter, out WriteContext ctx);

-            ctx.WriteMessage(message);

-            ctx.Flush();

-            Assert.AreEqual(expectedBytes1, bufferWriter.WrittenSpan.ToArray());

-

-            ctx.WriteMessage(message);

-            ctx.Flush();

-            Assert.AreEqual(expectedBytes2, bufferWriter.WrittenSpan.ToArray());

-        }

-        

-        [Test]

-        public void EncodeZigZag32()

-        {

-            Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag32(0));

-            Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag32(-1));

-            Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag32(1));

-            Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag32(-2));

-            Assert.AreEqual(0x7FFFFFFEu, WritingPrimitives.EncodeZigZag32(0x3FFFFFFF));

-            Assert.AreEqual(0x7FFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0xC0000000)));

-            Assert.AreEqual(0xFFFFFFFEu, WritingPrimitives.EncodeZigZag32(0x7FFFFFFF));

-            Assert.AreEqual(0xFFFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0x80000000)));

-        }

-

-        [Test]

-        public void EncodeZigZag64()

-        {

-            Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag64(0));

-            Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag64(-1));

-            Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag64(1));

-            Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag64(-2));

-            Assert.AreEqual(0x000000007FFFFFFEuL,

-                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL)));

-            Assert.AreEqual(0x000000007FFFFFFFuL,

-                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL)));

-            Assert.AreEqual(0x00000000FFFFFFFEuL,

-                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL)));

-            Assert.AreEqual(0x00000000FFFFFFFFuL,

-                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL)));

-            Assert.AreEqual(0xFFFFFFFFFFFFFFFEL,

-                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL)));

-            Assert.AreEqual(0xFFFFFFFFFFFFFFFFL,

-                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x8000000000000000UL)));

-        }

-

-        [Test]

-        public void RoundTripZigZag32()

-        {

-            // Some easier-to-verify round-trip tests.  The inputs (other than 0, 1, -1)

-            // were chosen semi-randomly via keyboard bashing.

-            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(0)));

-            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(1)));

-            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-1)));

-            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(14927)));

-            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-3612)));

-        }

-

-        [Test]

-        public void RoundTripZigZag64()

-        {

-            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(0)));

-            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(1)));

-            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-1)));

-            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(14927)));

-            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-3612)));

-

-            Assert.AreEqual(856912304801416L,

-                            ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(856912304801416L)));

-            Assert.AreEqual(-75123905439571256L,

-                            ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-75123905439571256L)));

-        }

-

-        [Test]

-        public void TestNegativeEnumNoTag()

-        {

-            Assert.AreEqual(10, CodedOutputStream.ComputeInt32Size(-2));

-            Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) SampleEnum.NegativeValue));

-

-            byte[] bytes = new byte[10];

-            CodedOutputStream output = new CodedOutputStream(bytes);

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

-        }

-

-        [Test]

-        public void TestCodedInputOutputPosition()

-        {

-            byte[] content = new byte[110];

-            for (int i = 0; i < content.Length; i++)

-                content[i] = (byte)i;

-

-            byte[] child = new byte[120];

-            {

-                MemoryStream ms = new MemoryStream(child);

-                CodedOutputStream cout = new CodedOutputStream(ms, 20);

-                // Field 11: numeric value: 500

-                cout.WriteTag(11, WireFormat.WireType.Varint);

-                Assert.AreEqual(1, cout.Position);

-                cout.WriteInt32(500);

-                Assert.AreEqual(3, cout.Position);

-                //Field 12: length delimited 120 bytes

-                cout.WriteTag(12, WireFormat.WireType.LengthDelimited);

-                Assert.AreEqual(4, cout.Position);

-                cout.WriteBytes(ByteString.CopyFrom(content));

-                Assert.AreEqual(115, cout.Position);

-                // Field 13: fixed numeric value: 501

-                cout.WriteTag(13, WireFormat.WireType.Fixed32);

-                Assert.AreEqual(116, cout.Position);

-                cout.WriteSFixed32(501);

-                Assert.AreEqual(120, cout.Position);

-                cout.Flush();

-            }

-

-            byte[] bytes = new byte[130];

-            {

-                CodedOutputStream cout = new CodedOutputStream(bytes);

-                // Field 1: numeric value: 500

-                cout.WriteTag(1, WireFormat.WireType.Varint);

-                Assert.AreEqual(1, cout.Position);

-                cout.WriteInt32(500);

-                Assert.AreEqual(3, cout.Position);

-                //Field 2: length delimited 120 bytes

-                cout.WriteTag(2, WireFormat.WireType.LengthDelimited);

-                Assert.AreEqual(4, cout.Position);

-                cout.WriteBytes(ByteString.CopyFrom(child));

-                Assert.AreEqual(125, cout.Position);

-                // Field 3: fixed numeric value: 500

-                cout.WriteTag(3, WireFormat.WireType.Fixed32);

-                Assert.AreEqual(126, cout.Position);

-                cout.WriteSFixed32(501);

-                Assert.AreEqual(130, cout.Position);

-                cout.Flush();

-            }

-            // Now test Input stream:

-            {

-                CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50], 0, 0, false);

-                Assert.AreEqual(0, cin.Position);

-                // Field 1:

-                uint tag = cin.ReadTag();

-                Assert.AreEqual(1, tag >> 3);

-                Assert.AreEqual(1, cin.Position);

-                Assert.AreEqual(500, cin.ReadInt32());

-                Assert.AreEqual(3, cin.Position);

-                //Field 2:

-                tag = cin.ReadTag();

-                Assert.AreEqual(2, tag >> 3);

-                Assert.AreEqual(4, cin.Position);

-                int childlen = cin.ReadLength();

-                Assert.AreEqual(120, childlen);

-                Assert.AreEqual(5, cin.Position);

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

-                Assert.AreEqual(5, cin.Position);

-                // Now we are reading child message

-                {

-                    // Field 11: numeric value: 500

-                    tag = cin.ReadTag();

-                    Assert.AreEqual(11, tag >> 3);

-                    Assert.AreEqual(6, cin.Position);

-                    Assert.AreEqual(500, cin.ReadInt32());

-                    Assert.AreEqual(8, cin.Position);

-                    //Field 12: length delimited 120 bytes

-                    tag = cin.ReadTag();

-                    Assert.AreEqual(12, tag >> 3);

-                    Assert.AreEqual(9, cin.Position);

-                    ByteString bstr = cin.ReadBytes();

-                    Assert.AreEqual(110, bstr.Length);

-                    Assert.AreEqual((byte) 109, bstr[109]);

-                    Assert.AreEqual(120, cin.Position);

-                    // Field 13: fixed numeric value: 501

-                    tag = cin.ReadTag();

-                    Assert.AreEqual(13, tag >> 3);

-                    // ROK - Previously broken here, this returned 126 failing to account for bufferSizeAfterLimit

-                    Assert.AreEqual(121, cin.Position);

-                    Assert.AreEqual(501, cin.ReadSFixed32());

-                    Assert.AreEqual(125, cin.Position);

-                    Assert.IsTrue(cin.IsAtEnd);

-                }

-                cin.PopLimit(oldlimit);

-                Assert.AreEqual(125, cin.Position);

-                // Field 3: fixed numeric value: 501

-                tag = cin.ReadTag();

-                Assert.AreEqual(3, tag >> 3);

-                Assert.AreEqual(126, cin.Position);

-                Assert.AreEqual(501, cin.ReadSFixed32());

-                Assert.AreEqual(130, cin.Position);

-                Assert.IsTrue(cin.IsAtEnd);

-            }

-        }

-

-        [Test]

-        public void Dispose_DisposesUnderlyingStream()

-        {

-            var memoryStream = new MemoryStream();

-            Assert.IsTrue(memoryStream.CanWrite);

-            using (var cos = new CodedOutputStream(memoryStream))

-            {

-                cos.WriteRawBytes(new byte[] {0});

-                Assert.AreEqual(0, memoryStream.Position); // Not flushed yet

-            }

-            Assert.AreEqual(1, memoryStream.ToArray().Length); // Flushed data from CodedOutputStream to MemoryStream

-            Assert.IsFalse(memoryStream.CanWrite); // Disposed

-        }

-

-        [Test]

-        public void Dispose_WithLeaveOpen()

-        {

-            var memoryStream = new MemoryStream();

-            Assert.IsTrue(memoryStream.CanWrite);

-            using (var cos = new CodedOutputStream(memoryStream, true))

-            {

-                cos.WriteRawBytes(new byte[] {0});

-                Assert.AreEqual(0, memoryStream.Position); // Not flushed yet

-            }

-            Assert.AreEqual(1, memoryStream.Position); // Flushed data from CodedOutputStream to MemoryStream

-            Assert.IsTrue(memoryStream.CanWrite); // We left the stream open

-        }

-

-        [Test]

-        public void Dispose_FromByteArray()

-        {

-            var stream = new CodedOutputStream(new byte[10]);

-            stream.Dispose();

-        }

-

-        [Test]

-        public void WriteString_AsciiSmall_MaxUtf8SizeExceedsBuffer()

-        {

-            var buffer = new byte[5];

-            var output = new CodedOutputStream(buffer);

-            output.WriteString("ABC");

-

-            output.Flush();

-

-            // Verify written content

-            var input = new CodedInputStream(buffer);

-            Assert.AreEqual("ABC", input.ReadString());

-        }

-

-        [Test]

-        public void WriteStringsOfDifferentSizes_Ascii()

-        {

-            for (int i = 1; i <= 1024; i++)

-            {

-                var buffer = new byte[4096];

-                var output = new CodedOutputStream(buffer);

-                var sb = new StringBuilder();

-                for (int j = 0; j < i; j++)

-                {

-                    sb.Append((j % 10).ToString()); // incrementing numbers, repeating

-                }

-                var s = sb.ToString();

-                output.WriteString(s);

-

-                output.Flush();

-

-                // Verify written content

-                var input = new CodedInputStream(buffer);

-                Assert.AreEqual(s, input.ReadString());

-            }

-        }

-

-        [Test]

-        public void WriteStringsOfDifferentSizes_Unicode()

-        {

-            for (int i = 1; i <= 1024; i++)

-            {

-                var buffer = new byte[4096];

-                var output = new CodedOutputStream(buffer);

-                var sb = new StringBuilder();

-                for (int j = 0; j < i; j++)

-                {

-                    char c = (char)((j % 10) + 10112);

-                    sb.Append(c.ToString()); // incrementing unicode numbers, repeating

-                }

-                var s = sb.ToString();

-                output.WriteString(s);

-

-                output.Flush();

-

-                // Verify written content

-                var input = new CodedInputStream(buffer);

-

-                Assert.AreEqual(s, input.ReadString());

-            }

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.IO;
+using Google.Protobuf.TestProtos;
+using Google.Protobuf.Buffers;
+using NUnit.Framework;
+using System.Text;
+
+namespace Google.Protobuf
+{
+    public class CodedOutputStreamTest
+    {
+        /// <summary>
+        /// Writes the given value using WriteRawVarint32() and WriteRawVarint64() and
+        /// checks that the result matches the given bytes
+        /// </summary>
+        private static void AssertWriteVarint(byte[] data, ulong value)
+        {
+            // Only do 32-bit write if the value fits in 32 bits.
+            if ((value >> 32) == 0)
+            {
+                // CodedOutputStream
+                MemoryStream rawOutput = new MemoryStream();
+                CodedOutputStream output = new CodedOutputStream(rawOutput);
+                output.WriteRawVarint32((uint) value);
+                output.Flush();
+                Assert.AreEqual(data, rawOutput.ToArray());
+
+                // IBufferWriter
+                var bufferWriter = new TestArrayBufferWriter<byte>();
+                WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                ctx.WriteUInt32((uint) value);
+                ctx.Flush();
+                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+
+                // Also try computing size.
+                Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint32Size((uint) value));
+            }
+
+            {
+                // CodedOutputStream
+                MemoryStream rawOutput = new MemoryStream();
+                CodedOutputStream output = new CodedOutputStream(rawOutput);
+                output.WriteRawVarint64(value);
+                output.Flush();
+                Assert.AreEqual(data, rawOutput.ToArray());
+
+                // IBufferWriter
+                var bufferWriter = new TestArrayBufferWriter<byte>();
+                WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                ctx.WriteUInt64(value);
+                ctx.Flush();
+                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+
+                // Also try computing size.
+                Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint64Size(value));
+            }
+
+            // Try different buffer sizes.
+            for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
+            {
+                // Only do 32-bit write if the value fits in 32 bits.
+                if ((value >> 32) == 0)
+                {
+                    MemoryStream rawOutput = new MemoryStream();
+                    CodedOutputStream output =
+                        new CodedOutputStream(rawOutput, bufferSize);
+                    output.WriteRawVarint32((uint) value);
+                    output.Flush();
+                    Assert.AreEqual(data, rawOutput.ToArray());
+
+                    var bufferWriter = new TestArrayBufferWriter<byte>();
+                    bufferWriter.MaxGrowBy = bufferSize;
+                    WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                    ctx.WriteUInt32((uint) value);
+                    ctx.Flush();
+                    Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+                }
+
+                {
+                    MemoryStream rawOutput = new MemoryStream();
+                    CodedOutputStream output = new CodedOutputStream(rawOutput, bufferSize);
+                    output.WriteRawVarint64(value);
+                    output.Flush();
+                    Assert.AreEqual(data, rawOutput.ToArray());
+
+                    var bufferWriter = new TestArrayBufferWriter<byte>();
+                    bufferWriter.MaxGrowBy = bufferSize;
+                    WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                    ctx.WriteUInt64(value);
+                    ctx.Flush();
+                    Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+                }
+
+            }
+        }
+
+        /// <summary>
+        /// Tests WriteRawVarint32() and WriteRawVarint64()
+        /// </summary>
+        [Test]
+        public void WriteVarint()
+        {
+            AssertWriteVarint(new byte[] {0x00}, 0);
+            AssertWriteVarint(new byte[] {0x01}, 1);
+            AssertWriteVarint(new byte[] {0x7f}, 127);
+            // 14882
+            AssertWriteVarint(new byte[] {0xa2, 0x74}, (0x22 << 0) | (0x74 << 7));
+            // 2961488830
+            AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x0b},
+                              (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+                              (0x0bL << 28));
+
+            // 64-bit
+            // 7256456126
+            AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x1b},
+                              (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+                              (0x1bL << 28));
+            // 41256202580718336
+            AssertWriteVarint(
+                new byte[] {0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49},
+                (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
+                (0x43UL << 28) | (0x49L << 35) | (0x24UL << 42) | (0x49UL << 49));
+            // 11964378330978735131
+            AssertWriteVarint(
+                new byte[] {0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01},
+                unchecked((ulong)
+                          ((0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
+                           (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
+                           (0x05L << 49) | (0x26L << 56) | (0x01L << 63))));
+        }
+
+        /// <summary>
+        /// Parses the given bytes using WriteRawLittleEndian32() and checks
+        /// that the result matches the given value.
+        /// </summary>
+        private static void AssertWriteLittleEndian32(byte[] data, uint value)
+        {
+            {
+                var rawOutput = new MemoryStream();
+                var output = new CodedOutputStream(rawOutput);
+                output.WriteRawLittleEndian32(value);
+                output.Flush();
+                Assert.AreEqual(data, rawOutput.ToArray());
+
+                var bufferWriter = new TestArrayBufferWriter<byte>();
+                WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                ctx.WriteFixed32(value);
+                ctx.Flush();
+                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+            }
+
+            // Try different buffer sizes.
+            for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
+            {
+                var rawOutput = new MemoryStream();
+                var output = new CodedOutputStream(rawOutput, bufferSize);
+                output.WriteRawLittleEndian32(value);
+                output.Flush();
+                Assert.AreEqual(data, rawOutput.ToArray());
+
+                var bufferWriter = new TestArrayBufferWriter<byte>();
+                bufferWriter.MaxGrowBy = bufferSize;
+                WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                ctx.WriteFixed32(value);
+                ctx.Flush();
+                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+            }
+        }
+
+        /// <summary>
+        /// Parses the given bytes using WriteRawLittleEndian64() and checks
+        /// that the result matches the given value.
+        /// </summary>
+        private static void AssertWriteLittleEndian64(byte[] data, ulong value)
+        {
+            {
+                var rawOutput = new MemoryStream();
+                var output = new CodedOutputStream(rawOutput);
+                output.WriteRawLittleEndian64(value);
+                output.Flush();
+                Assert.AreEqual(data, rawOutput.ToArray());
+
+                var bufferWriter = new TestArrayBufferWriter<byte>();
+                WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                ctx.WriteFixed64(value);
+                ctx.Flush();
+                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+            }
+
+            // Try different block sizes.
+            for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
+            {
+                var rawOutput = new MemoryStream();
+                var output = new CodedOutputStream(rawOutput, blockSize);
+                output.WriteRawLittleEndian64(value);
+                output.Flush();
+                Assert.AreEqual(data, rawOutput.ToArray());
+
+                var bufferWriter = new TestArrayBufferWriter<byte>();
+                bufferWriter.MaxGrowBy = blockSize;
+                WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+                ctx.WriteFixed64(value);
+                ctx.Flush();
+                Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+            }
+        }
+
+        /// <summary>
+        /// Tests writeRawLittleEndian32() and writeRawLittleEndian64().
+        /// </summary>
+        [Test]
+        public void WriteLittleEndian()
+        {
+            AssertWriteLittleEndian32(new byte[] {0x78, 0x56, 0x34, 0x12}, 0x12345678);
+            AssertWriteLittleEndian32(new byte[] {0xf0, 0xde, 0xbc, 0x9a}, 0x9abcdef0);
+
+            AssertWriteLittleEndian64(
+                new byte[] {0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12},
+                0x123456789abcdef0L);
+            AssertWriteLittleEndian64(
+                new byte[] {0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a},
+                0x9abcdef012345678UL);
+        }
+
+        [Test]
+        public void WriteWholeMessage_VaryingBlockSizes()
+        {
+            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+            byte[] rawBytes = message.ToByteArray();
+
+            // Try different block sizes.
+            for (int blockSize = 1; blockSize < 256; blockSize *= 2)
+            {
+                MemoryStream rawOutput = new MemoryStream();
+                CodedOutputStream output = new CodedOutputStream(rawOutput, blockSize);
+                message.WriteTo(output);
+                output.Flush();
+                Assert.AreEqual(rawBytes, rawOutput.ToArray());
+
+                var bufferWriter = new TestArrayBufferWriter<byte>();
+                bufferWriter.MaxGrowBy = blockSize;
+                message.WriteTo(bufferWriter);
+                Assert.AreEqual(rawBytes, bufferWriter.WrittenSpan.ToArray()); 
+            }
+        }
+
+        [Test]
+        public void WriteContext_WritesWithFlushes()
+        {
+            TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+            MemoryStream expectedOutput = new MemoryStream();
+            CodedOutputStream output = new CodedOutputStream(expectedOutput);
+            output.WriteMessage(message);
+            output.Flush();
+            byte[] expectedBytes1 = expectedOutput.ToArray();
+
+            output.WriteMessage(message);
+            output.Flush();
+            byte[] expectedBytes2 = expectedOutput.ToArray();
+
+            var bufferWriter = new TestArrayBufferWriter<byte>();
+            WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+            ctx.WriteMessage(message);
+            ctx.Flush();
+            Assert.AreEqual(expectedBytes1, bufferWriter.WrittenSpan.ToArray());
+
+            ctx.WriteMessage(message);
+            ctx.Flush();
+            Assert.AreEqual(expectedBytes2, bufferWriter.WrittenSpan.ToArray());
+        }
+        
+        [Test]
+        public void EncodeZigZag32()
+        {
+            Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag32(0));
+            Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag32(-1));
+            Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag32(1));
+            Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag32(-2));
+            Assert.AreEqual(0x7FFFFFFEu, WritingPrimitives.EncodeZigZag32(0x3FFFFFFF));
+            Assert.AreEqual(0x7FFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0xC0000000)));
+            Assert.AreEqual(0xFFFFFFFEu, WritingPrimitives.EncodeZigZag32(0x7FFFFFFF));
+            Assert.AreEqual(0xFFFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0x80000000)));
+        }
+
+        [Test]
+        public void EncodeZigZag64()
+        {
+            Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag64(0));
+            Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag64(-1));
+            Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag64(1));
+            Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag64(-2));
+            Assert.AreEqual(0x000000007FFFFFFEuL,
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL)));
+            Assert.AreEqual(0x000000007FFFFFFFuL,
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL)));
+            Assert.AreEqual(0x00000000FFFFFFFEuL,
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL)));
+            Assert.AreEqual(0x00000000FFFFFFFFuL,
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL)));
+            Assert.AreEqual(0xFFFFFFFFFFFFFFFEL,
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL)));
+            Assert.AreEqual(0xFFFFFFFFFFFFFFFFL,
+                            WritingPrimitives.EncodeZigZag64(unchecked((long) 0x8000000000000000UL)));
+        }
+
+        [Test]
+        public void RoundTripZigZag32()
+        {
+            // Some easier-to-verify round-trip tests.  The inputs (other than 0, 1, -1)
+            // were chosen semi-randomly via keyboard bashing.
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(0)));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(1)));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-1)));
+            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(14927)));
+            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-3612)));
+        }
+
+        [Test]
+        public void RoundTripZigZag64()
+        {
+            Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(0)));
+            Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(1)));
+            Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-1)));
+            Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(14927)));
+            Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-3612)));
+
+            Assert.AreEqual(856912304801416L,
+                            ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(856912304801416L)));
+            Assert.AreEqual(-75123905439571256L,
+                            ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-75123905439571256L)));
+        }
+
+        [Test]
+        public void TestNegativeEnumNoTag()
+        {
+            Assert.AreEqual(10, CodedOutputStream.ComputeInt32Size(-2));
+            Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) SampleEnum.NegativeValue));
+
+            byte[] bytes = new byte[10];
+            CodedOutputStream output = new CodedOutputStream(bytes);
+            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));
+        }
+
+        [Test]
+        public void TestCodedInputOutputPosition()
+        {
+            byte[] content = new byte[110];
+            for (int i = 0; i < content.Length; i++)
+                content[i] = (byte)i;
+
+            byte[] child = new byte[120];
+            {
+                MemoryStream ms = new MemoryStream(child);
+                CodedOutputStream cout = new CodedOutputStream(ms, 20);
+                // Field 11: numeric value: 500
+                cout.WriteTag(11, WireFormat.WireType.Varint);
+                Assert.AreEqual(1, cout.Position);
+                cout.WriteInt32(500);
+                Assert.AreEqual(3, cout.Position);
+                //Field 12: length delimited 120 bytes
+                cout.WriteTag(12, WireFormat.WireType.LengthDelimited);
+                Assert.AreEqual(4, cout.Position);
+                cout.WriteBytes(ByteString.CopyFrom(content));
+                Assert.AreEqual(115, cout.Position);
+                // Field 13: fixed numeric value: 501
+                cout.WriteTag(13, WireFormat.WireType.Fixed32);
+                Assert.AreEqual(116, cout.Position);
+                cout.WriteSFixed32(501);
+                Assert.AreEqual(120, cout.Position);
+                cout.Flush();
+            }
+
+            byte[] bytes = new byte[130];
+            {
+                CodedOutputStream cout = new CodedOutputStream(bytes);
+                // Field 1: numeric value: 500
+                cout.WriteTag(1, WireFormat.WireType.Varint);
+                Assert.AreEqual(1, cout.Position);
+                cout.WriteInt32(500);
+                Assert.AreEqual(3, cout.Position);
+                //Field 2: length delimited 120 bytes
+                cout.WriteTag(2, WireFormat.WireType.LengthDelimited);
+                Assert.AreEqual(4, cout.Position);
+                cout.WriteBytes(ByteString.CopyFrom(child));
+                Assert.AreEqual(125, cout.Position);
+                // Field 3: fixed numeric value: 500
+                cout.WriteTag(3, WireFormat.WireType.Fixed32);
+                Assert.AreEqual(126, cout.Position);
+                cout.WriteSFixed32(501);
+                Assert.AreEqual(130, cout.Position);
+                cout.Flush();
+            }
+            // Now test Input stream:
+            {
+                CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50], 0, 0, false);
+                Assert.AreEqual(0, cin.Position);
+                // Field 1:
+                uint tag = cin.ReadTag();
+                Assert.AreEqual(1, tag >> 3);
+                Assert.AreEqual(1, cin.Position);
+                Assert.AreEqual(500, cin.ReadInt32());
+                Assert.AreEqual(3, cin.Position);
+                //Field 2:
+                tag = cin.ReadTag();
+                Assert.AreEqual(2, tag >> 3);
+                Assert.AreEqual(4, cin.Position);
+                int childlen = cin.ReadLength();
+                Assert.AreEqual(120, childlen);
+                Assert.AreEqual(5, cin.Position);
+                int oldlimit = cin.PushLimit((int)childlen);
+                Assert.AreEqual(5, cin.Position);
+                // Now we are reading child message
+                {
+                    // Field 11: numeric value: 500
+                    tag = cin.ReadTag();
+                    Assert.AreEqual(11, tag >> 3);
+                    Assert.AreEqual(6, cin.Position);
+                    Assert.AreEqual(500, cin.ReadInt32());
+                    Assert.AreEqual(8, cin.Position);
+                    //Field 12: length delimited 120 bytes
+                    tag = cin.ReadTag();
+                    Assert.AreEqual(12, tag >> 3);
+                    Assert.AreEqual(9, cin.Position);
+                    ByteString bstr = cin.ReadBytes();
+                    Assert.AreEqual(110, bstr.Length);
+                    Assert.AreEqual((byte) 109, bstr[109]);
+                    Assert.AreEqual(120, cin.Position);
+                    // Field 13: fixed numeric value: 501
+                    tag = cin.ReadTag();
+                    Assert.AreEqual(13, tag >> 3);
+                    // ROK - Previously broken here, this returned 126 failing to account for bufferSizeAfterLimit
+                    Assert.AreEqual(121, cin.Position);
+                    Assert.AreEqual(501, cin.ReadSFixed32());
+                    Assert.AreEqual(125, cin.Position);
+                    Assert.IsTrue(cin.IsAtEnd);
+                }
+                cin.PopLimit(oldlimit);
+                Assert.AreEqual(125, cin.Position);
+                // Field 3: fixed numeric value: 501
+                tag = cin.ReadTag();
+                Assert.AreEqual(3, tag >> 3);
+                Assert.AreEqual(126, cin.Position);
+                Assert.AreEqual(501, cin.ReadSFixed32());
+                Assert.AreEqual(130, cin.Position);
+                Assert.IsTrue(cin.IsAtEnd);
+            }
+        }
+
+        [Test]
+        public void Dispose_DisposesUnderlyingStream()
+        {
+            var memoryStream = new MemoryStream();
+            Assert.IsTrue(memoryStream.CanWrite);
+            using (var cos = new CodedOutputStream(memoryStream))
+            {
+                cos.WriteRawBytes(new byte[] {0});
+                Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
+            }
+            Assert.AreEqual(1, memoryStream.ToArray().Length); // Flushed data from CodedOutputStream to MemoryStream
+            Assert.IsFalse(memoryStream.CanWrite); // Disposed
+        }
+
+        [Test]
+        public void Dispose_WithLeaveOpen()
+        {
+            var memoryStream = new MemoryStream();
+            Assert.IsTrue(memoryStream.CanWrite);
+            using (var cos = new CodedOutputStream(memoryStream, true))
+            {
+                cos.WriteRawBytes(new byte[] {0});
+                Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
+            }
+            Assert.AreEqual(1, memoryStream.Position); // Flushed data from CodedOutputStream to MemoryStream
+            Assert.IsTrue(memoryStream.CanWrite); // We left the stream open
+        }
+
+        [Test]
+        public void Dispose_FromByteArray()
+        {
+            var stream = new CodedOutputStream(new byte[10]);
+            stream.Dispose();
+        }
+
+        [Test]
+        public void WriteString_AsciiSmall_MaxUtf8SizeExceedsBuffer()
+        {
+            var buffer = new byte[5];
+            var output = new CodedOutputStream(buffer);
+            output.WriteString("ABC");
+
+            output.Flush();
+
+            // Verify written content
+            var input = new CodedInputStream(buffer);
+            Assert.AreEqual("ABC", input.ReadString());
+        }
+
+        [Test]
+        public void WriteStringsOfDifferentSizes_Ascii()
+        {
+            for (int i = 1; i <= 1024; i++)
+            {
+                var buffer = new byte[4096];
+                var output = new CodedOutputStream(buffer);
+                var sb = new StringBuilder();
+                for (int j = 0; j < i; j++)
+                {
+                    sb.Append((j % 10).ToString()); // incrementing numbers, repeating
+                }
+                var s = sb.ToString();
+                output.WriteString(s);
+
+                output.Flush();
+
+                // Verify written content
+                var input = new CodedInputStream(buffer);
+                Assert.AreEqual(s, input.ReadString());
+            }
+        }
+
+        [Test]
+        public void WriteStringsOfDifferentSizes_Unicode()
+        {
+            for (int i = 1; i <= 1024; i++)
+            {
+                var buffer = new byte[4096];
+                var output = new CodedOutputStream(buffer);
+                var sb = new StringBuilder();
+                for (int j = 0; j < i; j++)
+                {
+                    char c = (char)((j % 10) + 10112);
+                    sb.Append(c.ToString()); // incrementing unicode numbers, repeating
+                }
+                var s = sb.ToString();
+                output.WriteString(s);
+
+                output.Flush();
+
+                // Verify written content
+                var input = new CodedInputStream(buffer);
+
+                Assert.AreEqual(s, input.ReadString());
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf.Test/DeprecatedMemberTest.cs b/csharp/src/Google.Protobuf.Test/DeprecatedMemberTest.cs
index 34d5b9f..fd041e0 100644
--- a/csharp/src/Google.Protobuf.Test/DeprecatedMemberTest.cs
+++ b/csharp/src/Google.Protobuf.Test/DeprecatedMemberTest.cs
@@ -1,55 +1,55 @@
-#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.Reflection;

-using Google.Protobuf.TestProtos;

-using NUnit.Framework;

-

-namespace Google.Protobuf

-{

-    public class DeprecatedMemberTest

-    {

-        private static void AssertIsDeprecated(MemberInfo member)

-        {

-            Assert.NotNull(member);

-            Assert.IsTrue(member.IsDefined(typeof(ObsoleteAttribute), false), "Member not obsolete: " + member);

-        }

-

-        [Test]

-        public void TestDepreatedPrimitiveValue()

-        {

-            AssertIsDeprecated(typeof(TestDeprecatedFields).GetProperty("DeprecatedInt32"));

-        }

-

-    }

-}

+#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.Reflection;
+using Google.Protobuf.TestProtos;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+    public class DeprecatedMemberTest
+    {
+        private static void AssertIsDeprecated(MemberInfo member)
+        {
+            Assert.NotNull(member);
+            Assert.IsTrue(member.IsDefined(typeof(ObsoleteAttribute), false), "Member not obsolete: " + member);
+        }
+
+        [Test]
+        public void TestDepreatedPrimitiveValue()
+        {
+            AssertIsDeprecated(typeof(TestDeprecatedFields).GetProperty("DeprecatedInt32"));
+        }
+
+    }
+}
diff --git a/csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs b/csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs
index d163810..b2c24dd 100644
--- a/csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs
+++ b/csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs
@@ -1,196 +1,196 @@
-using System;

-using System.Collections;

-using Google.Protobuf.TestProtos.Proto2;

-using NUnit.Framework;

-

-using static Google.Protobuf.TestProtos.Proto2.UnittestExtensions;

-

-namespace Google.Protobuf

-{

-    public class ExtensionSetTest

-    {

-        [Test]

-        public void EmptyExtensionSet()

-        {

-            ExtensionSet<TestAllExtensions> extensions = new ExtensionSet<TestAllExtensions>();

-            Assert.AreEqual(0, extensions.CalculateSize());

-        }

-

-        [Test]

-        public void MergeExtensionSet()

-        {

-            ExtensionSet<TestAllExtensions> extensions = null;

-            ExtensionSet.Set(ref extensions, OptionalBoolExtension, true);

-

-            ExtensionSet<TestAllExtensions> other = null;

-

-            Assert.IsFalse(ExtensionSet.Has(ref other, OptionalBoolExtension));

-            ExtensionSet.MergeFrom(ref other, extensions);

-            Assert.IsTrue(ExtensionSet.Has(ref other, OptionalBoolExtension));

-        }

-

-        [Test]

-        public void TestMergeCodedInput()

-        {

-            var message = new TestAllExtensions();

-            message.SetExtension(OptionalBoolExtension, true);

-            var serialized = message.ToByteArray();

-

-            MessageParsingHelpers.AssertWritingMessage(message);

-

-            MessageParsingHelpers.AssertReadingMessage(

-                TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { OptionalBoolExtension }),

-                serialized,

-                other =>

-                {

-                    Assert.AreEqual(message, other);

-                    Assert.AreEqual(message.CalculateSize(), other.CalculateSize());

-                });

-        }

-

-        [Test]

-        public void TestMergeMessage()

-        {

-            var message = new TestAllExtensions();

-            message.SetExtension(OptionalBoolExtension, true);

-

-            var other = new TestAllExtensions();

-

-            Assert.AreNotEqual(message, other);

-            Assert.AreNotEqual(message.CalculateSize(), other.CalculateSize());

-

-            other.MergeFrom(message);

-

-            Assert.AreEqual(message, other);

-            Assert.AreEqual(message.CalculateSize(), other.CalculateSize());

-        }

-

-        [Test]

-        public void TryMergeFieldFrom_CodedInputStream()

-        {

-            var message = new TestAllExtensions();

-            message.SetExtension(OptionalStringExtension, "abcd");

-

-            var input = new CodedInputStream(message.ToByteArray());

-            input.ExtensionRegistry = new ExtensionRegistry() { OptionalStringExtension };

-            input.ReadTag(); // TryMergeFieldFrom expects that a tag was just read and will inspect the LastTag value

-

-            ExtensionSet<TestAllExtensions> extensionSet = null;

-            // test the legacy overload of TryMergeFieldFrom that takes a CodedInputStream

-            Assert.IsTrue(ExtensionSet.TryMergeFieldFrom(ref extensionSet, input));

-            Assert.AreEqual("abcd", ExtensionSet.Get(ref extensionSet, OptionalStringExtension));

-        }

-

-        [Test]

-        public void GetSingle()

-        {

-            var extensionValue = new TestAllTypes.Types.NestedMessage() { Bb = 42 };

-            var untypedExtension = new Extension<TestAllExtensions, object>(OptionalNestedMessageExtension.FieldNumber, codec: null);

-            var wrongTypedExtension = new Extension<TestAllExtensions, TestAllTypes>(OptionalNestedMessageExtension.FieldNumber, codec: null);

-

-            var message = new TestAllExtensions();

-

-            var value1 = message.GetExtension(untypedExtension);

-            Assert.IsNull(value1);

-

-            message.SetExtension(OptionalNestedMessageExtension, extensionValue);

-            var value2 = message.GetExtension(untypedExtension);

-            Assert.IsNotNull(value2);

-

-            var valueBytes = ((IMessage)value2).ToByteArray();

-            var parsedValue = TestProtos.Proto2.TestAllTypes.Types.NestedMessage.Parser.ParseFrom(valueBytes);

-            Assert.AreEqual(extensionValue, parsedValue);

-

-            var ex = Assert.Throws<InvalidOperationException>(() => message.GetExtension(wrongTypedExtension));

-

-            var expectedMessage = "The stored extension value has a type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes+Types+NestedMessage, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'. " +

-                "This a different from the requested type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'.";

-            Assert.AreEqual(expectedMessage, ex.Message);

-        }

-

-        [Test]

-        public void GetRepeated()

-        {

-            var extensionValue = new TestAllTypes.Types.NestedMessage() { Bb = 42 };

-            var untypedExtension = new Extension<TestAllExtensions, IList>(RepeatedNestedMessageExtension.FieldNumber, codec: null);

-            var wrongTypedExtension = new RepeatedExtension<TestAllExtensions, TestAllTypes>(RepeatedNestedMessageExtension.FieldNumber, codec: null);

-

-            var message = new TestAllExtensions();

-

-            var value1 = message.GetExtension(untypedExtension);

-            Assert.IsNull(value1);

-

-            var repeatedField = message.GetOrInitializeExtension<TestAllTypes.Types.NestedMessage>(RepeatedNestedMessageExtension);

-            repeatedField.Add(extensionValue);

-

-            var value2 = message.GetExtension(untypedExtension);

-            Assert.IsNotNull(value2);

-            Assert.AreEqual(1, value2.Count);

-

-            var valueBytes = ((IMessage)value2[0]).ToByteArray();

-            var parsedValue = TestProtos.Proto2.TestAllTypes.Types.NestedMessage.Parser.ParseFrom(valueBytes);

-            Assert.AreEqual(extensionValue, parsedValue);

-

-            var ex = Assert.Throws<InvalidOperationException>(() => message.GetExtension(wrongTypedExtension));

-

-            var expectedMessage = "The stored extension value has a type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes+Types+NestedMessage, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'. " +

-                "This a different from the requested type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'.";

-            Assert.AreEqual(expectedMessage, ex.Message);

-        }

-

-        [Test]

-        public void TestEquals()

-        {

-            var message = new TestAllExtensions();

-            message.SetExtension(OptionalBoolExtension, true);

-

-            var other = new TestAllExtensions();

-

-            Assert.AreNotEqual(message, other);

-            Assert.AreNotEqual(message.CalculateSize(), other.CalculateSize());

-

-            other.SetExtension(OptionalBoolExtension, true);

-

-            Assert.AreEqual(message, other);

-            Assert.AreEqual(message.CalculateSize(), other.CalculateSize());

-        }

-

-        [Test]

-        public void TestHashCode()

-        {

-            var message = new TestAllExtensions();

-            var hashCode = message.GetHashCode();

-

-            message.SetExtension(OptionalBoolExtension, true);

-

-            Assert.AreNotEqual(hashCode, message.GetHashCode());

-        }

-

-        [Test]

-        public void TestClone()

-        {

-            var message = new TestAllExtensions();

-            message.SetExtension(OptionalBoolExtension, true);

-

-            var other = message.Clone();

-

-            Assert.AreEqual(message, other);

-            Assert.AreEqual(message.CalculateSize(), other.CalculateSize());

-        }

-

-        [Test]

-        public void TestDefaultValueRoundTrip()

-        {

-            var message = new TestAllExtensions();

-            message.SetExtension(OptionalBoolExtension, false);

-            Assert.IsFalse(message.GetExtension(OptionalBoolExtension));

-            Assert.IsTrue(message.HasExtension(OptionalBoolExtension));

-

-            var bytes = message.ToByteArray();

-            var registry = new ExtensionRegistry { OptionalBoolExtension };

-            var parsed = TestAllExtensions.Parser.WithExtensionRegistry(registry).ParseFrom(bytes);

-            Assert.IsFalse(parsed.GetExtension(OptionalBoolExtension));

-            Assert.IsTrue(parsed.HasExtension(OptionalBoolExtension));

-        }

-    }

-}

+using System;
+using System.Collections;
+using Google.Protobuf.TestProtos.Proto2;
+using NUnit.Framework;
+
+using static Google.Protobuf.TestProtos.Proto2.UnittestExtensions;
+
+namespace Google.Protobuf
+{
+    public class ExtensionSetTest
+    {
+        [Test]
+        public void EmptyExtensionSet()
+        {
+            ExtensionSet<TestAllExtensions> extensions = new ExtensionSet<TestAllExtensions>();
+            Assert.AreEqual(0, extensions.CalculateSize());
+        }
+
+        [Test]
+        public void MergeExtensionSet()
+        {
+            ExtensionSet<TestAllExtensions> extensions = null;
+            ExtensionSet.Set(ref extensions, OptionalBoolExtension, true);
+
+            ExtensionSet<TestAllExtensions> other = null;
+
+            Assert.IsFalse(ExtensionSet.Has(ref other, OptionalBoolExtension));
+            ExtensionSet.MergeFrom(ref other, extensions);
+            Assert.IsTrue(ExtensionSet.Has(ref other, OptionalBoolExtension));
+        }
+
+        [Test]
+        public void TestMergeCodedInput()
+        {
+            var message = new TestAllExtensions();
+            message.SetExtension(OptionalBoolExtension, true);
+            var serialized = message.ToByteArray();
+
+            MessageParsingHelpers.AssertWritingMessage(message);
+
+            MessageParsingHelpers.AssertReadingMessage(
+                TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { OptionalBoolExtension }),
+                serialized,
+                other =>
+                {
+                    Assert.AreEqual(message, other);
+                    Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
+                });
+        }
+
+        [Test]
+        public void TestMergeMessage()
+        {
+            var message = new TestAllExtensions();
+            message.SetExtension(OptionalBoolExtension, true);
+
+            var other = new TestAllExtensions();
+
+            Assert.AreNotEqual(message, other);
+            Assert.AreNotEqual(message.CalculateSize(), other.CalculateSize());
+
+            other.MergeFrom(message);
+
+            Assert.AreEqual(message, other);
+            Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
+        }
+
+        [Test]
+        public void TryMergeFieldFrom_CodedInputStream()
+        {
+            var message = new TestAllExtensions();
+            message.SetExtension(OptionalStringExtension, "abcd");
+
+            var input = new CodedInputStream(message.ToByteArray());
+            input.ExtensionRegistry = new ExtensionRegistry() { OptionalStringExtension };
+            input.ReadTag(); // TryMergeFieldFrom expects that a tag was just read and will inspect the LastTag value
+
+            ExtensionSet<TestAllExtensions> extensionSet = null;
+            // test the legacy overload of TryMergeFieldFrom that takes a CodedInputStream
+            Assert.IsTrue(ExtensionSet.TryMergeFieldFrom(ref extensionSet, input));
+            Assert.AreEqual("abcd", ExtensionSet.Get(ref extensionSet, OptionalStringExtension));
+        }
+
+        [Test]
+        public void GetSingle()
+        {
+            var extensionValue = new TestAllTypes.Types.NestedMessage() { Bb = 42 };
+            var untypedExtension = new Extension<TestAllExtensions, object>(OptionalNestedMessageExtension.FieldNumber, codec: null);
+            var wrongTypedExtension = new Extension<TestAllExtensions, TestAllTypes>(OptionalNestedMessageExtension.FieldNumber, codec: null);
+
+            var message = new TestAllExtensions();
+
+            var value1 = message.GetExtension(untypedExtension);
+            Assert.IsNull(value1);
+
+            message.SetExtension(OptionalNestedMessageExtension, extensionValue);
+            var value2 = message.GetExtension(untypedExtension);
+            Assert.IsNotNull(value2);
+
+            var valueBytes = ((IMessage)value2).ToByteArray();
+            var parsedValue = TestProtos.Proto2.TestAllTypes.Types.NestedMessage.Parser.ParseFrom(valueBytes);
+            Assert.AreEqual(extensionValue, parsedValue);
+
+            var ex = Assert.Throws<InvalidOperationException>(() => message.GetExtension(wrongTypedExtension));
+
+            var expectedMessage = "The stored extension value has a type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes+Types+NestedMessage, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'. " +
+                "This a different from the requested type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'.";
+            Assert.AreEqual(expectedMessage, ex.Message);
+        }
+
+        [Test]
+        public void GetRepeated()
+        {
+            var extensionValue = new TestAllTypes.Types.NestedMessage() { Bb = 42 };
+            var untypedExtension = new Extension<TestAllExtensions, IList>(RepeatedNestedMessageExtension.FieldNumber, codec: null);
+            var wrongTypedExtension = new RepeatedExtension<TestAllExtensions, TestAllTypes>(RepeatedNestedMessageExtension.FieldNumber, codec: null);
+
+            var message = new TestAllExtensions();
+
+            var value1 = message.GetExtension(untypedExtension);
+            Assert.IsNull(value1);
+
+            var repeatedField = message.GetOrInitializeExtension<TestAllTypes.Types.NestedMessage>(RepeatedNestedMessageExtension);
+            repeatedField.Add(extensionValue);
+
+            var value2 = message.GetExtension(untypedExtension);
+            Assert.IsNotNull(value2);
+            Assert.AreEqual(1, value2.Count);
+
+            var valueBytes = ((IMessage)value2[0]).ToByteArray();
+            var parsedValue = TestProtos.Proto2.TestAllTypes.Types.NestedMessage.Parser.ParseFrom(valueBytes);
+            Assert.AreEqual(extensionValue, parsedValue);
+
+            var ex = Assert.Throws<InvalidOperationException>(() => message.GetExtension(wrongTypedExtension));
+
+            var expectedMessage = "The stored extension value has a type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes+Types+NestedMessage, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'. " +
+                "This a different from the requested type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'.";
+            Assert.AreEqual(expectedMessage, ex.Message);
+        }
+
+        [Test]
+        public void TestEquals()
+        {
+            var message = new TestAllExtensions();
+            message.SetExtension(OptionalBoolExtension, true);
+
+            var other = new TestAllExtensions();
+
+            Assert.AreNotEqual(message, other);
+            Assert.AreNotEqual(message.CalculateSize(), other.CalculateSize());
+
+            other.SetExtension(OptionalBoolExtension, true);
+
+            Assert.AreEqual(message, other);
+            Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
+        }
+
+        [Test]
+        public void TestHashCode()
+        {
+            var message = new TestAllExtensions();
+            var hashCode = message.GetHashCode();
+
+            message.SetExtension(OptionalBoolExtension, true);
+
+            Assert.AreNotEqual(hashCode, message.GetHashCode());
+        }
+
+        [Test]
+        public void TestClone()
+        {
+            var message = new TestAllExtensions();
+            message.SetExtension(OptionalBoolExtension, true);
+
+            var other = message.Clone();
+
+            Assert.AreEqual(message, other);
+            Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
+        }
+
+        [Test]
+        public void TestDefaultValueRoundTrip()
+        {
+            var message = new TestAllExtensions();
+            message.SetExtension(OptionalBoolExtension, false);
+            Assert.IsFalse(message.GetExtension(OptionalBoolExtension));
+            Assert.IsTrue(message.HasExtension(OptionalBoolExtension));
+
+            var bytes = message.ToByteArray();
+            var registry = new ExtensionRegistry { OptionalBoolExtension };
+            var parsed = TestAllExtensions.Parser.WithExtensionRegistry(registry).ParseFrom(bytes);
+            Assert.IsFalse(parsed.GetExtension(OptionalBoolExtension));
+            Assert.IsTrue(parsed.HasExtension(OptionalBoolExtension));
+        }
+    }
+}
diff --git a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs
index 894d914..fa5f927 100644
--- a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs
+++ b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs
@@ -1,397 +1,397 @@
-using Google.Protobuf.TestProtos.Proto2;

-using Proto2 = Google.Protobuf.TestProtos.Proto2;

-using NUnit.Framework;

-

-using static Google.Protobuf.TestProtos.Proto2.UnittestExtensions;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Tests around the generated TestAllTypes message in unittest.proto

-    /// </summary>

-    public partial class GeneratedMessageTest

-    {

-        [Test]

-        public void DefaultProto2Values()

-        {

-            var message = new TestAllTypes();

-            Assert.AreEqual(false, message.OptionalBool);

-            Assert.AreEqual(ByteString.Empty, message.OptionalBytes);

-            Assert.AreEqual(0.0, message.OptionalDouble);

-            Assert.AreEqual(0, message.OptionalFixed32);

-            Assert.AreEqual(0L, message.OptionalFixed64);

-            Assert.AreEqual(0.0f, message.OptionalFloat);

-            Assert.AreEqual(ForeignEnum.ForeignFoo, message.OptionalForeignEnum);

-            Assert.IsNull(message.OptionalForeignMessage);

-            Assert.AreEqual(ImportEnum.ImportFoo, message.OptionalImportEnum);

-            Assert.IsNull(message.OptionalImportMessage);

-            Assert.AreEqual(0, message.OptionalInt32);

-            Assert.AreEqual(0L, message.OptionalInt64);

-            Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Foo, message.OptionalNestedEnum);

-            Assert.IsNull(message.OptionalNestedMessage);

-            Assert.IsNull(message.OptionalPublicImportMessage);

-            Assert.AreEqual(0, message.OptionalSfixed32);

-            Assert.AreEqual(0L, message.OptionalSfixed64);

-            Assert.AreEqual(0, message.OptionalSint32);

-            Assert.AreEqual(0L, message.OptionalSint64);

-            Assert.AreEqual("", message.OptionalString);

-            Assert.AreEqual(0U, message.OptionalUint32);

-            Assert.AreEqual(0UL, message.OptionalUint64);

-

-            // Repeated fields

-            Assert.AreEqual(0, message.RepeatedBool.Count);

-            Assert.AreEqual(0, message.RepeatedBytes.Count);

-            Assert.AreEqual(0, message.RepeatedDouble.Count);

-            Assert.AreEqual(0, message.RepeatedFixed32.Count);

-            Assert.AreEqual(0, message.RepeatedFixed64.Count);

-            Assert.AreEqual(0, message.RepeatedFloat.Count);

-            Assert.AreEqual(0, message.RepeatedForeignEnum.Count);

-            Assert.AreEqual(0, message.RepeatedForeignMessage.Count);

-            Assert.AreEqual(0, message.RepeatedImportEnum.Count);

-            Assert.AreEqual(0, message.RepeatedImportMessage.Count);

-            Assert.AreEqual(0, message.RepeatedNestedEnum.Count);

-            Assert.AreEqual(0, message.RepeatedNestedMessage.Count);

-            Assert.AreEqual(0, message.RepeatedSfixed32.Count);

-            Assert.AreEqual(0, message.RepeatedSfixed64.Count);

-            Assert.AreEqual(0, message.RepeatedSint32.Count);

-            Assert.AreEqual(0, message.RepeatedSint64.Count);

-            Assert.AreEqual(0, message.RepeatedString.Count);

-            Assert.AreEqual(0, message.RepeatedUint32.Count);

-            Assert.AreEqual(0, message.RepeatedUint64.Count);

-

-            // Oneof fields

-            Assert.AreEqual(Proto2.TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);

-            Assert.AreEqual(0, message.OneofUint32);

-            Assert.AreEqual("", message.OneofString);

-            Assert.AreEqual(ByteString.Empty, message.OneofBytes);

-            Assert.IsNull(message.OneofNestedMessage);

-

-            Assert.AreEqual(true, message.DefaultBool);

-            Assert.AreEqual(ByteString.CopyFromUtf8("world"), message.DefaultBytes);

-            Assert.AreEqual("123", message.DefaultCord);

-            Assert.AreEqual(52e3, message.DefaultDouble);

-            Assert.AreEqual(47, message.DefaultFixed32);

-            Assert.AreEqual(48, message.DefaultFixed64);

-            Assert.AreEqual(51.5, message.DefaultFloat);

-            Assert.AreEqual(ForeignEnum.ForeignBar, message.DefaultForeignEnum);

-            Assert.AreEqual(ImportEnum.ImportBar, message.DefaultImportEnum);

-            Assert.AreEqual(41, message.DefaultInt32);

-            Assert.AreEqual(42, message.DefaultInt64);

-            Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Bar, message.DefaultNestedEnum);

-            Assert.AreEqual(49, message.DefaultSfixed32);

-            Assert.AreEqual(-50, message.DefaultSfixed64);

-            Assert.AreEqual(-45, message.DefaultSint32);

-            Assert.AreEqual(46, message.DefaultSint64);

-            Assert.AreEqual("hello", message.DefaultString);

-            Assert.AreEqual("abc", message.DefaultStringPiece);

-            Assert.AreEqual(43, message.DefaultUint32);

-            Assert.AreEqual(44, message.DefaultUint64);

-

-            Assert.False(message.HasDefaultBool);

-            Assert.False(message.HasDefaultBytes);

-            Assert.False(message.HasDefaultCord);

-            Assert.False(message.HasDefaultDouble);

-            Assert.False(message.HasDefaultFixed32);

-            Assert.False(message.HasDefaultFixed64);

-            Assert.False(message.HasDefaultFloat);

-            Assert.False(message.HasDefaultForeignEnum);

-            Assert.False(message.HasDefaultImportEnum);

-            Assert.False(message.HasDefaultInt32);

-            Assert.False(message.HasDefaultInt64);

-            Assert.False(message.HasDefaultNestedEnum);

-            Assert.False(message.HasDefaultSfixed32);

-            Assert.False(message.HasDefaultSfixed64);

-            Assert.False(message.HasDefaultSint32);

-            Assert.False(message.HasDefaultSint64);

-            Assert.False(message.HasDefaultString);

-            Assert.False(message.HasDefaultStringPiece);

-            Assert.False(message.HasDefaultUint32);

-            Assert.False(message.HasDefaultUint64);

-        }

-

-        [Test]

-        public void DefaultExtensionValues()

-        {

-            var message = new TestAllExtensions();

-            Assert.AreEqual(false, message.GetExtension(OptionalBoolExtension));

-            Assert.AreEqual(ByteString.Empty, message.GetExtension(OptionalBytesExtension));

-            Assert.AreEqual(0.0, message.GetExtension(OptionalDoubleExtension));

-            Assert.AreEqual(0, message.GetExtension(OptionalFixed32Extension));

-            Assert.AreEqual(0L, message.GetExtension(OptionalFixed64Extension));

-            Assert.AreEqual(0.0f, message.GetExtension(OptionalFloatExtension));

-            Assert.AreEqual(ForeignEnum.ForeignFoo, message.GetExtension(OptionalForeignEnumExtension));

-            Assert.IsNull(message.GetExtension(OptionalForeignMessageExtension));

-            Assert.AreEqual(ImportEnum.ImportFoo, message.GetExtension(OptionalImportEnumExtension));

-            Assert.IsNull(message.GetExtension(OptionalImportMessageExtension));

-            Assert.AreEqual(0, message.GetExtension(OptionalInt32Extension));

-            Assert.AreEqual(0L, message.GetExtension(OptionalInt64Extension));

-            Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Foo, message.GetExtension(OptionalNestedEnumExtension));

-            Assert.IsNull(message.GetExtension(OptionalNestedMessageExtension));

-            Assert.IsNull(message.GetExtension(OptionalPublicImportMessageExtension));

-            Assert.AreEqual(0, message.GetExtension(OptionalSfixed32Extension));

-            Assert.AreEqual(0L, message.GetExtension(OptionalSfixed64Extension));

-            Assert.AreEqual(0, message.GetExtension(OptionalSint32Extension));

-            Assert.AreEqual(0L, message.GetExtension(OptionalSint64Extension));

-            Assert.AreEqual("", message.GetExtension(OptionalStringExtension));

-            Assert.AreEqual(0U, message.GetExtension(OptionalUint32Extension));

-            Assert.AreEqual(0UL, message.GetExtension(OptionalUint64Extension));

-

-            // Repeated fields

-            Assert.IsNull(message.GetExtension(RepeatedBoolExtension));

-            Assert.IsNull(message.GetExtension(RepeatedBytesExtension));

-            Assert.IsNull(message.GetExtension(RepeatedDoubleExtension));

-            Assert.IsNull(message.GetExtension(RepeatedFixed32Extension));

-            Assert.IsNull(message.GetExtension(RepeatedFixed64Extension));

-            Assert.IsNull(message.GetExtension(RepeatedFloatExtension));

-            Assert.IsNull(message.GetExtension(RepeatedForeignEnumExtension));

-            Assert.IsNull(message.GetExtension(RepeatedForeignMessageExtension));

-            Assert.IsNull(message.GetExtension(RepeatedImportEnumExtension));

-            Assert.IsNull(message.GetExtension(RepeatedImportMessageExtension));

-            Assert.IsNull(message.GetExtension(RepeatedNestedEnumExtension));

-            Assert.IsNull(message.GetExtension(RepeatedNestedMessageExtension));

-            Assert.IsNull(message.GetExtension(RepeatedSfixed32Extension));

-            Assert.IsNull(message.GetExtension(RepeatedSfixed64Extension));

-            Assert.IsNull(message.GetExtension(RepeatedSint32Extension));

-            Assert.IsNull(message.GetExtension(RepeatedSint64Extension));

-            Assert.IsNull(message.GetExtension(RepeatedStringExtension));

-            Assert.IsNull(message.GetExtension(RepeatedUint32Extension));

-            Assert.IsNull(message.GetExtension(RepeatedUint64Extension));

-

-            // Oneof fields

-            Assert.AreEqual(0, message.GetExtension(OneofUint32Extension));

-            Assert.AreEqual("", message.GetExtension(OneofStringExtension));

-            Assert.AreEqual(ByteString.Empty, message.GetExtension(OneofBytesExtension));

-            Assert.IsNull(message.GetExtension(OneofNestedMessageExtension));

-

-            Assert.AreEqual(true, message.GetExtension(DefaultBoolExtension));

-            Assert.AreEqual(ByteString.CopyFromUtf8("world"), message.GetExtension(DefaultBytesExtension));

-            Assert.AreEqual("123", message.GetExtension(DefaultCordExtension));

-            Assert.AreEqual(52e3, message.GetExtension(DefaultDoubleExtension));

-            Assert.AreEqual(47, message.GetExtension(DefaultFixed32Extension));

-            Assert.AreEqual(48, message.GetExtension(DefaultFixed64Extension));

-            Assert.AreEqual(51.5, message.GetExtension(DefaultFloatExtension));

-            Assert.AreEqual(ForeignEnum.ForeignBar, message.GetExtension(DefaultForeignEnumExtension));

-            Assert.AreEqual(ImportEnum.ImportBar, message.GetExtension(DefaultImportEnumExtension));

-            Assert.AreEqual(41, message.GetExtension(DefaultInt32Extension));

-            Assert.AreEqual(42, message.GetExtension(DefaultInt64Extension));

-            Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Bar, message.GetExtension(DefaultNestedEnumExtension));

-            Assert.AreEqual(49, message.GetExtension(DefaultSfixed32Extension));

-            Assert.AreEqual(-50, message.GetExtension(DefaultSfixed64Extension));

-            Assert.AreEqual(-45, message.GetExtension(DefaultSint32Extension));

-            Assert.AreEqual(46, message.GetExtension(DefaultSint64Extension));

-            Assert.AreEqual("hello", message.GetExtension(DefaultStringExtension));

-            Assert.AreEqual("abc", message.GetExtension(DefaultStringPieceExtension));

-            Assert.AreEqual(43, message.GetExtension(DefaultUint32Extension));

-            Assert.AreEqual(44, message.GetExtension(DefaultUint64Extension));

-

-            Assert.False(message.HasExtension(DefaultBoolExtension));

-            Assert.False(message.HasExtension(DefaultBytesExtension));

-            Assert.False(message.HasExtension(DefaultCordExtension));

-            Assert.False(message.HasExtension(DefaultDoubleExtension));

-            Assert.False(message.HasExtension(DefaultFixed32Extension));

-            Assert.False(message.HasExtension(DefaultFixed64Extension));

-            Assert.False(message.HasExtension(DefaultFloatExtension));

-            Assert.False(message.HasExtension(DefaultForeignEnumExtension));

-            Assert.False(message.HasExtension(DefaultImportEnumExtension));

-            Assert.False(message.HasExtension(DefaultInt32Extension));

-            Assert.False(message.HasExtension(DefaultInt64Extension));

-            Assert.False(message.HasExtension(DefaultNestedEnumExtension));

-            Assert.False(message.HasExtension(DefaultSfixed32Extension));

-            Assert.False(message.HasExtension(DefaultSfixed64Extension));

-            Assert.False(message.HasExtension(DefaultSint32Extension));

-            Assert.False(message.HasExtension(DefaultSint64Extension));

-            Assert.False(message.HasExtension(DefaultStringExtension));

-            Assert.False(message.HasExtension(DefaultStringPieceExtension));

-            Assert.False(message.HasExtension(DefaultUint32Extension));

-            Assert.False(message.HasExtension(DefaultUint64Extension));

-        }

-

-        [Test]

-        public void FieldPresence()

-        {

-            var message = new TestAllTypes();

-

-            Assert.False(message.HasOptionalBool);

-            Assert.False(message.OptionalBool);

-

-            message.OptionalBool = true;

-

-            Assert.True(message.HasOptionalBool);

-            Assert.True(message.OptionalBool);

-

-            message.OptionalBool = false;

-

-            Assert.True(message.HasOptionalBool);

-            Assert.False(message.OptionalBool);

-

-            message.ClearOptionalBool();

-

-            Assert.False(message.HasOptionalBool);

-            Assert.False(message.OptionalBool);

-

-            Assert.False(message.HasDefaultBool);

-            Assert.True(message.DefaultBool);

-

-            message.DefaultBool = false;

-

-            Assert.True(message.HasDefaultBool);

-            Assert.False(message.DefaultBool);

-

-            message.DefaultBool = true;

-

-            Assert.True(message.HasDefaultBool);

-            Assert.True(message.DefaultBool);

-

-            message.ClearDefaultBool();

-

-            Assert.False(message.HasDefaultBool);

-            Assert.True(message.DefaultBool);

-        }

-

-        [Test]

-        public void RequiredFields()

-        {

-            var message = new TestRequired();

-            Assert.False(message.IsInitialized());

-

-            message.A = 1;

-            message.B = 2;

-            message.C = 3;

-

-            Assert.True(message.IsInitialized());

-        }

-

-        /// <summary>

-        /// Code was accidentally left in message parser that threw exceptions when missing required fields after parsing.

-        /// We've decided to not throw exceptions on missing fields, instead leaving it up to the consumer how they

-        /// want to check and handle missing fields.

-        /// </summary>

-        [Test]

-        public void RequiredFieldsNoThrow()

-        {

-            Assert.DoesNotThrow(() => MessageParsingHelpers.AssertReadingMessage(TestRequired.Parser, new byte[0], m => { }));

-            Assert.DoesNotThrow(() => MessageParsingHelpers.AssertReadingMessage(TestRequired.Parser as MessageParser, new byte[0], m => { }));

-        }

-

-        [Test]

-        public void RequiredFieldsInExtensions()

-        {

-            var message = new TestAllExtensions();

-            Assert.True(message.IsInitialized());

-

-            message.SetExtension(TestRequired.Extensions.Single, new TestRequired());

-

-            Assert.False(message.IsInitialized());

-

-            var extensionMessage = message.GetExtension(TestRequired.Extensions.Single);

-            extensionMessage.A = 1;

-            extensionMessage.B = 2;

-            extensionMessage.C = 3;

-

-            Assert.True(message.IsInitialized());

-

-            message.GetOrInitializeExtension(TestRequired.Extensions.Multi);

-

-            Assert.True(message.IsInitialized());

-

-            message.GetExtension(TestRequired.Extensions.Multi).Add(new TestRequired());

-

-            Assert.False(message.IsInitialized());

-

-            extensionMessage = message.GetExtension(TestRequired.Extensions.Multi)[0];

-            extensionMessage.A = 1;

-            extensionMessage.B = 2;

-            extensionMessage.C = 3;

-

-            Assert.True(message.IsInitialized());

-

-            message.SetExtension(UnittestExtensions.OptionalBoolExtension, true);

-

-            Assert.True(message.IsInitialized());

-

-            message.GetOrInitializeExtension(UnittestExtensions.RepeatedBoolExtension).Add(true);

-

-            Assert.True(message.IsInitialized());

-        }

-

-        [Test]

-        public void RequiredFieldInNestedMessageMapValue()

-        {

-            var message = new TestRequiredMap();

-            message.Foo.Add(0, new TestRequiredMap.Types.NestedMessage());

-

-            Assert.False(message.IsInitialized());

-

-            message.Foo[0].RequiredInt32 = 12;

-

-            Assert.True(message.IsInitialized());

-        }

-

-        [Test]

-        public void RoundTrip_Groups()

-        {

-            var message = new TestAllTypes

-            {

-                OptionalGroup = new TestAllTypes.Types.OptionalGroup

-                {

-                    A = 10

-                },

-                RepeatedGroup =

-                {

-                    new TestAllTypes.Types.RepeatedGroup { A = 10 },

-                    new TestAllTypes.Types.RepeatedGroup { A = 20 },

-                    new TestAllTypes.Types.RepeatedGroup { A = 30 }

-                }

-            };

-

-            MessageParsingHelpers.AssertWritingMessage(message);

-

-            MessageParsingHelpers.AssertRoundtrip(Proto2.TestAllTypes.Parser, message);

-        }

-

-        [Test]

-        public void RoundTrip_ExtensionGroups()

-        {

-            var message = new TestAllExtensions();

-            message.SetExtension(UnittestExtensions.OptionalGroupExtension, new OptionalGroup_extension { A = 10 });

-            message.GetOrInitializeExtension(UnittestExtensions.RepeatedGroupExtension).AddRange(new[]

-            {

-                new RepeatedGroup_extension { A = 10 },

-                new RepeatedGroup_extension { A = 20 },

-                new RepeatedGroup_extension { A = 30 }

-            });

-

-            MessageParsingHelpers.AssertWritingMessage(message);

-

-            MessageParsingHelpers.AssertRoundtrip(

-                TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { UnittestExtensions.OptionalGroupExtension, UnittestExtensions.RepeatedGroupExtension }),

-                message);

-        }

-

-        [Test]

-        public void RoundTrip_NestedExtensionGroup()

-        {

-            var message = new TestGroupExtension();

-            message.SetExtension(TestNestedExtension.Extensions.OptionalGroupExtension, new TestNestedExtension.Types.OptionalGroup_extension { A = 10 });

-

-            MessageParsingHelpers.AssertWritingMessage(message);

-            

-            MessageParsingHelpers.AssertRoundtrip(

-                TestGroupExtension.Parser.WithExtensionRegistry(new ExtensionRegistry() { TestNestedExtension.Extensions.OptionalGroupExtension }),

-                message);

-        }

-

-        [Test]

-        public void RoundTrip_ParseUsingCodedInput()

-        {

-            var message = new TestAllExtensions();

-            message.SetExtension(UnittestExtensions.OptionalBoolExtension, true);

-            byte[] bytes = message.ToByteArray();

-            using (CodedInputStream input = new CodedInputStream(bytes))

-            {

-                var parsed = TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { UnittestExtensions.OptionalBoolExtension }).ParseFrom(input);

-                Assert.AreEqual(message, parsed);

-            }

-        }

-    }

-}

+using Google.Protobuf.TestProtos.Proto2;
+using Proto2 = Google.Protobuf.TestProtos.Proto2;
+using NUnit.Framework;
+
+using static Google.Protobuf.TestProtos.Proto2.UnittestExtensions;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Tests around the generated TestAllTypes message in unittest.proto
+    /// </summary>
+    public partial class GeneratedMessageTest
+    {
+        [Test]
+        public void DefaultProto2Values()
+        {
+            var message = new TestAllTypes();
+            Assert.AreEqual(false, message.OptionalBool);
+            Assert.AreEqual(ByteString.Empty, message.OptionalBytes);
+            Assert.AreEqual(0.0, message.OptionalDouble);
+            Assert.AreEqual(0, message.OptionalFixed32);
+            Assert.AreEqual(0L, message.OptionalFixed64);
+            Assert.AreEqual(0.0f, message.OptionalFloat);
+            Assert.AreEqual(ForeignEnum.ForeignFoo, message.OptionalForeignEnum);
+            Assert.IsNull(message.OptionalForeignMessage);
+            Assert.AreEqual(ImportEnum.ImportFoo, message.OptionalImportEnum);
+            Assert.IsNull(message.OptionalImportMessage);
+            Assert.AreEqual(0, message.OptionalInt32);
+            Assert.AreEqual(0L, message.OptionalInt64);
+            Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Foo, message.OptionalNestedEnum);
+            Assert.IsNull(message.OptionalNestedMessage);
+            Assert.IsNull(message.OptionalPublicImportMessage);
+            Assert.AreEqual(0, message.OptionalSfixed32);
+            Assert.AreEqual(0L, message.OptionalSfixed64);
+            Assert.AreEqual(0, message.OptionalSint32);
+            Assert.AreEqual(0L, message.OptionalSint64);
+            Assert.AreEqual("", message.OptionalString);
+            Assert.AreEqual(0U, message.OptionalUint32);
+            Assert.AreEqual(0UL, message.OptionalUint64);
+
+            // Repeated fields
+            Assert.AreEqual(0, message.RepeatedBool.Count);
+            Assert.AreEqual(0, message.RepeatedBytes.Count);
+            Assert.AreEqual(0, message.RepeatedDouble.Count);
+            Assert.AreEqual(0, message.RepeatedFixed32.Count);
+            Assert.AreEqual(0, message.RepeatedFixed64.Count);
+            Assert.AreEqual(0, message.RepeatedFloat.Count);
+            Assert.AreEqual(0, message.RepeatedForeignEnum.Count);
+            Assert.AreEqual(0, message.RepeatedForeignMessage.Count);
+            Assert.AreEqual(0, message.RepeatedImportEnum.Count);
+            Assert.AreEqual(0, message.RepeatedImportMessage.Count);
+            Assert.AreEqual(0, message.RepeatedNestedEnum.Count);
+            Assert.AreEqual(0, message.RepeatedNestedMessage.Count);
+            Assert.AreEqual(0, message.RepeatedSfixed32.Count);
+            Assert.AreEqual(0, message.RepeatedSfixed64.Count);
+            Assert.AreEqual(0, message.RepeatedSint32.Count);
+            Assert.AreEqual(0, message.RepeatedSint64.Count);
+            Assert.AreEqual(0, message.RepeatedString.Count);
+            Assert.AreEqual(0, message.RepeatedUint32.Count);
+            Assert.AreEqual(0, message.RepeatedUint64.Count);
+
+            // Oneof fields
+            Assert.AreEqual(Proto2.TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
+            Assert.AreEqual(0, message.OneofUint32);
+            Assert.AreEqual("", message.OneofString);
+            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+            Assert.IsNull(message.OneofNestedMessage);
+
+            Assert.AreEqual(true, message.DefaultBool);
+            Assert.AreEqual(ByteString.CopyFromUtf8("world"), message.DefaultBytes);
+            Assert.AreEqual("123", message.DefaultCord);
+            Assert.AreEqual(52e3, message.DefaultDouble);
+            Assert.AreEqual(47, message.DefaultFixed32);
+            Assert.AreEqual(48, message.DefaultFixed64);
+            Assert.AreEqual(51.5, message.DefaultFloat);
+            Assert.AreEqual(ForeignEnum.ForeignBar, message.DefaultForeignEnum);
+            Assert.AreEqual(ImportEnum.ImportBar, message.DefaultImportEnum);
+            Assert.AreEqual(41, message.DefaultInt32);
+            Assert.AreEqual(42, message.DefaultInt64);
+            Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Bar, message.DefaultNestedEnum);
+            Assert.AreEqual(49, message.DefaultSfixed32);
+            Assert.AreEqual(-50, message.DefaultSfixed64);
+            Assert.AreEqual(-45, message.DefaultSint32);
+            Assert.AreEqual(46, message.DefaultSint64);
+            Assert.AreEqual("hello", message.DefaultString);
+            Assert.AreEqual("abc", message.DefaultStringPiece);
+            Assert.AreEqual(43, message.DefaultUint32);
+            Assert.AreEqual(44, message.DefaultUint64);
+
+            Assert.False(message.HasDefaultBool);
+            Assert.False(message.HasDefaultBytes);
+            Assert.False(message.HasDefaultCord);
+            Assert.False(message.HasDefaultDouble);
+            Assert.False(message.HasDefaultFixed32);
+            Assert.False(message.HasDefaultFixed64);
+            Assert.False(message.HasDefaultFloat);
+            Assert.False(message.HasDefaultForeignEnum);
+            Assert.False(message.HasDefaultImportEnum);
+            Assert.False(message.HasDefaultInt32);
+            Assert.False(message.HasDefaultInt64);
+            Assert.False(message.HasDefaultNestedEnum);
+            Assert.False(message.HasDefaultSfixed32);
+            Assert.False(message.HasDefaultSfixed64);
+            Assert.False(message.HasDefaultSint32);
+            Assert.False(message.HasDefaultSint64);
+            Assert.False(message.HasDefaultString);
+            Assert.False(message.HasDefaultStringPiece);
+            Assert.False(message.HasDefaultUint32);
+            Assert.False(message.HasDefaultUint64);
+        }
+
+        [Test]
+        public void DefaultExtensionValues()
+        {
+            var message = new TestAllExtensions();
+            Assert.AreEqual(false, message.GetExtension(OptionalBoolExtension));
+            Assert.AreEqual(ByteString.Empty, message.GetExtension(OptionalBytesExtension));
+            Assert.AreEqual(0.0, message.GetExtension(OptionalDoubleExtension));
+            Assert.AreEqual(0, message.GetExtension(OptionalFixed32Extension));
+            Assert.AreEqual(0L, message.GetExtension(OptionalFixed64Extension));
+            Assert.AreEqual(0.0f, message.GetExtension(OptionalFloatExtension));
+            Assert.AreEqual(ForeignEnum.ForeignFoo, message.GetExtension(OptionalForeignEnumExtension));
+            Assert.IsNull(message.GetExtension(OptionalForeignMessageExtension));
+            Assert.AreEqual(ImportEnum.ImportFoo, message.GetExtension(OptionalImportEnumExtension));
+            Assert.IsNull(message.GetExtension(OptionalImportMessageExtension));
+            Assert.AreEqual(0, message.GetExtension(OptionalInt32Extension));
+            Assert.AreEqual(0L, message.GetExtension(OptionalInt64Extension));
+            Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Foo, message.GetExtension(OptionalNestedEnumExtension));
+            Assert.IsNull(message.GetExtension(OptionalNestedMessageExtension));
+            Assert.IsNull(message.GetExtension(OptionalPublicImportMessageExtension));
+            Assert.AreEqual(0, message.GetExtension(OptionalSfixed32Extension));
+            Assert.AreEqual(0L, message.GetExtension(OptionalSfixed64Extension));
+            Assert.AreEqual(0, message.GetExtension(OptionalSint32Extension));
+            Assert.AreEqual(0L, message.GetExtension(OptionalSint64Extension));
+            Assert.AreEqual("", message.GetExtension(OptionalStringExtension));
+            Assert.AreEqual(0U, message.GetExtension(OptionalUint32Extension));
+            Assert.AreEqual(0UL, message.GetExtension(OptionalUint64Extension));
+
+            // Repeated fields
+            Assert.IsNull(message.GetExtension(RepeatedBoolExtension));
+            Assert.IsNull(message.GetExtension(RepeatedBytesExtension));
+            Assert.IsNull(message.GetExtension(RepeatedDoubleExtension));
+            Assert.IsNull(message.GetExtension(RepeatedFixed32Extension));
+            Assert.IsNull(message.GetExtension(RepeatedFixed64Extension));
+            Assert.IsNull(message.GetExtension(RepeatedFloatExtension));
+            Assert.IsNull(message.GetExtension(RepeatedForeignEnumExtension));
+            Assert.IsNull(message.GetExtension(RepeatedForeignMessageExtension));
+            Assert.IsNull(message.GetExtension(RepeatedImportEnumExtension));
+            Assert.IsNull(message.GetExtension(RepeatedImportMessageExtension));
+            Assert.IsNull(message.GetExtension(RepeatedNestedEnumExtension));
+            Assert.IsNull(message.GetExtension(RepeatedNestedMessageExtension));
+            Assert.IsNull(message.GetExtension(RepeatedSfixed32Extension));
+            Assert.IsNull(message.GetExtension(RepeatedSfixed64Extension));
+            Assert.IsNull(message.GetExtension(RepeatedSint32Extension));
+            Assert.IsNull(message.GetExtension(RepeatedSint64Extension));
+            Assert.IsNull(message.GetExtension(RepeatedStringExtension));
+            Assert.IsNull(message.GetExtension(RepeatedUint32Extension));
+            Assert.IsNull(message.GetExtension(RepeatedUint64Extension));
+
+            // Oneof fields
+            Assert.AreEqual(0, message.GetExtension(OneofUint32Extension));
+            Assert.AreEqual("", message.GetExtension(OneofStringExtension));
+            Assert.AreEqual(ByteString.Empty, message.GetExtension(OneofBytesExtension));
+            Assert.IsNull(message.GetExtension(OneofNestedMessageExtension));
+
+            Assert.AreEqual(true, message.GetExtension(DefaultBoolExtension));
+            Assert.AreEqual(ByteString.CopyFromUtf8("world"), message.GetExtension(DefaultBytesExtension));
+            Assert.AreEqual("123", message.GetExtension(DefaultCordExtension));
+            Assert.AreEqual(52e3, message.GetExtension(DefaultDoubleExtension));
+            Assert.AreEqual(47, message.GetExtension(DefaultFixed32Extension));
+            Assert.AreEqual(48, message.GetExtension(DefaultFixed64Extension));
+            Assert.AreEqual(51.5, message.GetExtension(DefaultFloatExtension));
+            Assert.AreEqual(ForeignEnum.ForeignBar, message.GetExtension(DefaultForeignEnumExtension));
+            Assert.AreEqual(ImportEnum.ImportBar, message.GetExtension(DefaultImportEnumExtension));
+            Assert.AreEqual(41, message.GetExtension(DefaultInt32Extension));
+            Assert.AreEqual(42, message.GetExtension(DefaultInt64Extension));
+            Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Bar, message.GetExtension(DefaultNestedEnumExtension));
+            Assert.AreEqual(49, message.GetExtension(DefaultSfixed32Extension));
+            Assert.AreEqual(-50, message.GetExtension(DefaultSfixed64Extension));
+            Assert.AreEqual(-45, message.GetExtension(DefaultSint32Extension));
+            Assert.AreEqual(46, message.GetExtension(DefaultSint64Extension));
+            Assert.AreEqual("hello", message.GetExtension(DefaultStringExtension));
+            Assert.AreEqual("abc", message.GetExtension(DefaultStringPieceExtension));
+            Assert.AreEqual(43, message.GetExtension(DefaultUint32Extension));
+            Assert.AreEqual(44, message.GetExtension(DefaultUint64Extension));
+
+            Assert.False(message.HasExtension(DefaultBoolExtension));
+            Assert.False(message.HasExtension(DefaultBytesExtension));
+            Assert.False(message.HasExtension(DefaultCordExtension));
+            Assert.False(message.HasExtension(DefaultDoubleExtension));
+            Assert.False(message.HasExtension(DefaultFixed32Extension));
+            Assert.False(message.HasExtension(DefaultFixed64Extension));
+            Assert.False(message.HasExtension(DefaultFloatExtension));
+            Assert.False(message.HasExtension(DefaultForeignEnumExtension));
+            Assert.False(message.HasExtension(DefaultImportEnumExtension));
+            Assert.False(message.HasExtension(DefaultInt32Extension));
+            Assert.False(message.HasExtension(DefaultInt64Extension));
+            Assert.False(message.HasExtension(DefaultNestedEnumExtension));
+            Assert.False(message.HasExtension(DefaultSfixed32Extension));
+            Assert.False(message.HasExtension(DefaultSfixed64Extension));
+            Assert.False(message.HasExtension(DefaultSint32Extension));
+            Assert.False(message.HasExtension(DefaultSint64Extension));
+            Assert.False(message.HasExtension(DefaultStringExtension));
+            Assert.False(message.HasExtension(DefaultStringPieceExtension));
+            Assert.False(message.HasExtension(DefaultUint32Extension));
+            Assert.False(message.HasExtension(DefaultUint64Extension));
+        }
+
+        [Test]
+        public void FieldPresence()
+        {
+            var message = new TestAllTypes();
+
+            Assert.False(message.HasOptionalBool);
+            Assert.False(message.OptionalBool);
+
+            message.OptionalBool = true;
+
+            Assert.True(message.HasOptionalBool);
+            Assert.True(message.OptionalBool);
+
+            message.OptionalBool = false;
+
+            Assert.True(message.HasOptionalBool);
+            Assert.False(message.OptionalBool);
+
+            message.ClearOptionalBool();
+
+            Assert.False(message.HasOptionalBool);
+            Assert.False(message.OptionalBool);
+
+            Assert.False(message.HasDefaultBool);
+            Assert.True(message.DefaultBool);
+
+            message.DefaultBool = false;
+
+            Assert.True(message.HasDefaultBool);
+            Assert.False(message.DefaultBool);
+
+            message.DefaultBool = true;
+
+            Assert.True(message.HasDefaultBool);
+            Assert.True(message.DefaultBool);
+
+            message.ClearDefaultBool();
+
+            Assert.False(message.HasDefaultBool);
+            Assert.True(message.DefaultBool);
+        }
+
+        [Test]
+        public void RequiredFields()
+        {
+            var message = new TestRequired();
+            Assert.False(message.IsInitialized());
+
+            message.A = 1;
+            message.B = 2;
+            message.C = 3;
+
+            Assert.True(message.IsInitialized());
+        }
+
+        /// <summary>
+        /// Code was accidentally left in message parser that threw exceptions when missing required fields after parsing.
+        /// We've decided to not throw exceptions on missing fields, instead leaving it up to the consumer how they
+        /// want to check and handle missing fields.
+        /// </summary>
+        [Test]
+        public void RequiredFieldsNoThrow()
+        {
+            Assert.DoesNotThrow(() => MessageParsingHelpers.AssertReadingMessage(TestRequired.Parser, new byte[0], m => { }));
+            Assert.DoesNotThrow(() => MessageParsingHelpers.AssertReadingMessage(TestRequired.Parser as MessageParser, new byte[0], m => { }));
+        }
+
+        [Test]
+        public void RequiredFieldsInExtensions()
+        {
+            var message = new TestAllExtensions();
+            Assert.True(message.IsInitialized());
+
+            message.SetExtension(TestRequired.Extensions.Single, new TestRequired());
+
+            Assert.False(message.IsInitialized());
+
+            var extensionMessage = message.GetExtension(TestRequired.Extensions.Single);
+            extensionMessage.A = 1;
+            extensionMessage.B = 2;
+            extensionMessage.C = 3;
+
+            Assert.True(message.IsInitialized());
+
+            message.GetOrInitializeExtension(TestRequired.Extensions.Multi);
+
+            Assert.True(message.IsInitialized());
+
+            message.GetExtension(TestRequired.Extensions.Multi).Add(new TestRequired());
+
+            Assert.False(message.IsInitialized());
+
+            extensionMessage = message.GetExtension(TestRequired.Extensions.Multi)[0];
+            extensionMessage.A = 1;
+            extensionMessage.B = 2;
+            extensionMessage.C = 3;
+
+            Assert.True(message.IsInitialized());
+
+            message.SetExtension(UnittestExtensions.OptionalBoolExtension, true);
+
+            Assert.True(message.IsInitialized());
+
+            message.GetOrInitializeExtension(UnittestExtensions.RepeatedBoolExtension).Add(true);
+
+            Assert.True(message.IsInitialized());
+        }
+
+        [Test]
+        public void RequiredFieldInNestedMessageMapValue()
+        {
+            var message = new TestRequiredMap();
+            message.Foo.Add(0, new TestRequiredMap.Types.NestedMessage());
+
+            Assert.False(message.IsInitialized());
+
+            message.Foo[0].RequiredInt32 = 12;
+
+            Assert.True(message.IsInitialized());
+        }
+
+        [Test]
+        public void RoundTrip_Groups()
+        {
+            var message = new TestAllTypes
+            {
+                OptionalGroup = new TestAllTypes.Types.OptionalGroup
+                {
+                    A = 10
+                },
+                RepeatedGroup =
+                {
+                    new TestAllTypes.Types.RepeatedGroup { A = 10 },
+                    new TestAllTypes.Types.RepeatedGroup { A = 20 },
+                    new TestAllTypes.Types.RepeatedGroup { A = 30 }
+                }
+            };
+
+            MessageParsingHelpers.AssertWritingMessage(message);
+
+            MessageParsingHelpers.AssertRoundtrip(Proto2.TestAllTypes.Parser, message);
+        }
+
+        [Test]
+        public void RoundTrip_ExtensionGroups()
+        {
+            var message = new TestAllExtensions();
+            message.SetExtension(UnittestExtensions.OptionalGroupExtension, new OptionalGroup_extension { A = 10 });
+            message.GetOrInitializeExtension(UnittestExtensions.RepeatedGroupExtension).AddRange(new[]
+            {
+                new RepeatedGroup_extension { A = 10 },
+                new RepeatedGroup_extension { A = 20 },
+                new RepeatedGroup_extension { A = 30 }
+            });
+
+            MessageParsingHelpers.AssertWritingMessage(message);
+
+            MessageParsingHelpers.AssertRoundtrip(
+                TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { UnittestExtensions.OptionalGroupExtension, UnittestExtensions.RepeatedGroupExtension }),
+                message);
+        }
+
+        [Test]
+        public void RoundTrip_NestedExtensionGroup()
+        {
+            var message = new TestGroupExtension();
+            message.SetExtension(TestNestedExtension.Extensions.OptionalGroupExtension, new TestNestedExtension.Types.OptionalGroup_extension { A = 10 });
+
+            MessageParsingHelpers.AssertWritingMessage(message);
+            
+            MessageParsingHelpers.AssertRoundtrip(
+                TestGroupExtension.Parser.WithExtensionRegistry(new ExtensionRegistry() { TestNestedExtension.Extensions.OptionalGroupExtension }),
+                message);
+        }
+
+        [Test]
+        public void RoundTrip_ParseUsingCodedInput()
+        {
+            var message = new TestAllExtensions();
+            message.SetExtension(UnittestExtensions.OptionalBoolExtension, true);
+            byte[] bytes = message.ToByteArray();
+            using (CodedInputStream input = new CodedInputStream(bytes))
+            {
+                var parsed = TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { UnittestExtensions.OptionalBoolExtension }).ParseFrom(input);
+                Assert.AreEqual(message, parsed);
+            }
+        }
+    }
+}
diff --git a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
index 06af5e9..41a0b91 100644
--- a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
+++ b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
@@ -1,799 +1,799 @@
-#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.IO;

-using Google.Protobuf.TestProtos;

-using Proto2 = Google.Protobuf.TestProtos.Proto2;

-using NUnit.Framework;

-using System.Collections;

-using System.Collections.Generic;

-using System.Linq;

-using Google.Protobuf.WellKnownTypes;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Tests around the generated TestAllTypes message.

-    /// </summary>

-    public partial class GeneratedMessageTest

-    {

-        [Test]

-        public void EmptyMessageFieldDistinctFromMissingMessageField()

-        {

-            // This demonstrates what we're really interested in...

-            var message1 = new TestAllTypes { SingleForeignMessage = new ForeignMessage() };

-            var message2 = new TestAllTypes(); // SingleForeignMessage is null

-            EqualityTester.AssertInequality(message1, message2);

-        }

-

-        [Test]

-        public void DefaultValues()

-        {

-            // Single fields

-            var message = new TestAllTypes();

-            Assert.AreEqual(false, message.SingleBool);

-            Assert.AreEqual(ByteString.Empty, message.SingleBytes);

-            Assert.AreEqual(0.0, message.SingleDouble);

-            Assert.AreEqual(0, message.SingleFixed32);

-            Assert.AreEqual(0L, message.SingleFixed64);

-            Assert.AreEqual(0.0f, message.SingleFloat);

-            Assert.AreEqual(ForeignEnum.ForeignUnspecified, message.SingleForeignEnum);

-            Assert.IsNull(message.SingleForeignMessage);

-            Assert.AreEqual(ImportEnum.Unspecified, message.SingleImportEnum);

-            Assert.IsNull(message.SingleImportMessage);

-            Assert.AreEqual(0, message.SingleInt32);

-            Assert.AreEqual(0L, message.SingleInt64);

-            Assert.AreEqual(TestAllTypes.Types.NestedEnum.Unspecified, message.SingleNestedEnum);

-            Assert.IsNull(message.SingleNestedMessage);

-            Assert.IsNull(message.SinglePublicImportMessage);

-            Assert.AreEqual(0, message.SingleSfixed32);

-            Assert.AreEqual(0L, message.SingleSfixed64);

-            Assert.AreEqual(0, message.SingleSint32);

-            Assert.AreEqual(0L, message.SingleSint64);

-            Assert.AreEqual("", message.SingleString);

-            Assert.AreEqual(0U, message.SingleUint32);

-            Assert.AreEqual(0UL, message.SingleUint64);

-

-            // Repeated fields

-            Assert.AreEqual(0, message.RepeatedBool.Count);

-            Assert.AreEqual(0, message.RepeatedBytes.Count);

-            Assert.AreEqual(0, message.RepeatedDouble.Count);

-            Assert.AreEqual(0, message.RepeatedFixed32.Count);

-            Assert.AreEqual(0, message.RepeatedFixed64.Count);

-            Assert.AreEqual(0, message.RepeatedFloat.Count);

-            Assert.AreEqual(0, message.RepeatedForeignEnum.Count);

-            Assert.AreEqual(0, message.RepeatedForeignMessage.Count);

-            Assert.AreEqual(0, message.RepeatedImportEnum.Count);

-            Assert.AreEqual(0, message.RepeatedImportMessage.Count);

-            Assert.AreEqual(0, message.RepeatedNestedEnum.Count);

-            Assert.AreEqual(0, message.RepeatedNestedMessage.Count);

-            Assert.AreEqual(0, message.RepeatedPublicImportMessage.Count);

-            Assert.AreEqual(0, message.RepeatedSfixed32.Count);

-            Assert.AreEqual(0, message.RepeatedSfixed64.Count);

-            Assert.AreEqual(0, message.RepeatedSint32.Count);

-            Assert.AreEqual(0, message.RepeatedSint64.Count);

-            Assert.AreEqual(0, message.RepeatedString.Count);

-            Assert.AreEqual(0, message.RepeatedUint32.Count);

-            Assert.AreEqual(0, message.RepeatedUint64.Count);

-

-            // Oneof fields

-            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);

-            Assert.AreEqual(0, message.OneofUint32);

-            Assert.AreEqual("", message.OneofString);

-            Assert.AreEqual(ByteString.Empty, message.OneofBytes);

-            Assert.IsNull(message.OneofNestedMessage);

-        }

-

-        [Test]

-        public void NullStringAndBytesRejected()

-        {

-            var message = new TestAllTypes();

-            Assert.Throws<ArgumentNullException>(() => message.SingleString = null);

-            Assert.Throws<ArgumentNullException>(() => message.OneofString = null);

-            Assert.Throws<ArgumentNullException>(() => message.SingleBytes = null);

-            Assert.Throws<ArgumentNullException>(() => message.OneofBytes = null);

-        }

-

-        [Test]

-        public void RoundTrip_Empty()

-        {

-            var message = new TestAllTypes();

-            // Without setting any values, there's nothing to write.

-            byte[] bytes = message.ToByteArray();

-            Assert.AreEqual(0, bytes.Length);

-

-            MessageParsingHelpers.AssertWritingMessage(message);

-

-            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);

-        }

-

-        [Test]

-        public void RoundTrip_SingleValues()

-        {

-            var message = new TestAllTypes

-            {

-                SingleBool = true,

-                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),

-                SingleDouble = 23.5,

-                SingleFixed32 = 23,

-                SingleFixed64 = 1234567890123,

-                SingleFloat = 12.25f,

-                SingleForeignEnum = ForeignEnum.ForeignBar,

-                SingleForeignMessage = new ForeignMessage { C = 10 },

-                SingleImportEnum = ImportEnum.ImportBaz,

-                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

-            };

-

-            MessageParsingHelpers.AssertWritingMessage(message);

-

-            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);

-        }

-

-        [Test]

-        public void RoundTrip_RepeatedValues()

-        {

-            var message = new TestAllTypes

-            {

-                RepeatedBool = { true, false },

-                RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },

-                RepeatedDouble = { -12.25, 23.5 },

-                RepeatedFixed32 = { uint.MaxValue, 23 },

-                RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },

-                RepeatedFloat = { 100f, 12.25f },

-                RepeatedForeignEnum = { ForeignEnum.ForeignFoo, ForeignEnum.ForeignBar },

-                RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } },

-                RepeatedImportEnum = { ImportEnum.ImportBaz, ImportEnum.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 }

-            };

-

-            MessageParsingHelpers.AssertWritingMessage(message);

-

-            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);

-        }

-

-        // Note that not every map within map_unittest_proto3 is used. They all go through very

-        // similar code paths. The fact that all maps are present is validation that we have codecs

-        // for every type.

-        [Test]

-        public void RoundTrip_Maps()

-        {

-            var message = new TestMap

-            {

-                MapBoolBool = {

-                    { false, true },

-                    { true, false }

-                },

-                MapInt32Bytes = {

-                    { 5, ByteString.CopyFrom(6, 7, 8) },

-                    { 25, ByteString.CopyFrom(1, 2, 3, 4, 5) },

-                    { 10, ByteString.Empty }

-                },

-                MapInt32ForeignMessage = {

-                    { 0, new ForeignMessage { C = 10 } },

-                    { 5, new ForeignMessage() },

-                },

-                MapInt32Enum = {

-                    { 1, MapEnum.Bar },

-                    { 2000, MapEnum.Foo }

-                }

-            };

-

-            MessageParsingHelpers.AssertWritingMessage(message);

-

-            MessageParsingHelpers.AssertRoundtrip(TestMap.Parser, message);

-        }

-

-        [Test]

-        public void MapWithEmptyEntry()

-        {

-            var message = new TestMap

-            {

-                MapInt32Bytes = { { 0, ByteString.Empty } }

-            };

-

-            byte[] bytes = message.ToByteArray();

-            Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)

-

-            MessageParsingHelpers.AssertWritingMessage(message);

-

-            MessageParsingHelpers.AssertReadingMessage(

-                TestMap.Parser,

-                bytes,

-                parsed=>

-                {

-                    Assert.AreEqual(1, parsed.MapInt32Bytes.Count);

-                    Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);

-                });

-        }

-

-        [Test]

-        public void MapWithOnlyValue()

-        {

-            // Hand-craft the stream to contain a single entry with just a value.

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-            output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);

-            var nestedMessage = new ForeignMessage { C = 20 };

-            // Size of the entry (tag, size written by WriteMessage, data written by WriteMessage)

-            output.WriteLength(2 + nestedMessage.CalculateSize());

-            output.WriteTag(2, WireFormat.WireType.LengthDelimited);

-            output.WriteMessage(nestedMessage);

-            output.Flush();

-

-            MessageParsingHelpers.AssertReadingMessage(

-                TestMap.Parser,

-                memoryStream.ToArray(),

-                parsed =>

-                {

-                    Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);

-                });

-        }

-

-        [Test]

-        public void MapWithOnlyKey_PrimitiveValue()

-        {

-            // Hand-craft the stream to contain a single entry with just a key.

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-            output.WriteTag(TestMap.MapInt32DoubleFieldNumber, WireFormat.WireType.LengthDelimited);

-            int key = 10;

-            output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key);

-            output.Flush();

-

-            MessageParsingHelpers.AssertReadingMessage(

-                TestMap.Parser,

-                memoryStream.ToArray(),

-                parsed =>

-                {

-                    Assert.AreEqual(0.0, parsed.MapInt32Double[key]);

-                });

-        }

-

-        [Test]

-        public void MapWithOnlyKey_MessageValue()

-        {

-            // Hand-craft the stream to contain a single entry with just a key.

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-            output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);

-            int key = 10;

-            output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key);

-            output.Flush();

-

-            MessageParsingHelpers.AssertReadingMessage(

-                TestMap.Parser,

-                memoryStream.ToArray(),

-                parsed =>

-                {

-                    Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);

-                });

-        }

-

-        [Test]

-        public void MapIgnoresExtraFieldsWithinEntryMessages()

-        {

-            // Hand-craft the stream to contain a single entry with three fields

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-

-            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);

-

-            var key = 10; // Field 1 

-            var value = 20; // Field 2

-            var extra = 30; // Field 3

-

-            // Each field can be represented in a single byte, with a single byte tag.

-            // Total message size: 6 bytes.

-            output.WriteLength(6);

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key);

-            output.WriteTag(2, WireFormat.WireType.Varint);

-            output.WriteInt32(value);

-            output.WriteTag(3, WireFormat.WireType.Varint);

-            output.WriteInt32(extra);

-            output.Flush();

-

-            MessageParsingHelpers.AssertReadingMessage(

-                TestMap.Parser,

-                memoryStream.ToArray(),

-                parsed =>

-                {

-                    Assert.AreEqual(value, parsed.MapInt32Int32[key]);

-                });

-        }

-

-        [Test]

-        public void MapFieldOrderIsIrrelevant()

-        {

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-

-            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);

-

-            var key = 10;

-            var value = 20;

-

-            // Each field can be represented in a single byte, with a single byte tag.

-            // Total message size: 4 bytes.

-            output.WriteLength(4);

-            output.WriteTag(2, WireFormat.WireType.Varint);

-            output.WriteInt32(value);

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key);

-            output.Flush();

-

-            MessageParsingHelpers.AssertReadingMessage(

-                TestMap.Parser,

-                memoryStream.ToArray(),

-                parsed =>

-                {

-                    Assert.AreEqual(value, parsed.MapInt32Int32[key]);

-                });

-        }

-

-        [Test]

-        public void MapNonContiguousEntries()

-        {

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-

-            // Message structure:

-            // Entry for MapInt32Int32

-            // Entry for MapStringString

-            // Entry for MapInt32Int32

-

-            // First entry

-            var key1 = 10;

-            var value1 = 20;

-            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteLength(4);

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key1);

-            output.WriteTag(2, WireFormat.WireType.Varint);

-            output.WriteInt32(value1);

-

-            // Second entry

-            var key2 = "a";

-            var value2 = "b";

-            output.WriteTag(TestMap.MapStringStringFieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteLength(6); // 3 bytes per entry: tag, size, character

-            output.WriteTag(1, WireFormat.WireType.LengthDelimited);

-            output.WriteString(key2);

-            output.WriteTag(2, WireFormat.WireType.LengthDelimited);

-            output.WriteString(value2);

-

-            // Third entry

-            var key3 = 15;

-            var value3 = 25;

-            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteLength(4);

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key3);

-            output.WriteTag(2, WireFormat.WireType.Varint);

-            output.WriteInt32(value3);

-

-            output.Flush();

-

-            MessageParsingHelpers.AssertReadingMessage(

-                TestMap.Parser,

-                memoryStream.ToArray(),

-                parsed =>

-                {

-                    var expected = new TestMap

-                    {

-                        MapInt32Int32 = { { key1, value1 }, { key3, value3 } },

-                        MapStringString = { { key2, value2 } }

-                    };

-                    Assert.AreEqual(expected, parsed);

-                });

-        }

-

-        [Test]

-        public void DuplicateKeys_LastEntryWins()

-        {

-            var memoryStream = new MemoryStream();

-            var output = new CodedOutputStream(memoryStream);

-

-            var key = 10;

-            var value1 = 20;

-            var value2 = 30;

-

-            // First entry

-            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteLength(4);

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key);

-            output.WriteTag(2, WireFormat.WireType.Varint);

-            output.WriteInt32(value1);

-

-            // Second entry - same key, different value

-            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteLength(4);

-            output.WriteTag(1, WireFormat.WireType.Varint);

-            output.WriteInt32(key);

-            output.WriteTag(2, WireFormat.WireType.Varint);

-            output.WriteInt32(value2);

-            output.Flush();

-

-            MessageParsingHelpers.AssertReadingMessage(

-                TestMap.Parser,

-                memoryStream.ToArray(),

-                parsed =>

-                {

-                    Assert.AreEqual(value2, parsed.MapInt32Int32[key]);

-                });

-        }

-

-        [Test]

-        public void CloneSingleNonMessageValues()

-        {

-            var original = new TestAllTypes

-            {

-                SingleBool = true,

-                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),

-                SingleDouble = 23.5,

-                SingleFixed32 = 23,

-                SingleFixed64 = 1234567890123,

-                SingleFloat = 12.25f,

-                SingleInt32 = 100,

-                SingleInt64 = 3210987654321,

-                SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,

-                SingleSfixed32 = -123,

-                SingleSfixed64 = -12345678901234,

-                SingleSint32 = -456,

-                SingleSint64 = -12345678901235,

-                SingleString = "test",

-                SingleUint32 = uint.MaxValue,

-                SingleUint64 = ulong.MaxValue

-            };

-            var clone = original.Clone();

-            Assert.AreNotSame(original, clone);

-            Assert.AreEqual(original, clone);

-            // Just as a single example

-            clone.SingleInt32 = 150;

-            Assert.AreNotEqual(original, clone);

-        }

-

-        [Test]

-        public void CloneRepeatedNonMessageValues()

-        {

-            var original = new TestAllTypes

-            {

-                RepeatedBool = { true, false },

-                RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },

-                RepeatedDouble = { -12.25, 23.5 },

-                RepeatedFixed32 = { uint.MaxValue, 23 },

-                RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },

-                RepeatedFloat = { 100f, 12.25f },

-                RepeatedInt32 = { 100, 200 },

-                RepeatedInt64 = { 3210987654321, long.MaxValue },

-                RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },

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

-            };

-

-            var clone = original.Clone();

-            Assert.AreNotSame(original, clone);

-            Assert.AreEqual(original, clone);

-            // Just as a single example

-            clone.RepeatedDouble.Add(25.5);

-            Assert.AreNotEqual(original, clone);

-        }

-

-        [Test]

-        public void CloneSingleMessageField()

-        {

-            var original = new TestAllTypes

-            {

-                SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }

-            };

-

-            var clone = original.Clone();

-            Assert.AreNotSame(original, clone);

-            Assert.AreNotSame(original.SingleNestedMessage, clone.SingleNestedMessage);

-            Assert.AreEqual(original, clone);

-

-            clone.SingleNestedMessage.Bb = 30;

-            Assert.AreNotEqual(original, clone);

-        }

-

-        [Test]

-        public void CloneRepeatedMessageField()

-        {

-            var original = new TestAllTypes

-            {

-                RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 20 } }

-            };

-

-            var clone = original.Clone();

-            Assert.AreNotSame(original, clone);

-            Assert.AreNotSame(original.RepeatedNestedMessage, clone.RepeatedNestedMessage);

-            Assert.AreNotSame(original.RepeatedNestedMessage[0], clone.RepeatedNestedMessage[0]);

-            Assert.AreEqual(original, clone);

-

-            clone.RepeatedNestedMessage[0].Bb = 30;

-            Assert.AreNotEqual(original, clone);

-        }

-

-        [Test]

-        public void CloneOneofField()

-        {

-            var original = new TestAllTypes

-            {

-                OneofNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }

-            };

-

-            var clone = original.Clone();

-            Assert.AreNotSame(original, clone);

-            Assert.AreEqual(original, clone);

-

-            // We should have cloned the message

-            original.OneofNestedMessage.Bb = 30;

-            Assert.AreNotEqual(original, clone);

-        }

-

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

-        {

-            var message1 = new TestAllTypes { OneofString = "" };

-            var message2 = new TestAllTypes { OneofUint32 = 0 };

-            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message1.OneofFieldCase);

-            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);

-            Assert.AreNotEqual(message1, message2);

-        }

-

-        [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!

-

-            MessageParsingHelpers.AssertWritingMessage(message);

-

-            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>

-            {

-                Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.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

-

-            MessageParsingHelpers.AssertWritingMessage(message);

-

-            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>

-            {

-                Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);

-            });

-        }

-

-        [Test]

-        public void DiscardUnknownFields_RealDataStillRead()

-        {

-            var message = SampleMessages.CreateFullTestAllTypes();

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-            var unusedFieldNumber = 23456;

-            Assert.IsFalse(TestAllTypes.Descriptor.Fields.InDeclarationOrder().Select(x => x.FieldNumber).Contains(unusedFieldNumber));

-            output.WriteTag(unusedFieldNumber, WireFormat.WireType.LengthDelimited);

-            output.WriteString("ignore me");

-            message.WriteTo(output);

-            output.Flush();

-

-            MessageParsingHelpers.AssertReadingMessage(

-                TestAllTypes.Parser,

-                stream.ToArray(),

-                parsed =>

-                {

-                    // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.

-                    // Assert.AreEqual(message, parsed);

-                });

-        }

-

-        [Test]

-        public void DiscardUnknownFields_AllTypes()

-        {

-            // Simple way of ensuring we can skip all kinds of fields.

-            var data = SampleMessages.CreateFullTestAllTypes().ToByteArray();

-            var empty = Empty.Parser.ParseFrom(data);

-

-            MessageParsingHelpers.AssertReadingMessage(

-                Empty.Parser,

-                data,

-                parsed =>

-                {

-                    // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.

-                    // Assert.AreNotEqual(new Empty(), empty);

-                });

-        }

-

-        // This was originally seen as a conformance test failure.

-        [Test]

-        public void TruncatedMessageFieldThrows()

-        {

-            // 130, 3 is the message tag

-            // 1 is the data length - but there's no data.

-            var data = new byte[] { 130, 3, 1 };

-            MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, data);

-        }

-

-        /// <summary>

-        /// Demonstrates current behaviour with an extraneous end group tag - see issue 688

-        /// for details; we may want to change this.

-        /// </summary>

-        [Test]

-        public void ExtraEndGroupThrows()

-        {

-            var message = SampleMessages.CreateFullTestAllTypes();

-            var stream = new MemoryStream();

-            var output = new CodedOutputStream(stream);

-

-            output.WriteTag(TestAllTypes.SingleFixed32FieldNumber, WireFormat.WireType.Fixed32);

-            output.WriteFixed32(123);

-            output.WriteTag(100, WireFormat.WireType.EndGroup);

-

-            output.Flush();

-

-            stream.Position = 0;

-            MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, stream.ToArray());

-        }

-

-        [Test]

-        public void CustomDiagnosticMessage_DirectToStringCall()

-        {

-            var message = new ForeignMessage { C = 31 };

-            Assert.AreEqual("{ \"c\": 31, \"@cInHex\": \"1f\" }", message.ToString());

-            Assert.AreEqual("{ \"c\": 31 }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void CustomDiagnosticMessage_Nested()

-        {

-            var message = new TestAllTypes { SingleForeignMessage = new ForeignMessage { C = 16 } };

-            Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16, \"@cInHex\": \"10\" } }", message.ToString());

-            Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16 } }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void CustomDiagnosticMessage_DirectToTextWriterCall()

-        {

-            var message = new ForeignMessage { C = 31 };

-            var writer = new StringWriter();

-            JsonFormatter.Default.Format(message, writer);

-            Assert.AreEqual("{ \"c\": 31 }", writer.ToString());

-        }

-

-        [Test]

-        public void NaNComparisons()

-        {

-            var message1 = new TestAllTypes { SingleDouble = SampleNaNs.Regular };

-            var message2 = new TestAllTypes { SingleDouble = SampleNaNs.PayloadFlipped };

-            var message3 = new TestAllTypes { SingleDouble = SampleNaNs.Regular };

-

-            EqualityTester.AssertInequality(message1, message2);

-            EqualityTester.AssertEquality(message1, message3);

-        }

-    }

+#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.IO;
+using Google.Protobuf.TestProtos;
+using Proto2 = Google.Protobuf.TestProtos.Proto2;
+using NUnit.Framework;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Google.Protobuf.WellKnownTypes;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Tests around the generated TestAllTypes message.
+    /// </summary>
+    public partial class GeneratedMessageTest
+    {
+        [Test]
+        public void EmptyMessageFieldDistinctFromMissingMessageField()
+        {
+            // This demonstrates what we're really interested in...
+            var message1 = new TestAllTypes { SingleForeignMessage = new ForeignMessage() };
+            var message2 = new TestAllTypes(); // SingleForeignMessage is null
+            EqualityTester.AssertInequality(message1, message2);
+        }
+
+        [Test]
+        public void DefaultValues()
+        {
+            // Single fields
+            var message = new TestAllTypes();
+            Assert.AreEqual(false, message.SingleBool);
+            Assert.AreEqual(ByteString.Empty, message.SingleBytes);
+            Assert.AreEqual(0.0, message.SingleDouble);
+            Assert.AreEqual(0, message.SingleFixed32);
+            Assert.AreEqual(0L, message.SingleFixed64);
+            Assert.AreEqual(0.0f, message.SingleFloat);
+            Assert.AreEqual(ForeignEnum.ForeignUnspecified, message.SingleForeignEnum);
+            Assert.IsNull(message.SingleForeignMessage);
+            Assert.AreEqual(ImportEnum.Unspecified, message.SingleImportEnum);
+            Assert.IsNull(message.SingleImportMessage);
+            Assert.AreEqual(0, message.SingleInt32);
+            Assert.AreEqual(0L, message.SingleInt64);
+            Assert.AreEqual(TestAllTypes.Types.NestedEnum.Unspecified, message.SingleNestedEnum);
+            Assert.IsNull(message.SingleNestedMessage);
+            Assert.IsNull(message.SinglePublicImportMessage);
+            Assert.AreEqual(0, message.SingleSfixed32);
+            Assert.AreEqual(0L, message.SingleSfixed64);
+            Assert.AreEqual(0, message.SingleSint32);
+            Assert.AreEqual(0L, message.SingleSint64);
+            Assert.AreEqual("", message.SingleString);
+            Assert.AreEqual(0U, message.SingleUint32);
+            Assert.AreEqual(0UL, message.SingleUint64);
+
+            // Repeated fields
+            Assert.AreEqual(0, message.RepeatedBool.Count);
+            Assert.AreEqual(0, message.RepeatedBytes.Count);
+            Assert.AreEqual(0, message.RepeatedDouble.Count);
+            Assert.AreEqual(0, message.RepeatedFixed32.Count);
+            Assert.AreEqual(0, message.RepeatedFixed64.Count);
+            Assert.AreEqual(0, message.RepeatedFloat.Count);
+            Assert.AreEqual(0, message.RepeatedForeignEnum.Count);
+            Assert.AreEqual(0, message.RepeatedForeignMessage.Count);
+            Assert.AreEqual(0, message.RepeatedImportEnum.Count);
+            Assert.AreEqual(0, message.RepeatedImportMessage.Count);
+            Assert.AreEqual(0, message.RepeatedNestedEnum.Count);
+            Assert.AreEqual(0, message.RepeatedNestedMessage.Count);
+            Assert.AreEqual(0, message.RepeatedPublicImportMessage.Count);
+            Assert.AreEqual(0, message.RepeatedSfixed32.Count);
+            Assert.AreEqual(0, message.RepeatedSfixed64.Count);
+            Assert.AreEqual(0, message.RepeatedSint32.Count);
+            Assert.AreEqual(0, message.RepeatedSint64.Count);
+            Assert.AreEqual(0, message.RepeatedString.Count);
+            Assert.AreEqual(0, message.RepeatedUint32.Count);
+            Assert.AreEqual(0, message.RepeatedUint64.Count);
+
+            // Oneof fields
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
+            Assert.AreEqual(0, message.OneofUint32);
+            Assert.AreEqual("", message.OneofString);
+            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+            Assert.IsNull(message.OneofNestedMessage);
+        }
+
+        [Test]
+        public void NullStringAndBytesRejected()
+        {
+            var message = new TestAllTypes();
+            Assert.Throws<ArgumentNullException>(() => message.SingleString = null);
+            Assert.Throws<ArgumentNullException>(() => message.OneofString = null);
+            Assert.Throws<ArgumentNullException>(() => message.SingleBytes = null);
+            Assert.Throws<ArgumentNullException>(() => message.OneofBytes = null);
+        }
+
+        [Test]
+        public void RoundTrip_Empty()
+        {
+            var message = new TestAllTypes();
+            // Without setting any values, there's nothing to write.
+            byte[] bytes = message.ToByteArray();
+            Assert.AreEqual(0, bytes.Length);
+
+            MessageParsingHelpers.AssertWritingMessage(message);
+
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
+        }
+
+        [Test]
+        public void RoundTrip_SingleValues()
+        {
+            var message = new TestAllTypes
+            {
+                SingleBool = true,
+                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
+                SingleDouble = 23.5,
+                SingleFixed32 = 23,
+                SingleFixed64 = 1234567890123,
+                SingleFloat = 12.25f,
+                SingleForeignEnum = ForeignEnum.ForeignBar,
+                SingleForeignMessage = new ForeignMessage { C = 10 },
+                SingleImportEnum = ImportEnum.ImportBaz,
+                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
+            };
+
+            MessageParsingHelpers.AssertWritingMessage(message);
+
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
+        }
+
+        [Test]
+        public void RoundTrip_RepeatedValues()
+        {
+            var message = new TestAllTypes
+            {
+                RepeatedBool = { true, false },
+                RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
+                RepeatedDouble = { -12.25, 23.5 },
+                RepeatedFixed32 = { uint.MaxValue, 23 },
+                RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
+                RepeatedFloat = { 100f, 12.25f },
+                RepeatedForeignEnum = { ForeignEnum.ForeignFoo, ForeignEnum.ForeignBar },
+                RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } },
+                RepeatedImportEnum = { ImportEnum.ImportBaz, ImportEnum.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 }
+            };
+
+            MessageParsingHelpers.AssertWritingMessage(message);
+
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
+        }
+
+        // Note that not every map within map_unittest_proto3 is used. They all go through very
+        // similar code paths. The fact that all maps are present is validation that we have codecs
+        // for every type.
+        [Test]
+        public void RoundTrip_Maps()
+        {
+            var message = new TestMap
+            {
+                MapBoolBool = {
+                    { false, true },
+                    { true, false }
+                },
+                MapInt32Bytes = {
+                    { 5, ByteString.CopyFrom(6, 7, 8) },
+                    { 25, ByteString.CopyFrom(1, 2, 3, 4, 5) },
+                    { 10, ByteString.Empty }
+                },
+                MapInt32ForeignMessage = {
+                    { 0, new ForeignMessage { C = 10 } },
+                    { 5, new ForeignMessage() },
+                },
+                MapInt32Enum = {
+                    { 1, MapEnum.Bar },
+                    { 2000, MapEnum.Foo }
+                }
+            };
+
+            MessageParsingHelpers.AssertWritingMessage(message);
+
+            MessageParsingHelpers.AssertRoundtrip(TestMap.Parser, message);
+        }
+
+        [Test]
+        public void MapWithEmptyEntry()
+        {
+            var message = new TestMap
+            {
+                MapInt32Bytes = { { 0, ByteString.Empty } }
+            };
+
+            byte[] bytes = message.ToByteArray();
+            Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)
+
+            MessageParsingHelpers.AssertWritingMessage(message);
+
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                bytes,
+                parsed=>
+                {
+                    Assert.AreEqual(1, parsed.MapInt32Bytes.Count);
+                    Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);
+                });
+        }
+
+        [Test]
+        public void MapWithOnlyValue()
+        {
+            // Hand-craft the stream to contain a single entry with just a value.
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+            output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
+            var nestedMessage = new ForeignMessage { C = 20 };
+            // Size of the entry (tag, size written by WriteMessage, data written by WriteMessage)
+            output.WriteLength(2 + nestedMessage.CalculateSize());
+            output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+            output.WriteMessage(nestedMessage);
+            output.Flush();
+
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
+                });
+        }
+
+        [Test]
+        public void MapWithOnlyKey_PrimitiveValue()
+        {
+            // Hand-craft the stream to contain a single entry with just a key.
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+            output.WriteTag(TestMap.MapInt32DoubleFieldNumber, WireFormat.WireType.LengthDelimited);
+            int key = 10;
+            output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key);
+            output.Flush();
+
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
+                });
+        }
+
+        [Test]
+        public void MapWithOnlyKey_MessageValue()
+        {
+            // Hand-craft the stream to contain a single entry with just a key.
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+            output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
+            int key = 10;
+            output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key);
+            output.Flush();
+
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
+                });
+        }
+
+        [Test]
+        public void MapIgnoresExtraFieldsWithinEntryMessages()
+        {
+            // Hand-craft the stream to contain a single entry with three fields
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+
+            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+
+            var key = 10; // Field 1 
+            var value = 20; // Field 2
+            var extra = 30; // Field 3
+
+            // Each field can be represented in a single byte, with a single byte tag.
+            // Total message size: 6 bytes.
+            output.WriteLength(6);
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key);
+            output.WriteTag(2, WireFormat.WireType.Varint);
+            output.WriteInt32(value);
+            output.WriteTag(3, WireFormat.WireType.Varint);
+            output.WriteInt32(extra);
+            output.Flush();
+
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+                });
+        }
+
+        [Test]
+        public void MapFieldOrderIsIrrelevant()
+        {
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+
+            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+
+            var key = 10;
+            var value = 20;
+
+            // Each field can be represented in a single byte, with a single byte tag.
+            // Total message size: 4 bytes.
+            output.WriteLength(4);
+            output.WriteTag(2, WireFormat.WireType.Varint);
+            output.WriteInt32(value);
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key);
+            output.Flush();
+
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+                });
+        }
+
+        [Test]
+        public void MapNonContiguousEntries()
+        {
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+
+            // Message structure:
+            // Entry for MapInt32Int32
+            // Entry for MapStringString
+            // Entry for MapInt32Int32
+
+            // First entry
+            var key1 = 10;
+            var value1 = 20;
+            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteLength(4);
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key1);
+            output.WriteTag(2, WireFormat.WireType.Varint);
+            output.WriteInt32(value1);
+
+            // Second entry
+            var key2 = "a";
+            var value2 = "b";
+            output.WriteTag(TestMap.MapStringStringFieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteLength(6); // 3 bytes per entry: tag, size, character
+            output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+            output.WriteString(key2);
+            output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+            output.WriteString(value2);
+
+            // Third entry
+            var key3 = 15;
+            var value3 = 25;
+            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteLength(4);
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key3);
+            output.WriteTag(2, WireFormat.WireType.Varint);
+            output.WriteInt32(value3);
+
+            output.Flush();
+
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    var expected = new TestMap
+                    {
+                        MapInt32Int32 = { { key1, value1 }, { key3, value3 } },
+                        MapStringString = { { key2, value2 } }
+                    };
+                    Assert.AreEqual(expected, parsed);
+                });
+        }
+
+        [Test]
+        public void DuplicateKeys_LastEntryWins()
+        {
+            var memoryStream = new MemoryStream();
+            var output = new CodedOutputStream(memoryStream);
+
+            var key = 10;
+            var value1 = 20;
+            var value2 = 30;
+
+            // First entry
+            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteLength(4);
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key);
+            output.WriteTag(2, WireFormat.WireType.Varint);
+            output.WriteInt32(value1);
+
+            // Second entry - same key, different value
+            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteLength(4);
+            output.WriteTag(1, WireFormat.WireType.Varint);
+            output.WriteInt32(key);
+            output.WriteTag(2, WireFormat.WireType.Varint);
+            output.WriteInt32(value2);
+            output.Flush();
+
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(value2, parsed.MapInt32Int32[key]);
+                });
+        }
+
+        [Test]
+        public void CloneSingleNonMessageValues()
+        {
+            var original = new TestAllTypes
+            {
+                SingleBool = true,
+                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
+                SingleDouble = 23.5,
+                SingleFixed32 = 23,
+                SingleFixed64 = 1234567890123,
+                SingleFloat = 12.25f,
+                SingleInt32 = 100,
+                SingleInt64 = 3210987654321,
+                SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
+                SingleSfixed32 = -123,
+                SingleSfixed64 = -12345678901234,
+                SingleSint32 = -456,
+                SingleSint64 = -12345678901235,
+                SingleString = "test",
+                SingleUint32 = uint.MaxValue,
+                SingleUint64 = ulong.MaxValue
+            };
+            var clone = original.Clone();
+            Assert.AreNotSame(original, clone);
+            Assert.AreEqual(original, clone);
+            // Just as a single example
+            clone.SingleInt32 = 150;
+            Assert.AreNotEqual(original, clone);
+        }
+
+        [Test]
+        public void CloneRepeatedNonMessageValues()
+        {
+            var original = new TestAllTypes
+            {
+                RepeatedBool = { true, false },
+                RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
+                RepeatedDouble = { -12.25, 23.5 },
+                RepeatedFixed32 = { uint.MaxValue, 23 },
+                RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
+                RepeatedFloat = { 100f, 12.25f },
+                RepeatedInt32 = { 100, 200 },
+                RepeatedInt64 = { 3210987654321, long.MaxValue },
+                RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },
+                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 }
+            };
+
+            var clone = original.Clone();
+            Assert.AreNotSame(original, clone);
+            Assert.AreEqual(original, clone);
+            // Just as a single example
+            clone.RepeatedDouble.Add(25.5);
+            Assert.AreNotEqual(original, clone);
+        }
+
+        [Test]
+        public void CloneSingleMessageField()
+        {
+            var original = new TestAllTypes
+            {
+                SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
+            };
+
+            var clone = original.Clone();
+            Assert.AreNotSame(original, clone);
+            Assert.AreNotSame(original.SingleNestedMessage, clone.SingleNestedMessage);
+            Assert.AreEqual(original, clone);
+
+            clone.SingleNestedMessage.Bb = 30;
+            Assert.AreNotEqual(original, clone);
+        }
+
+        [Test]
+        public void CloneRepeatedMessageField()
+        {
+            var original = new TestAllTypes
+            {
+                RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 20 } }
+            };
+
+            var clone = original.Clone();
+            Assert.AreNotSame(original, clone);
+            Assert.AreNotSame(original.RepeatedNestedMessage, clone.RepeatedNestedMessage);
+            Assert.AreNotSame(original.RepeatedNestedMessage[0], clone.RepeatedNestedMessage[0]);
+            Assert.AreEqual(original, clone);
+
+            clone.RepeatedNestedMessage[0].Bb = 30;
+            Assert.AreNotEqual(original, clone);
+        }
+
+        [Test]
+        public void CloneOneofField()
+        {
+            var original = new TestAllTypes
+            {
+                OneofNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
+            };
+
+            var clone = original.Clone();
+            Assert.AreNotSame(original, clone);
+            Assert.AreEqual(original, clone);
+
+            // We should have cloned the message
+            original.OneofNestedMessage.Bb = 30;
+            Assert.AreNotEqual(original, clone);
+        }
+
+        [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 Oneof_DefaultValuesNotEqual()
+        {
+            var message1 = new TestAllTypes { OneofString = "" };
+            var message2 = new TestAllTypes { OneofUint32 = 0 };
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message1.OneofFieldCase);
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
+            Assert.AreNotEqual(message1, message2);
+        }
+
+        [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!
+
+            MessageParsingHelpers.AssertWritingMessage(message);
+
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
+            {
+                Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.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
+
+            MessageParsingHelpers.AssertWritingMessage(message);
+
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
+            {
+                Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);
+            });
+        }
+
+        [Test]
+        public void DiscardUnknownFields_RealDataStillRead()
+        {
+            var message = SampleMessages.CreateFullTestAllTypes();
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+            var unusedFieldNumber = 23456;
+            Assert.IsFalse(TestAllTypes.Descriptor.Fields.InDeclarationOrder().Select(x => x.FieldNumber).Contains(unusedFieldNumber));
+            output.WriteTag(unusedFieldNumber, WireFormat.WireType.LengthDelimited);
+            output.WriteString("ignore me");
+            message.WriteTo(output);
+            output.Flush();
+
+            MessageParsingHelpers.AssertReadingMessage(
+                TestAllTypes.Parser,
+                stream.ToArray(),
+                parsed =>
+                {
+                    // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
+                    // Assert.AreEqual(message, parsed);
+                });
+        }
+
+        [Test]
+        public void DiscardUnknownFields_AllTypes()
+        {
+            // Simple way of ensuring we can skip all kinds of fields.
+            var data = SampleMessages.CreateFullTestAllTypes().ToByteArray();
+            var empty = Empty.Parser.ParseFrom(data);
+
+            MessageParsingHelpers.AssertReadingMessage(
+                Empty.Parser,
+                data,
+                parsed =>
+                {
+                    // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
+                    // Assert.AreNotEqual(new Empty(), empty);
+                });
+        }
+
+        // This was originally seen as a conformance test failure.
+        [Test]
+        public void TruncatedMessageFieldThrows()
+        {
+            // 130, 3 is the message tag
+            // 1 is the data length - but there's no data.
+            var data = new byte[] { 130, 3, 1 };
+            MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, data);
+        }
+
+        /// <summary>
+        /// Demonstrates current behaviour with an extraneous end group tag - see issue 688
+        /// for details; we may want to change this.
+        /// </summary>
+        [Test]
+        public void ExtraEndGroupThrows()
+        {
+            var message = SampleMessages.CreateFullTestAllTypes();
+            var stream = new MemoryStream();
+            var output = new CodedOutputStream(stream);
+
+            output.WriteTag(TestAllTypes.SingleFixed32FieldNumber, WireFormat.WireType.Fixed32);
+            output.WriteFixed32(123);
+            output.WriteTag(100, WireFormat.WireType.EndGroup);
+
+            output.Flush();
+
+            stream.Position = 0;
+            MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, stream.ToArray());
+        }
+
+        [Test]
+        public void CustomDiagnosticMessage_DirectToStringCall()
+        {
+            var message = new ForeignMessage { C = 31 };
+            Assert.AreEqual("{ \"c\": 31, \"@cInHex\": \"1f\" }", message.ToString());
+            Assert.AreEqual("{ \"c\": 31 }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void CustomDiagnosticMessage_Nested()
+        {
+            var message = new TestAllTypes { SingleForeignMessage = new ForeignMessage { C = 16 } };
+            Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16, \"@cInHex\": \"10\" } }", message.ToString());
+            Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16 } }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void CustomDiagnosticMessage_DirectToTextWriterCall()
+        {
+            var message = new ForeignMessage { C = 31 };
+            var writer = new StringWriter();
+            JsonFormatter.Default.Format(message, writer);
+            Assert.AreEqual("{ \"c\": 31 }", writer.ToString());
+        }
+
+        [Test]
+        public void NaNComparisons()
+        {
+            var message1 = new TestAllTypes { SingleDouble = SampleNaNs.Regular };
+            var message2 = new TestAllTypes { SingleDouble = SampleNaNs.PayloadFlipped };
+            var message3 = new TestAllTypes { SingleDouble = SampleNaNs.Regular };
+
+            EqualityTester.AssertInequality(message1, message2);
+            EqualityTester.AssertEquality(message1, message3);
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj b/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
index 641cb0a..fe5ff80 100644
--- a/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
+++ b/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
@@ -14,14 +14,14 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
-    <PackageReference Include="NUnit" Version="3.9.0" />
-    <PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
+    <PackageReference Include="NUnit" Version="3.13.3" />
+    <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
   </ItemGroup>
 
   <!-- Needed for the net45 build to work on Unix. See https://github.com/dotnet/designs/pull/33 -->
   <ItemGroup>
-    <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
+    <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.2" PrivateAssets="All" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/csharp/src/Google.Protobuf.Test/IssuesTest.cs b/csharp/src/Google.Protobuf.Test/IssuesTest.cs
index 2904c46..6953989 100644
--- a/csharp/src/Google.Protobuf.Test/IssuesTest.cs
+++ b/csharp/src/Google.Protobuf.Test/IssuesTest.cs
@@ -1,116 +1,132 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using Google.Protobuf.Reflection;

-using UnitTest.Issues.TestProtos;

-using NUnit.Framework;

-using System.IO;

-using static UnitTest.Issues.TestProtos.OneofMerging.Types;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Tests for issues which aren't easily compartmentalized into other unit tests.

-    /// </summary>

-    public class IssuesTest

-    {

-        // Issue 45

-        [Test]

-        public void FieldCalledItem()

-        {

-            ItemField message = new ItemField { Item = 3 };

-            FieldDescriptor field = ItemField.Descriptor.FindFieldByName("item");

-            Assert.NotNull(field);

-            Assert.AreEqual(3, (int)field.Accessor.GetValue(message));

-        }

-

-        [Test]

-        public void ReservedNames()

-        {

-            var message = new ReservedNames { Types_ = 10, Descriptor_ = 20 };

-            // Underscores aren't reflected in the JSON.

-            Assert.AreEqual("{ \"types\": 10, \"descriptor\": 20 }", message.ToString());

-        }

-

-        [Test]

-        public void JsonNameParseTest()

-        {

-            var settings = new JsonParser.Settings(10, TypeRegistry.FromFiles(UnittestIssuesReflection.Descriptor));

-            var parser = new JsonParser(settings);

-

-            // It is safe to use either original field name or explicitly specified json_name

-            Assert.AreEqual(new TestJsonName { Name = "test", Description = "test2", Guid = "test3" },

-                parser.Parse<TestJsonName>("{ \"name\": \"test\", \"desc\": \"test2\", \"guid\": \"test3\" }"));

-        }

-

-        [Test]

-        public void JsonNameFormatTest()

-        {

-            var message = new TestJsonName { Name = "test", Description = "test2", Guid = "test3" };

-            Assert.AreEqual("{ \"name\": \"test\", \"desc\": \"test2\", \"exid\": \"test3\" }",

-                JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void OneofMerging()

-        {

-            var message1 = new OneofMerging { Nested = new Nested { X = 10 } };

-            var message2 = new OneofMerging { Nested = new Nested { Y = 20 } };

-            var expected = new OneofMerging { Nested = new Nested { X = 10, Y = 20 } };

-

-            var merged = message1.Clone();

-            merged.MergeFrom(message2);

-            Assert.AreEqual(expected, merged);

-        }

-

-        // Check that a tag immediately followed by end of limit can still be read.

-        [Test]

-        public void CodedInputStream_LimitReachedRightAfterTag()

-        {

-            MemoryStream ms = new MemoryStream();

-            var cos = new CodedOutputStream(ms);

-            cos.WriteTag(11, WireFormat.WireType.Varint);

-            Assert.AreEqual(1, cos.Position);

-            cos.WriteString("some extra padding");  // ensure is currentLimit distinct from the end of the buffer.

-            cos.Flush();

-

-            var cis = new CodedInputStream(ms.ToArray());

-            cis.PushLimit(1);  // make sure we reach the limit right after reading the tag.

-

-            // we still must read the tag correctly, even though the tag is at the very end of our limited input

-            // (which is a corner case and will most likely result in an error when trying to read value of the field

-            // described by this tag, but it would be a logical error not to read the tag that's actually present).

-            // See https://github.com/protocolbuffers/protobuf/pull/7289

-            cis.AssertNextTag(WireFormat.MakeTag(11, WireFormat.WireType.Varint));

-        }

-    }

-}

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using Google.Protobuf.Reflection;
+using UnitTest.Issues.TestProtos;
+using NUnit.Framework;
+using System.IO;
+using static UnitTest.Issues.TestProtos.OneofMerging.Types;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Tests for issues which aren't easily compartmentalized into other unit tests.
+    /// </summary>
+    public class IssuesTest
+    {
+        // Issue 45
+        [Test]
+        public void FieldCalledItem()
+        {
+            ItemField message = new ItemField { Item = 3 };
+            FieldDescriptor field = ItemField.Descriptor.FindFieldByName("item");
+            Assert.NotNull(field);
+            Assert.AreEqual(3, (int)field.Accessor.GetValue(message));
+        }
+
+        [Test]
+        public void ReservedNames()
+        {
+            var message = new ReservedNames { Types_ = 10, Descriptor_ = 20 };
+            // Underscores aren't reflected in the JSON.
+            Assert.AreEqual("{ \"types\": 10, \"descriptor\": 20 }", message.ToString());
+        }
+
+        [Test]
+        public void JsonNameParseTest()
+        {
+            var settings = new JsonParser.Settings(10, TypeRegistry.FromFiles(UnittestIssuesReflection.Descriptor));
+            var parser = new JsonParser(settings);
+
+            // It is safe to use either original field name or explicitly specified json_name
+            Assert.AreEqual(new TestJsonName { Name = "test", Description = "test2", Guid = "test3" },
+                parser.Parse<TestJsonName>("{ \"name\": \"test\", \"desc\": \"test2\", \"guid\": \"test3\" }"));
+        }
+
+        [Test]
+        public void JsonNameFormatTest()
+        {
+            var message = new TestJsonName { Name = "test", Description = "test2", Guid = "test3" };
+            Assert.AreEqual("{ \"name\": \"test\", \"desc\": \"test2\", \"exid\": \"test3\" }",
+                JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void OneofMerging()
+        {
+            var message1 = new OneofMerging { Nested = new Nested { X = 10 } };
+            var message2 = new OneofMerging { Nested = new Nested { Y = 20 } };
+            var expected = new OneofMerging { Nested = new Nested { X = 10, Y = 20 } };
+
+            var merged = message1.Clone();
+            merged.MergeFrom(message2);
+            Assert.AreEqual(expected, merged);
+        }
+
+        // Check that a tag immediately followed by end of limit can still be read.
+        [Test]
+        public void CodedInputStream_LimitReachedRightAfterTag()
+        {
+            MemoryStream ms = new MemoryStream();
+            var cos = new CodedOutputStream(ms);
+            cos.WriteTag(11, WireFormat.WireType.Varint);
+            Assert.AreEqual(1, cos.Position);
+            cos.WriteString("some extra padding");  // ensure is currentLimit distinct from the end of the buffer.
+            cos.Flush();
+
+            var cis = new CodedInputStream(ms.ToArray());
+            cis.PushLimit(1);  // make sure we reach the limit right after reading the tag.
+
+            // we still must read the tag correctly, even though the tag is at the very end of our limited input
+            // (which is a corner case and will most likely result in an error when trying to read value of the field
+            // described by this tag, but it would be a logical error not to read the tag that's actually present).
+            // See https://github.com/protocolbuffers/protobuf/pull/7289
+            cis.AssertNextTag(WireFormat.MakeTag(11, WireFormat.WireType.Varint));
+        }
+
+        [Test]
+        public void NoneFieldInOneof()
+        {
+            var message = new OneofWithNoneField();
+            var emptyHashCode = message.GetHashCode();
+            Assert.AreEqual(OneofWithNoneField.TestOneofCase.None, message.TestCase);
+            message.None = "test";
+            Assert.AreEqual(OneofWithNoneField.TestOneofCase.None_, message.TestCase);
+            Assert.AreNotEqual(emptyHashCode, message.GetHashCode());
+
+            var bytes = message.ToByteArray();
+            var parsed = OneofWithNoneField.Parser.ParseFrom(bytes);
+            Assert.AreEqual(message, parsed);
+            Assert.AreEqual("test", parsed.None);
+        }
+    }
+}
diff --git a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
index 51fa5e0..3a77990 100644
--- a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
+++ b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
@@ -1,705 +1,705 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using Google.Protobuf.TestProtos;

-using NUnit.Framework;

-using UnitTest.Issues.TestProtos;

-using Google.Protobuf.WellKnownTypes;

-using Google.Protobuf.Reflection;

-

-using static Google.Protobuf.JsonParserTest; // For WrapInQuotes

-using System.IO;

-using Google.Protobuf.Collections;

-using ProtobufUnittest;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Tests for the JSON formatter. Note that in these tests, double quotes are replaced with apostrophes

-    /// for the sake of readability (embedding \" everywhere is painful). See the AssertJson method for details.

-    /// </summary>

-    public class JsonFormatterTest

-    {

-        [Test]

-        public void DefaultValues_WhenOmitted()

-        {

-            var formatter = JsonFormatter.Default;

-

-            AssertJson("{ }", formatter.Format(new ForeignMessage()));

-            AssertJson("{ }", formatter.Format(new TestAllTypes()));

-            AssertJson("{ }", formatter.Format(new TestMap()));

-        }

-

-        [Test]

-        public void DefaultValues_WhenIncluded()

-        {

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));

-            AssertJson("{ 'c': 0 }", formatter.Format(new ForeignMessage()));

-        }

-

-        [Test]

-        public void EnumAllowAlias()

-        {

-            var message = new TestEnumAllowAlias

-            {

-                Value = TestEnumWithDupValue.Foo2,

-            };

-            var actualText = JsonFormatter.Default.Format(message);

-            var expectedText = "{ 'value': 'FOO1' }";

-            AssertJson(expectedText, actualText);

-        }

-

-        [Test]

-        public void EnumAsInt()

-        {

-            var message = new TestAllTypes

-            {

-                SingleForeignEnum = ForeignEnum.ForeignBar,

-                RepeatedForeignEnum = { ForeignEnum.ForeignBaz, (ForeignEnum) 100, ForeignEnum.ForeignFoo }

-            };

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatEnumsAsIntegers(true));

-            var actualText = formatter.Format(message);

-            var expectedText = "{ " +

-                               "'singleForeignEnum': 5, " +

-                               "'repeatedForeignEnum': [ 6, 100, 4 ]" +

-                               " }";

-            AssertJson(expectedText, actualText);

-        }

-

-        [Test]

-        public void AllSingleFields()

-        {

-            var message = new TestAllTypes

-            {

-                SingleBool = true,

-                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),

-                SingleDouble = 23.5,

-                SingleFixed32 = 23,

-                SingleFixed64 = 1234567890123,

-                SingleFloat = 12.25f,

-                SingleForeignEnum = ForeignEnum.ForeignBar,

-                SingleForeignMessage = new ForeignMessage { C = 10 },

-                SingleImportEnum = ImportEnum.ImportBaz,

-                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\twith\ttabs",

-                SingleUint32 = uint.MaxValue,

-                SingleUint64 = ulong.MaxValue,

-            };

-            var actualText = JsonFormatter.Default.Format(message);

-

-            // Fields in numeric order

-            var expectedText = "{ " +

-                "'singleInt32': 100, " +

-                "'singleInt64': '3210987654321', " +

-                "'singleUint32': 4294967295, " +

-                "'singleUint64': '18446744073709551615', " +

-                "'singleSint32': -456, " +

-                "'singleSint64': '-12345678901235', " +

-                "'singleFixed32': 23, " +

-                "'singleFixed64': '1234567890123', " +

-                "'singleSfixed32': -123, " +

-                "'singleSfixed64': '-12345678901234', " +

-                "'singleFloat': 12.25, " +

-                "'singleDouble': 23.5, " +

-                "'singleBool': true, " +

-                "'singleString': 'test\\twith\\ttabs', " +

-                "'singleBytes': 'AQIDBA==', " +

-                "'singleNestedMessage': { 'bb': 35 }, " +

-                "'singleForeignMessage': { 'c': 10 }, " +

-                "'singleImportMessage': { 'd': 20 }, " +

-                "'singleNestedEnum': 'FOO', " +

-                "'singleForeignEnum': 'FOREIGN_BAR', " +

-                "'singleImportEnum': 'IMPORT_BAZ', " +

-                "'singlePublicImportMessage': { 'e': 54 }" +

-                " }";

-            AssertJson(expectedText, actualText);

-        }

-

-        [Test]

-        public void WithFormatDefaultValues_DoesNotAffectMessageFields()

-        {

-            var message = new TestAllTypes();

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));

-            var json = formatter.Format(message);

-            Assert.IsFalse(json.Contains("\"singleNestedMessage\""));

-            Assert.IsFalse(json.Contains("\"singleForeignMessage\""));

-            Assert.IsFalse(json.Contains("\"singleImportMessage\""));

-        }

-

-        [Test]

-        public void WithFormatDefaultValues_DoesNotAffectProto3OptionalFields()

-        {

-            var message = new TestProto3Optional();

-            message.OptionalInt32 = 0;

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));

-            var json = formatter.Format(message);

-            // The non-optional proto3 fields are formatted, as is the optional-but-specified field.

-            AssertJson("{ 'optionalInt32': 0, 'singularInt32': 0, 'singularInt64': '0' }", json);

-        }

-

-        [Test]

-        public void WithFormatDefaultValues_DoesNotAffectProto2Fields()

-        {

-            var message = new TestProtos.Proto2.ForeignMessage();

-            message.C = 0;

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));

-            var json = formatter.Format(message);

-            // The specified field is formatted, but the non-specified field (d) is not.

-            AssertJson("{ 'c': 0 }", json);

-        }

-

-        [Test]

-        public void WithFormatDefaultValues_DoesNotAffectOneofFields()

-        {

-            var message = new TestOneof();

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));

-            var json = formatter.Format(message);

-            AssertJson("{ }", json);

-        }

-

-        [Test]

-        public void RepeatedField()

-        {

-            AssertJson("{ 'repeatedInt32': [ 1, 2, 3, 4, 5 ] }",

-                JsonFormatter.Default.Format(new TestAllTypes { RepeatedInt32 = { 1, 2, 3, 4, 5 } }));

-        }

-

-        [Test]

-        public void MapField_StringString()

-        {

-            AssertJson("{ 'mapStringString': { 'with spaces': 'bar', 'a': 'b' } }",

-                JsonFormatter.Default.Format(new TestMap { MapStringString = { { "with spaces", "bar" }, { "a", "b" } } }));

-        }

-

-        [Test]

-        public void MapField_Int32Int32()

-        {

-            // The keys are quoted, but the values aren't.

-            AssertJson("{ 'mapInt32Int32': { '0': 1, '2': 3 } }",

-                JsonFormatter.Default.Format(new TestMap { MapInt32Int32 = { { 0, 1 }, { 2, 3 } } }));

-        }

-

-        [Test]

-        public void MapField_BoolBool()

-        {

-            // The keys are quoted, but the values aren't.

-            AssertJson("{ 'mapBoolBool': { 'false': true, 'true': false } }",

-                JsonFormatter.Default.Format(new TestMap { MapBoolBool = { { false, true }, { true, false } } }));

-        }

-

-        [Test]

-        public void NullValueOutsideStruct()

-        {

-            var message = new NullValueOutsideStruct { NullValue = NullValue.NullValue };

-            AssertJson("{ 'nullValue': null }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void NullValueNotInOneof()

-        {

-            var message = new NullValueNotInOneof();

-            AssertJson("{ }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void NullValueNotInOneof_FormatDefaults()

-        {

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));

-            var message = new NullValueNotInOneof();

-            AssertJson("{ 'nullValue': null }", formatter.Format(message));

-        }

-

-        [TestCase(1.0, "1")]

-        [TestCase(double.NaN, "'NaN'")]

-        [TestCase(double.PositiveInfinity, "'Infinity'")]

-        [TestCase(double.NegativeInfinity, "'-Infinity'")]

-        public void DoubleRepresentations(double value, string expectedValueText)

-        {

-            var message = new TestAllTypes { SingleDouble = value };

-            string actualText = JsonFormatter.Default.Format(message);

-            string expectedText = "{ 'singleDouble': " + expectedValueText + " }";

-            AssertJson(expectedText, actualText);

-        }

-

-        [Test]

-        public void UnknownEnumValueNumeric_SingleField()

-        {

-            var message = new TestAllTypes { SingleForeignEnum = (ForeignEnum) 100 };

-            AssertJson("{ 'singleForeignEnum': 100 }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void UnknownEnumValueNumeric_RepeatedField()

-        {

-            var message = new TestAllTypes { RepeatedForeignEnum = { ForeignEnum.ForeignBaz, (ForeignEnum) 100, ForeignEnum.ForeignFoo } };

-            AssertJson("{ 'repeatedForeignEnum': [ 'FOREIGN_BAZ', 100, 'FOREIGN_FOO' ] }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void UnknownEnumValueNumeric_MapField()

-        {

-            var message = new TestMap { MapInt32Enum = { { 1, MapEnum.Foo }, { 2, (MapEnum) 100 }, { 3, MapEnum.Bar } } };

-            AssertJson("{ 'mapInt32Enum': { '1': 'MAP_ENUM_FOO', '2': 100, '3': 'MAP_ENUM_BAR' } }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void UnknownEnumValue_RepeatedField_AllEntriesUnknown()

-        {

-            var message = new TestAllTypes { RepeatedForeignEnum = { (ForeignEnum) 200, (ForeignEnum) 100 } };

-            AssertJson("{ 'repeatedForeignEnum': [ 200, 100 ] }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        [TestCase("a\u17b4b", "a\\u17b4b")] // Explicit

-        [TestCase("a\u0601b", "a\\u0601b")] // Ranged

-        [TestCase("a\u0605b", "a\u0605b")] // Passthrough (note lack of double backslash...)

-        public void SimpleNonAscii(string text, string encoded)

-        {

-            var message = new TestAllTypes { SingleString = text };

-            AssertJson("{ 'singleString': '" + encoded + "' }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void SurrogatePairEscaping()

-        {

-            var message = new TestAllTypes { SingleString = "a\uD801\uDC01b" };

-            AssertJson("{ 'singleString': 'a\\ud801\\udc01b' }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void InvalidSurrogatePairsFail()

-        {

-            // Note: don't use TestCase for these, as the strings can't be reliably represented

-            // See http://codeblog.jonskeet.uk/2014/11/07/when-is-a-string-not-a-string/

-

-            // Lone low surrogate

-            var message = new TestAllTypes { SingleString = "a\uDC01b" };

-            Assert.Throws<ArgumentException>(() => JsonFormatter.Default.Format(message));

-

-            // Lone high surrogate

-            message = new TestAllTypes { SingleString = "a\uD801b" };

-            Assert.Throws<ArgumentException>(() => JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        [TestCase("foo_bar", "fooBar")]

-        [TestCase("bananaBanana", "bananaBanana")]

-        [TestCase("BANANABanana", "BANANABanana")]

-        [TestCase("simple", "simple")]

-        [TestCase("ACTION_AND_ADVENTURE", "ACTIONANDADVENTURE")]

-        [TestCase("action_and_adventure", "actionAndAdventure")]

-        [TestCase("kFoo", "kFoo")]

-        [TestCase("HTTPServer", "HTTPServer")]

-        [TestCase("CLIENT", "CLIENT")]

-        public void ToJsonName(string original, string expected)

-        {

-            Assert.AreEqual(expected, JsonFormatter.ToJsonName(original));

-        }

-

-        [Test]

-        [TestCase(null, "{ }")]

-        [TestCase("x", "{ 'fooString': 'x' }")]

-        [TestCase("", "{ 'fooString': '' }")]

-        public void Oneof(string fooStringValue, string expectedJson)

-        {

-            var message = new TestOneof();

-            if (fooStringValue != null)

-            {

-                message.FooString = fooStringValue;

-            }

-

-            // We should get the same result both with and without "format default values".

-            var formatter = JsonFormatter.Default;

-            AssertJson(expectedJson, formatter.Format(message));

-            formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));

-            AssertJson(expectedJson, formatter.Format(message));

-        }

-

-        [Test]

-        public void WrapperFormatting_Single()

-        {

-            // Just a few examples, handling both classes and value types, and

-            // default vs non-default values

-            var message = new TestWellKnownTypes

-            {

-                Int64Field = 10,

-                Int32Field = 0,

-                BytesField = ByteString.FromBase64("ABCD"),

-                StringField = ""

-            };

-            var expectedJson = "{ 'int64Field': '10', 'int32Field': 0, 'stringField': '', 'bytesField': 'ABCD' }";

-            AssertJson(expectedJson, JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void WrapperFormatting_Message()

-        {

-            Assert.AreEqual("\"\"", JsonFormatter.Default.Format(new StringValue()));

-            Assert.AreEqual("0", JsonFormatter.Default.Format(new Int32Value()));

-        }

-

-        [Test]

-        public void WrapperFormatting_FormatDefaultValuesDoesNotFormatNull()

-        {

-            // The actual JSON here is very large because there are lots of fields. Just test a couple of them.

-            var message = new TestWellKnownTypes { Int32Field = 10 };

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));

-            var actualJson = formatter.Format(message);

-            // This *used* to include "int64Field": null, but that was a bug.

-            // WithDefaultValues should not affect message fields, including wrapper types.

-            Assert.IsFalse(actualJson.Contains("\"int64Field\": null"));

-            Assert.IsTrue(actualJson.Contains("\"int32Field\": 10"));

-        }

-

-        [Test]

-        public void OutputIsInNumericFieldOrder_NoDefaults()

-        {

-            var formatter = JsonFormatter.Default;

-            var message = new TestJsonFieldOrdering { PlainString = "p1", PlainInt32 = 2 };

-            AssertJson("{ 'plainString': 'p1', 'plainInt32': 2 }", formatter.Format(message));

-            message = new TestJsonFieldOrdering { O1Int32 = 5, O2String = "o2", PlainInt32 = 10, PlainString = "plain" };

-            AssertJson("{ 'plainString': 'plain', 'o2String': 'o2', 'plainInt32': 10, 'o1Int32': 5 }", formatter.Format(message));

-            message = new TestJsonFieldOrdering { O1String = "", O2Int32 = 0, PlainInt32 = 10, PlainString = "plain" };

-            AssertJson("{ 'plainString': 'plain', 'o1String': '', 'plainInt32': 10, 'o2Int32': 0 }", formatter.Format(message));

-        }

-

-        [Test]

-        public void OutputIsInNumericFieldOrder_WithDefaults()

-        {

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));

-            var message = new TestJsonFieldOrdering();

-            AssertJson("{ 'plainString': '', 'plainInt32': 0 }", formatter.Format(message));

-            message = new TestJsonFieldOrdering { O1Int32 = 5, O2String = "o2", PlainInt32 = 10, PlainString = "plain" };

-            AssertJson("{ 'plainString': 'plain', 'o2String': 'o2', 'plainInt32': 10, 'o1Int32': 5 }", formatter.Format(message));

-            message = new TestJsonFieldOrdering { O1String = "", O2Int32 = 0, PlainInt32 = 10, PlainString = "plain" };

-            AssertJson("{ 'plainString': 'plain', 'o1String': '', 'plainInt32': 10, 'o2Int32': 0 }", formatter.Format(message));

-        }

-

-        [Test]

-        [TestCase("1970-01-01T00:00:00Z", 0)]

-        [TestCase("1970-01-01T00:00:00.000000001Z", 1)]

-        [TestCase("1970-01-01T00:00:00.000000010Z", 10)]

-        [TestCase("1970-01-01T00:00:00.000000100Z", 100)]

-        [TestCase("1970-01-01T00:00:00.000001Z", 1000)]

-        [TestCase("1970-01-01T00:00:00.000010Z", 10000)]

-        [TestCase("1970-01-01T00:00:00.000100Z", 100000)]

-        [TestCase("1970-01-01T00:00:00.001Z", 1000000)]

-        [TestCase("1970-01-01T00:00:00.010Z", 10000000)]

-        [TestCase("1970-01-01T00:00:00.100Z", 100000000)]

-        [TestCase("1970-01-01T00:00:00.120Z", 120000000)]

-        [TestCase("1970-01-01T00:00:00.123Z", 123000000)]

-        [TestCase("1970-01-01T00:00:00.123400Z", 123400000)]

-        [TestCase("1970-01-01T00:00:00.123450Z", 123450000)]

-        [TestCase("1970-01-01T00:00:00.123456Z", 123456000)]

-        [TestCase("1970-01-01T00:00:00.123456700Z", 123456700)]

-        [TestCase("1970-01-01T00:00:00.123456780Z", 123456780)]

-        [TestCase("1970-01-01T00:00:00.123456789Z", 123456789)]

-        public void TimestampStandalone(string expected, int nanos)

-        {

-            Assert.AreEqual(WrapInQuotes(expected), new Timestamp { Nanos = nanos }.ToString());

-        }

-

-        [Test]

-        public void TimestampStandalone_FromDateTime()

-        {

-            // One before and one after the Unix epoch, more easily represented via DateTime.

-            Assert.AreEqual("\"1673-06-19T12:34:56Z\"",

-                new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp().ToString());

-            Assert.AreEqual("\"2015-07-31T10:29:34Z\"",

-                new DateTime(2015, 7, 31, 10, 29, 34, DateTimeKind.Utc).ToTimestamp().ToString());

-        }

-

-        [Test]

-        [TestCase(-1, -1)] // Would be valid as duration

-        [TestCase(1, Timestamp.MaxNanos + 1)]

-        [TestCase(Timestamp.UnixSecondsAtBclMaxValue + 1, 0)]

-        [TestCase(Timestamp.UnixSecondsAtBclMinValue - 1, 0)]

-        public void TimestampStandalone_NonNormalized(long seconds, int nanoseconds)

-        {

-            var timestamp = new Timestamp { Seconds = seconds, Nanos = nanoseconds };

-            Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(timestamp));

-        }

-

-        [Test]

-        public void TimestampField()

-        {

-            var message = new TestWellKnownTypes { TimestampField = new Timestamp() };

-            AssertJson("{ 'timestampField': '1970-01-01T00:00:00Z' }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        [TestCase(0, 0, "0s")]

-        [TestCase(1, 0, "1s")]

-        [TestCase(-1, 0, "-1s")]

-        [TestCase(0, 1, "0.000000001s")]

-        [TestCase(0, 10, "0.000000010s")]

-        [TestCase(0, 100, "0.000000100s")]

-        [TestCase(0, 1000, "0.000001s")]

-        [TestCase(0, 10000, "0.000010s")]

-        [TestCase(0, 100000, "0.000100s")]

-        [TestCase(0, 1000000, "0.001s")]

-        [TestCase(0, 10000000, "0.010s")]

-        [TestCase(0, 100000000, "0.100s")]

-        [TestCase(0, 120000000, "0.120s")]

-        [TestCase(0, 123000000, "0.123s")]

-        [TestCase(0, 123400000, "0.123400s")]

-        [TestCase(0, 123450000, "0.123450s")]

-        [TestCase(0, 123456000, "0.123456s")]

-        [TestCase(0, 123456700, "0.123456700s")]

-        [TestCase(0, 123456780, "0.123456780s")]

-        [TestCase(0, 123456789, "0.123456789s")]

-        [TestCase(0, -100000000, "-0.100s")]

-        [TestCase(1, 100000000, "1.100s")]

-        [TestCase(-1, -100000000, "-1.100s")]

-        public void DurationStandalone(long seconds, int nanoseconds, string expected)

-        {

-            var json = JsonFormatter.Default.Format(new Duration { Seconds = seconds, Nanos = nanoseconds });

-            Assert.AreEqual(WrapInQuotes(expected), json);

-        }

-

-        [Test]

-        [TestCase(1, 2123456789)]

-        [TestCase(1, -100000000)]

-        public void DurationStandalone_NonNormalized(long seconds, int nanoseconds)

-        {

-            var duration = new Duration { Seconds = seconds, Nanos = nanoseconds };

-            Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(duration));

-        }

-

-        [Test]

-        public void DurationField()

-        {

-            var message = new TestWellKnownTypes { DurationField = new Duration() };

-            AssertJson("{ 'durationField': '0s' }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void StructSample()

-        {

-            var message = new Struct

-            {

-                Fields =

-                {

-                    { "a", Value.ForNull() },

-                    { "b", Value.ForBool(false) },

-                    { "c", Value.ForNumber(10.5) },

-                    { "d", Value.ForString("text") },

-                    { "e", Value.ForList(Value.ForString("t1"), Value.ForNumber(5)) },

-                    { "f", Value.ForStruct(new Struct { Fields = { { "nested", Value.ForString("value") } } }) }

-                }

-            };

-            AssertJson("{ 'a': null, 'b': false, 'c': 10.5, 'd': 'text', 'e': [ 't1', 5 ], 'f': { 'nested': 'value' } }", message.ToString());

-        }

-

-        [Test]

-        [TestCase("foo__bar")]

-        [TestCase("foo_3_ar")]

-        [TestCase("fooBar")]

-        public void FieldMaskInvalid(string input)

-        {

-            var mask = new FieldMask { Paths = { input } };

-            Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(mask));

-        }

-

-        [Test]

-        public void FieldMaskStandalone()

-        {

-            var fieldMask = new FieldMask { Paths = { "", "single", "with_underscore", "nested.field.name", "nested..double_dot" } };

-            Assert.AreEqual("\",single,withUnderscore,nested.field.name,nested..doubleDot\"", fieldMask.ToString());

-

-            // Invalid, but we shouldn't create broken JSON...

-            fieldMask = new FieldMask { Paths = { "x\\y" } };

-            Assert.AreEqual(@"""x\\y""", fieldMask.ToString());

-        }

-

-        [Test]

-        public void FieldMaskField()

-        {

-            var message = new TestWellKnownTypes { FieldMaskField = new FieldMask { Paths = { "user.display_name", "photo" } } };

-            AssertJson("{ 'fieldMaskField': 'user.displayName,photo' }", JsonFormatter.Default.Format(message));

-        }

-

-        // SourceContext is an example of a well-known type with no special JSON handling

-        [Test]

-        public void SourceContextStandalone()

-        {

-            var message = new SourceContext { FileName = "foo.proto" };

-            AssertJson("{ 'fileName': 'foo.proto' }", JsonFormatter.Default.Format(message));

-        }

-

-        [Test]

-        public void AnyWellKnownType()

-        {

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(Timestamp.Descriptor)));

-            var timestamp = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();

-            var any = Any.Pack(timestamp);

-            AssertJson("{ '@type': 'type.googleapis.com/google.protobuf.Timestamp', 'value': '1673-06-19T12:34:56Z' }", formatter.Format(any));

-        }

-

-        [Test]

-        public void AnyMessageType()

-        {

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(TestAllTypes.Descriptor)));

-            var message = new TestAllTypes { SingleInt32 = 10, SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 } };

-            var any = Any.Pack(message);

-            AssertJson("{ '@type': 'type.googleapis.com/protobuf_unittest3.TestAllTypes', 'singleInt32': 10, 'singleNestedMessage': { 'bb': 20 } }", formatter.Format(any));

-        }

-

-        [Test]

-        public void AnyMessageType_CustomPrefix()

-        {

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(TestAllTypes.Descriptor)));

-            var message = new TestAllTypes { SingleInt32 = 10 };

-            var any = Any.Pack(message, "foo.bar/baz");

-            AssertJson("{ '@type': 'foo.bar/baz/protobuf_unittest3.TestAllTypes', 'singleInt32': 10 }", formatter.Format(any));

-        }

-

-        [Test]

-        public void AnyNested()

-        {

-            var registry = TypeRegistry.FromMessages(TestWellKnownTypes.Descriptor, TestAllTypes.Descriptor);

-            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(registry));

-

-            // Nest an Any as the value of an Any.

-            var doubleNestedMessage = new TestAllTypes { SingleInt32 = 20 };

-            var nestedMessage = Any.Pack(doubleNestedMessage);

-            var message = new TestWellKnownTypes { AnyField = Any.Pack(nestedMessage) };

-            AssertJson("{ 'anyField': { '@type': 'type.googleapis.com/google.protobuf.Any', 'value': { '@type': 'type.googleapis.com/protobuf_unittest3.TestAllTypes', 'singleInt32': 20 } } }",

-                formatter.Format(message));

-        }

-

-        [Test]

-        public void AnyUnknownType()

-        {

-            // The default type registry doesn't have any types in it.

-            var message = new TestAllTypes();

-            var any = Any.Pack(message);

-            Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(any));

-        }

-

-        [Test]

-        [TestCase(typeof(BoolValue), true, "true")]

-        [TestCase(typeof(Int32Value), 32, "32")]

-        [TestCase(typeof(Int64Value), 32L, "\"32\"")]

-        [TestCase(typeof(UInt32Value), 32U, "32")]

-        [TestCase(typeof(UInt64Value), 32UL, "\"32\"")]

-        [TestCase(typeof(StringValue), "foo", "\"foo\"")]

-        [TestCase(typeof(FloatValue), 1.5f, "1.5")]

-        [TestCase(typeof(DoubleValue), 1.5d, "1.5")]

-        public void Wrappers_Standalone(System.Type wrapperType, object value, string expectedJson)

-        {

-            IMessage populated = (IMessage)Activator.CreateInstance(wrapperType);

-            populated.Descriptor.Fields[WrappersReflection.WrapperValueFieldNumber].Accessor.SetValue(populated, value);

-            Assert.AreEqual(expectedJson, JsonFormatter.Default.Format(populated));

-        }

-

-        // Sanity tests for WriteValue. Not particularly comprehensive, as it's all covered above already,

-        // as FormatMessage uses WriteValue.

-

-        [TestCase(null, "null")]

-        [TestCase(1, "1")]

-        [TestCase(1L, "'1'")]

-        [TestCase(0.5f, "0.5")]

-        [TestCase(0.5d, "0.5")]

-        [TestCase("text", "'text'")]

-        [TestCase("x\ny", @"'x\ny'")]

-        [TestCase(ForeignEnum.ForeignBar, "'FOREIGN_BAR'")]

-        public void WriteValue_Constant(object value, string expectedJson)

-        {

-            AssertWriteValue(value, expectedJson);

-        }

-

-        [Test]

-        public void WriteValue_Timestamp()

-        {

-            var value = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();

-            AssertWriteValue(value, "'1673-06-19T12:34:56Z'");

-        }

-

-        [Test]

-        public void WriteValue_Message()

-        {

-            var value = new TestAllTypes { SingleInt32 = 100, SingleInt64 = 3210987654321L };

-            AssertWriteValue(value, "{ 'singleInt32': 100, 'singleInt64': '3210987654321' }");

-        }

-

-        [Test]

-        public void WriteValue_Message_PreserveNames()

-        {

-            var value = new TestAllTypes { SingleInt32 = 100, SingleInt64 = 3210987654321L };

-            AssertWriteValue(value, "{ 'single_int32': 100, 'single_int64': '3210987654321' }", JsonFormatter.Settings.Default.WithPreserveProtoFieldNames(true));

-        }

-

-        [Test]

-        public void WriteValue_List()

-        {

-            var value = new RepeatedField<int> { 1, 2, 3 };

-            AssertWriteValue(value, "[ 1, 2, 3 ]");

-        }

-

-        [Test]

-        public void Proto2_DefaultValuesWritten()

-        {

-            var value = new ProtobufTestMessages.Proto2.TestAllTypesProto2() { FieldName13 = 0 };

-            AssertWriteValue(value, "{ 'FieldName13': 0 }");

-        }

-

-        private static void AssertWriteValue(object value, string expectedJson, JsonFormatter.Settings settings = null)

-        {

-            var writer = new StringWriter();

-            new JsonFormatter(settings ?? JsonFormatter.Settings.Default).WriteValue(writer, value);

-            string actual = writer.ToString();

-            AssertJson(expectedJson, actual);

-        }

-

-        /// <summary>

-        /// Checks that the actual JSON is the same as the expected JSON - but after replacing

-        /// all apostrophes in the expected JSON with double quotes. This basically makes the tests easier

-        /// to read.

-        /// </summary>

-        private static void AssertJson(string expectedJsonWithApostrophes, string actualJson)

-        {

-            var expectedJson = expectedJsonWithApostrophes.Replace("'", "\"");

-            Assert.AreEqual(expectedJson, actualJson);

-        }

-    }

-}

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using Google.Protobuf.TestProtos;
+using NUnit.Framework;
+using UnitTest.Issues.TestProtos;
+using Google.Protobuf.WellKnownTypes;
+using Google.Protobuf.Reflection;
+
+using static Google.Protobuf.JsonParserTest; // For WrapInQuotes
+using System.IO;
+using Google.Protobuf.Collections;
+using ProtobufUnittest;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Tests for the JSON formatter. Note that in these tests, double quotes are replaced with apostrophes
+    /// for the sake of readability (embedding \" everywhere is painful). See the AssertJson method for details.
+    /// </summary>
+    public class JsonFormatterTest
+    {
+        [Test]
+        public void DefaultValues_WhenOmitted()
+        {
+            var formatter = JsonFormatter.Default;
+
+            AssertJson("{ }", formatter.Format(new ForeignMessage()));
+            AssertJson("{ }", formatter.Format(new TestAllTypes()));
+            AssertJson("{ }", formatter.Format(new TestMap()));
+        }
+
+        [Test]
+        public void DefaultValues_WhenIncluded()
+        {
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+            AssertJson("{ 'c': 0 }", formatter.Format(new ForeignMessage()));
+        }
+
+        [Test]
+        public void EnumAllowAlias()
+        {
+            var message = new TestEnumAllowAlias
+            {
+                Value = TestEnumWithDupValue.Foo2,
+            };
+            var actualText = JsonFormatter.Default.Format(message);
+            var expectedText = "{ 'value': 'FOO1' }";
+            AssertJson(expectedText, actualText);
+        }
+
+        [Test]
+        public void EnumAsInt()
+        {
+            var message = new TestAllTypes
+            {
+                SingleForeignEnum = ForeignEnum.ForeignBar,
+                RepeatedForeignEnum = { ForeignEnum.ForeignBaz, (ForeignEnum) 100, ForeignEnum.ForeignFoo }
+            };
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatEnumsAsIntegers(true));
+            var actualText = formatter.Format(message);
+            var expectedText = "{ " +
+                               "'singleForeignEnum': 5, " +
+                               "'repeatedForeignEnum': [ 6, 100, 4 ]" +
+                               " }";
+            AssertJson(expectedText, actualText);
+        }
+
+        [Test]
+        public void AllSingleFields()
+        {
+            var message = new TestAllTypes
+            {
+                SingleBool = true,
+                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
+                SingleDouble = 23.5,
+                SingleFixed32 = 23,
+                SingleFixed64 = 1234567890123,
+                SingleFloat = 12.25f,
+                SingleForeignEnum = ForeignEnum.ForeignBar,
+                SingleForeignMessage = new ForeignMessage { C = 10 },
+                SingleImportEnum = ImportEnum.ImportBaz,
+                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\twith\ttabs",
+                SingleUint32 = uint.MaxValue,
+                SingleUint64 = ulong.MaxValue,
+            };
+            var actualText = JsonFormatter.Default.Format(message);
+
+            // Fields in numeric order
+            var expectedText = "{ " +
+                "'singleInt32': 100, " +
+                "'singleInt64': '3210987654321', " +
+                "'singleUint32': 4294967295, " +
+                "'singleUint64': '18446744073709551615', " +
+                "'singleSint32': -456, " +
+                "'singleSint64': '-12345678901235', " +
+                "'singleFixed32': 23, " +
+                "'singleFixed64': '1234567890123', " +
+                "'singleSfixed32': -123, " +
+                "'singleSfixed64': '-12345678901234', " +
+                "'singleFloat': 12.25, " +
+                "'singleDouble': 23.5, " +
+                "'singleBool': true, " +
+                "'singleString': 'test\\twith\\ttabs', " +
+                "'singleBytes': 'AQIDBA==', " +
+                "'singleNestedMessage': { 'bb': 35 }, " +
+                "'singleForeignMessage': { 'c': 10 }, " +
+                "'singleImportMessage': { 'd': 20 }, " +
+                "'singleNestedEnum': 'FOO', " +
+                "'singleForeignEnum': 'FOREIGN_BAR', " +
+                "'singleImportEnum': 'IMPORT_BAZ', " +
+                "'singlePublicImportMessage': { 'e': 54 }" +
+                " }";
+            AssertJson(expectedText, actualText);
+        }
+
+        [Test]
+        public void WithFormatDefaultValues_DoesNotAffectMessageFields()
+        {
+            var message = new TestAllTypes();
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+            var json = formatter.Format(message);
+            Assert.IsFalse(json.Contains("\"singleNestedMessage\""));
+            Assert.IsFalse(json.Contains("\"singleForeignMessage\""));
+            Assert.IsFalse(json.Contains("\"singleImportMessage\""));
+        }
+
+        [Test]
+        public void WithFormatDefaultValues_DoesNotAffectProto3OptionalFields()
+        {
+            var message = new TestProto3Optional();
+            message.OptionalInt32 = 0;
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+            var json = formatter.Format(message);
+            // The non-optional proto3 fields are formatted, as is the optional-but-specified field.
+            AssertJson("{ 'optionalInt32': 0, 'singularInt32': 0, 'singularInt64': '0' }", json);
+        }
+
+        [Test]
+        public void WithFormatDefaultValues_DoesNotAffectProto2Fields()
+        {
+            var message = new TestProtos.Proto2.ForeignMessage();
+            message.C = 0;
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+            var json = formatter.Format(message);
+            // The specified field is formatted, but the non-specified field (d) is not.
+            AssertJson("{ 'c': 0 }", json);
+        }
+
+        [Test]
+        public void WithFormatDefaultValues_DoesNotAffectOneofFields()
+        {
+            var message = new TestOneof();
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+            var json = formatter.Format(message);
+            AssertJson("{ }", json);
+        }
+
+        [Test]
+        public void RepeatedField()
+        {
+            AssertJson("{ 'repeatedInt32': [ 1, 2, 3, 4, 5 ] }",
+                JsonFormatter.Default.Format(new TestAllTypes { RepeatedInt32 = { 1, 2, 3, 4, 5 } }));
+        }
+
+        [Test]
+        public void MapField_StringString()
+        {
+            AssertJson("{ 'mapStringString': { 'with spaces': 'bar', 'a': 'b' } }",
+                JsonFormatter.Default.Format(new TestMap { MapStringString = { { "with spaces", "bar" }, { "a", "b" } } }));
+        }
+
+        [Test]
+        public void MapField_Int32Int32()
+        {
+            // The keys are quoted, but the values aren't.
+            AssertJson("{ 'mapInt32Int32': { '0': 1, '2': 3 } }",
+                JsonFormatter.Default.Format(new TestMap { MapInt32Int32 = { { 0, 1 }, { 2, 3 } } }));
+        }
+
+        [Test]
+        public void MapField_BoolBool()
+        {
+            // The keys are quoted, but the values aren't.
+            AssertJson("{ 'mapBoolBool': { 'false': true, 'true': false } }",
+                JsonFormatter.Default.Format(new TestMap { MapBoolBool = { { false, true }, { true, false } } }));
+        }
+
+        [Test]
+        public void NullValueOutsideStruct()
+        {
+            var message = new NullValueOutsideStruct { NullValue = NullValue.NullValue };
+            AssertJson("{ 'nullValue': null }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void NullValueNotInOneof()
+        {
+            var message = new NullValueNotInOneof();
+            AssertJson("{ }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void NullValueNotInOneof_FormatDefaults()
+        {
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+            var message = new NullValueNotInOneof();
+            AssertJson("{ 'nullValue': null }", formatter.Format(message));
+        }
+
+        [TestCase(1.0, "1")]
+        [TestCase(double.NaN, "'NaN'")]
+        [TestCase(double.PositiveInfinity, "'Infinity'")]
+        [TestCase(double.NegativeInfinity, "'-Infinity'")]
+        public void DoubleRepresentations(double value, string expectedValueText)
+        {
+            var message = new TestAllTypes { SingleDouble = value };
+            string actualText = JsonFormatter.Default.Format(message);
+            string expectedText = "{ 'singleDouble': " + expectedValueText + " }";
+            AssertJson(expectedText, actualText);
+        }
+
+        [Test]
+        public void UnknownEnumValueNumeric_SingleField()
+        {
+            var message = new TestAllTypes { SingleForeignEnum = (ForeignEnum) 100 };
+            AssertJson("{ 'singleForeignEnum': 100 }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void UnknownEnumValueNumeric_RepeatedField()
+        {
+            var message = new TestAllTypes { RepeatedForeignEnum = { ForeignEnum.ForeignBaz, (ForeignEnum) 100, ForeignEnum.ForeignFoo } };
+            AssertJson("{ 'repeatedForeignEnum': [ 'FOREIGN_BAZ', 100, 'FOREIGN_FOO' ] }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void UnknownEnumValueNumeric_MapField()
+        {
+            var message = new TestMap { MapInt32Enum = { { 1, MapEnum.Foo }, { 2, (MapEnum) 100 }, { 3, MapEnum.Bar } } };
+            AssertJson("{ 'mapInt32Enum': { '1': 'MAP_ENUM_FOO', '2': 100, '3': 'MAP_ENUM_BAR' } }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void UnknownEnumValue_RepeatedField_AllEntriesUnknown()
+        {
+            var message = new TestAllTypes { RepeatedForeignEnum = { (ForeignEnum) 200, (ForeignEnum) 100 } };
+            AssertJson("{ 'repeatedForeignEnum': [ 200, 100 ] }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        [TestCase("a\u17b4b", "a\\u17b4b")] // Explicit
+        [TestCase("a\u0601b", "a\\u0601b")] // Ranged
+        [TestCase("a\u0605b", "a\u0605b")] // Passthrough (note lack of double backslash...)
+        public void SimpleNonAscii(string text, string encoded)
+        {
+            var message = new TestAllTypes { SingleString = text };
+            AssertJson("{ 'singleString': '" + encoded + "' }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void SurrogatePairEscaping()
+        {
+            var message = new TestAllTypes { SingleString = "a\uD801\uDC01b" };
+            AssertJson("{ 'singleString': 'a\\ud801\\udc01b' }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void InvalidSurrogatePairsFail()
+        {
+            // Note: don't use TestCase for these, as the strings can't be reliably represented
+            // See http://codeblog.jonskeet.uk/2014/11/07/when-is-a-string-not-a-string/
+
+            // Lone low surrogate
+            var message = new TestAllTypes { SingleString = "a\uDC01b" };
+            Assert.Throws<ArgumentException>(() => JsonFormatter.Default.Format(message));
+
+            // Lone high surrogate
+            message = new TestAllTypes { SingleString = "a\uD801b" };
+            Assert.Throws<ArgumentException>(() => JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        [TestCase("foo_bar", "fooBar")]
+        [TestCase("bananaBanana", "bananaBanana")]
+        [TestCase("BANANABanana", "BANANABanana")]
+        [TestCase("simple", "simple")]
+        [TestCase("ACTION_AND_ADVENTURE", "ACTIONANDADVENTURE")]
+        [TestCase("action_and_adventure", "actionAndAdventure")]
+        [TestCase("kFoo", "kFoo")]
+        [TestCase("HTTPServer", "HTTPServer")]
+        [TestCase("CLIENT", "CLIENT")]
+        public void ToJsonName(string original, string expected)
+        {
+            Assert.AreEqual(expected, JsonFormatter.ToJsonName(original));
+        }
+
+        [Test]
+        [TestCase(null, "{ }")]
+        [TestCase("x", "{ 'fooString': 'x' }")]
+        [TestCase("", "{ 'fooString': '' }")]
+        public void Oneof(string fooStringValue, string expectedJson)
+        {
+            var message = new TestOneof();
+            if (fooStringValue != null)
+            {
+                message.FooString = fooStringValue;
+            }
+
+            // We should get the same result both with and without "format default values".
+            var formatter = JsonFormatter.Default;
+            AssertJson(expectedJson, formatter.Format(message));
+            formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+            AssertJson(expectedJson, formatter.Format(message));
+        }
+
+        [Test]
+        public void WrapperFormatting_Single()
+        {
+            // Just a few examples, handling both classes and value types, and
+            // default vs non-default values
+            var message = new TestWellKnownTypes
+            {
+                Int64Field = 10,
+                Int32Field = 0,
+                BytesField = ByteString.FromBase64("ABCD"),
+                StringField = ""
+            };
+            var expectedJson = "{ 'int64Field': '10', 'int32Field': 0, 'stringField': '', 'bytesField': 'ABCD' }";
+            AssertJson(expectedJson, JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void WrapperFormatting_Message()
+        {
+            Assert.AreEqual("\"\"", JsonFormatter.Default.Format(new StringValue()));
+            Assert.AreEqual("0", JsonFormatter.Default.Format(new Int32Value()));
+        }
+
+        [Test]
+        public void WrapperFormatting_FormatDefaultValuesDoesNotFormatNull()
+        {
+            // The actual JSON here is very large because there are lots of fields. Just test a couple of them.
+            var message = new TestWellKnownTypes { Int32Field = 10 };
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+            var actualJson = formatter.Format(message);
+            // This *used* to include "int64Field": null, but that was a bug.
+            // WithDefaultValues should not affect message fields, including wrapper types.
+            Assert.IsFalse(actualJson.Contains("\"int64Field\": null"));
+            Assert.IsTrue(actualJson.Contains("\"int32Field\": 10"));
+        }
+
+        [Test]
+        public void OutputIsInNumericFieldOrder_NoDefaults()
+        {
+            var formatter = JsonFormatter.Default;
+            var message = new TestJsonFieldOrdering { PlainString = "p1", PlainInt32 = 2 };
+            AssertJson("{ 'plainString': 'p1', 'plainInt32': 2 }", formatter.Format(message));
+            message = new TestJsonFieldOrdering { O1Int32 = 5, O2String = "o2", PlainInt32 = 10, PlainString = "plain" };
+            AssertJson("{ 'plainString': 'plain', 'o2String': 'o2', 'plainInt32': 10, 'o1Int32': 5 }", formatter.Format(message));
+            message = new TestJsonFieldOrdering { O1String = "", O2Int32 = 0, PlainInt32 = 10, PlainString = "plain" };
+            AssertJson("{ 'plainString': 'plain', 'o1String': '', 'plainInt32': 10, 'o2Int32': 0 }", formatter.Format(message));
+        }
+
+        [Test]
+        public void OutputIsInNumericFieldOrder_WithDefaults()
+        {
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+            var message = new TestJsonFieldOrdering();
+            AssertJson("{ 'plainString': '', 'plainInt32': 0 }", formatter.Format(message));
+            message = new TestJsonFieldOrdering { O1Int32 = 5, O2String = "o2", PlainInt32 = 10, PlainString = "plain" };
+            AssertJson("{ 'plainString': 'plain', 'o2String': 'o2', 'plainInt32': 10, 'o1Int32': 5 }", formatter.Format(message));
+            message = new TestJsonFieldOrdering { O1String = "", O2Int32 = 0, PlainInt32 = 10, PlainString = "plain" };
+            AssertJson("{ 'plainString': 'plain', 'o1String': '', 'plainInt32': 10, 'o2Int32': 0 }", formatter.Format(message));
+        }
+
+        [Test]
+        [TestCase("1970-01-01T00:00:00Z", 0)]
+        [TestCase("1970-01-01T00:00:00.000000001Z", 1)]
+        [TestCase("1970-01-01T00:00:00.000000010Z", 10)]
+        [TestCase("1970-01-01T00:00:00.000000100Z", 100)]
+        [TestCase("1970-01-01T00:00:00.000001Z", 1000)]
+        [TestCase("1970-01-01T00:00:00.000010Z", 10000)]
+        [TestCase("1970-01-01T00:00:00.000100Z", 100000)]
+        [TestCase("1970-01-01T00:00:00.001Z", 1000000)]
+        [TestCase("1970-01-01T00:00:00.010Z", 10000000)]
+        [TestCase("1970-01-01T00:00:00.100Z", 100000000)]
+        [TestCase("1970-01-01T00:00:00.120Z", 120000000)]
+        [TestCase("1970-01-01T00:00:00.123Z", 123000000)]
+        [TestCase("1970-01-01T00:00:00.123400Z", 123400000)]
+        [TestCase("1970-01-01T00:00:00.123450Z", 123450000)]
+        [TestCase("1970-01-01T00:00:00.123456Z", 123456000)]
+        [TestCase("1970-01-01T00:00:00.123456700Z", 123456700)]
+        [TestCase("1970-01-01T00:00:00.123456780Z", 123456780)]
+        [TestCase("1970-01-01T00:00:00.123456789Z", 123456789)]
+        public void TimestampStandalone(string expected, int nanos)
+        {
+            Assert.AreEqual(WrapInQuotes(expected), new Timestamp { Nanos = nanos }.ToString());
+        }
+
+        [Test]
+        public void TimestampStandalone_FromDateTime()
+        {
+            // One before and one after the Unix epoch, more easily represented via DateTime.
+            Assert.AreEqual("\"1673-06-19T12:34:56Z\"",
+                new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp().ToString());
+            Assert.AreEqual("\"2015-07-31T10:29:34Z\"",
+                new DateTime(2015, 7, 31, 10, 29, 34, DateTimeKind.Utc).ToTimestamp().ToString());
+        }
+
+        [Test]
+        [TestCase(-1, -1)] // Would be valid as duration
+        [TestCase(1, Timestamp.MaxNanos + 1)]
+        [TestCase(Timestamp.UnixSecondsAtBclMaxValue + 1, 0)]
+        [TestCase(Timestamp.UnixSecondsAtBclMinValue - 1, 0)]
+        public void TimestampStandalone_NonNormalized(long seconds, int nanoseconds)
+        {
+            var timestamp = new Timestamp { Seconds = seconds, Nanos = nanoseconds };
+            Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(timestamp));
+        }
+
+        [Test]
+        public void TimestampField()
+        {
+            var message = new TestWellKnownTypes { TimestampField = new Timestamp() };
+            AssertJson("{ 'timestampField': '1970-01-01T00:00:00Z' }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        [TestCase(0, 0, "0s")]
+        [TestCase(1, 0, "1s")]
+        [TestCase(-1, 0, "-1s")]
+        [TestCase(0, 1, "0.000000001s")]
+        [TestCase(0, 10, "0.000000010s")]
+        [TestCase(0, 100, "0.000000100s")]
+        [TestCase(0, 1000, "0.000001s")]
+        [TestCase(0, 10000, "0.000010s")]
+        [TestCase(0, 100000, "0.000100s")]
+        [TestCase(0, 1000000, "0.001s")]
+        [TestCase(0, 10000000, "0.010s")]
+        [TestCase(0, 100000000, "0.100s")]
+        [TestCase(0, 120000000, "0.120s")]
+        [TestCase(0, 123000000, "0.123s")]
+        [TestCase(0, 123400000, "0.123400s")]
+        [TestCase(0, 123450000, "0.123450s")]
+        [TestCase(0, 123456000, "0.123456s")]
+        [TestCase(0, 123456700, "0.123456700s")]
+        [TestCase(0, 123456780, "0.123456780s")]
+        [TestCase(0, 123456789, "0.123456789s")]
+        [TestCase(0, -100000000, "-0.100s")]
+        [TestCase(1, 100000000, "1.100s")]
+        [TestCase(-1, -100000000, "-1.100s")]
+        public void DurationStandalone(long seconds, int nanoseconds, string expected)
+        {
+            var json = JsonFormatter.Default.Format(new Duration { Seconds = seconds, Nanos = nanoseconds });
+            Assert.AreEqual(WrapInQuotes(expected), json);
+        }
+
+        [Test]
+        [TestCase(1, 2123456789)]
+        [TestCase(1, -100000000)]
+        public void DurationStandalone_NonNormalized(long seconds, int nanoseconds)
+        {
+            var duration = new Duration { Seconds = seconds, Nanos = nanoseconds };
+            Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(duration));
+        }
+
+        [Test]
+        public void DurationField()
+        {
+            var message = new TestWellKnownTypes { DurationField = new Duration() };
+            AssertJson("{ 'durationField': '0s' }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void StructSample()
+        {
+            var message = new Struct
+            {
+                Fields =
+                {
+                    { "a", Value.ForNull() },
+                    { "b", Value.ForBool(false) },
+                    { "c", Value.ForNumber(10.5) },
+                    { "d", Value.ForString("text") },
+                    { "e", Value.ForList(Value.ForString("t1"), Value.ForNumber(5)) },
+                    { "f", Value.ForStruct(new Struct { Fields = { { "nested", Value.ForString("value") } } }) }
+                }
+            };
+            AssertJson("{ 'a': null, 'b': false, 'c': 10.5, 'd': 'text', 'e': [ 't1', 5 ], 'f': { 'nested': 'value' } }", message.ToString());
+        }
+
+        [Test]
+        [TestCase("foo__bar")]
+        [TestCase("foo_3_ar")]
+        [TestCase("fooBar")]
+        public void FieldMaskInvalid(string input)
+        {
+            var mask = new FieldMask { Paths = { input } };
+            Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(mask));
+        }
+
+        [Test]
+        public void FieldMaskStandalone()
+        {
+            var fieldMask = new FieldMask { Paths = { "", "single", "with_underscore", "nested.field.name", "nested..double_dot" } };
+            Assert.AreEqual("\",single,withUnderscore,nested.field.name,nested..doubleDot\"", fieldMask.ToString());
+
+            // Invalid, but we shouldn't create broken JSON...
+            fieldMask = new FieldMask { Paths = { "x\\y" } };
+            Assert.AreEqual(@"""x\\y""", fieldMask.ToString());
+        }
+
+        [Test]
+        public void FieldMaskField()
+        {
+            var message = new TestWellKnownTypes { FieldMaskField = new FieldMask { Paths = { "user.display_name", "photo" } } };
+            AssertJson("{ 'fieldMaskField': 'user.displayName,photo' }", JsonFormatter.Default.Format(message));
+        }
+
+        // SourceContext is an example of a well-known type with no special JSON handling
+        [Test]
+        public void SourceContextStandalone()
+        {
+            var message = new SourceContext { FileName = "foo.proto" };
+            AssertJson("{ 'fileName': 'foo.proto' }", JsonFormatter.Default.Format(message));
+        }
+
+        [Test]
+        public void AnyWellKnownType()
+        {
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(Timestamp.Descriptor)));
+            var timestamp = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();
+            var any = Any.Pack(timestamp);
+            AssertJson("{ '@type': 'type.googleapis.com/google.protobuf.Timestamp', 'value': '1673-06-19T12:34:56Z' }", formatter.Format(any));
+        }
+
+        [Test]
+        public void AnyMessageType()
+        {
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
+            var message = new TestAllTypes { SingleInt32 = 10, SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 } };
+            var any = Any.Pack(message);
+            AssertJson("{ '@type': 'type.googleapis.com/protobuf_unittest3.TestAllTypes', 'singleInt32': 10, 'singleNestedMessage': { 'bb': 20 } }", formatter.Format(any));
+        }
+
+        [Test]
+        public void AnyMessageType_CustomPrefix()
+        {
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
+            var message = new TestAllTypes { SingleInt32 = 10 };
+            var any = Any.Pack(message, "foo.bar/baz");
+            AssertJson("{ '@type': 'foo.bar/baz/protobuf_unittest3.TestAllTypes', 'singleInt32': 10 }", formatter.Format(any));
+        }
+
+        [Test]
+        public void AnyNested()
+        {
+            var registry = TypeRegistry.FromMessages(TestWellKnownTypes.Descriptor, TestAllTypes.Descriptor);
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(registry));
+
+            // Nest an Any as the value of an Any.
+            var doubleNestedMessage = new TestAllTypes { SingleInt32 = 20 };
+            var nestedMessage = Any.Pack(doubleNestedMessage);
+            var message = new TestWellKnownTypes { AnyField = Any.Pack(nestedMessage) };
+            AssertJson("{ 'anyField': { '@type': 'type.googleapis.com/google.protobuf.Any', 'value': { '@type': 'type.googleapis.com/protobuf_unittest3.TestAllTypes', 'singleInt32': 20 } } }",
+                formatter.Format(message));
+        }
+
+        [Test]
+        public void AnyUnknownType()
+        {
+            // The default type registry doesn't have any types in it.
+            var message = new TestAllTypes();
+            var any = Any.Pack(message);
+            Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(any));
+        }
+
+        [Test]
+        [TestCase(typeof(BoolValue), true, "true")]
+        [TestCase(typeof(Int32Value), 32, "32")]
+        [TestCase(typeof(Int64Value), 32L, "\"32\"")]
+        [TestCase(typeof(UInt32Value), 32U, "32")]
+        [TestCase(typeof(UInt64Value), 32UL, "\"32\"")]
+        [TestCase(typeof(StringValue), "foo", "\"foo\"")]
+        [TestCase(typeof(FloatValue), 1.5f, "1.5")]
+        [TestCase(typeof(DoubleValue), 1.5d, "1.5")]
+        public void Wrappers_Standalone(System.Type wrapperType, object value, string expectedJson)
+        {
+            IMessage populated = (IMessage)Activator.CreateInstance(wrapperType);
+            populated.Descriptor.Fields[WrappersReflection.WrapperValueFieldNumber].Accessor.SetValue(populated, value);
+            Assert.AreEqual(expectedJson, JsonFormatter.Default.Format(populated));
+        }
+
+        // Sanity tests for WriteValue. Not particularly comprehensive, as it's all covered above already,
+        // as FormatMessage uses WriteValue.
+
+        [TestCase(null, "null")]
+        [TestCase(1, "1")]
+        [TestCase(1L, "'1'")]
+        [TestCase(0.5f, "0.5")]
+        [TestCase(0.5d, "0.5")]
+        [TestCase("text", "'text'")]
+        [TestCase("x\ny", @"'x\ny'")]
+        [TestCase(ForeignEnum.ForeignBar, "'FOREIGN_BAR'")]
+        public void WriteValue_Constant(object value, string expectedJson)
+        {
+            AssertWriteValue(value, expectedJson);
+        }
+
+        [Test]
+        public void WriteValue_Timestamp()
+        {
+            var value = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();
+            AssertWriteValue(value, "'1673-06-19T12:34:56Z'");
+        }
+
+        [Test]
+        public void WriteValue_Message()
+        {
+            var value = new TestAllTypes { SingleInt32 = 100, SingleInt64 = 3210987654321L };
+            AssertWriteValue(value, "{ 'singleInt32': 100, 'singleInt64': '3210987654321' }");
+        }
+
+        [Test]
+        public void WriteValue_Message_PreserveNames()
+        {
+            var value = new TestAllTypes { SingleInt32 = 100, SingleInt64 = 3210987654321L };
+            AssertWriteValue(value, "{ 'single_int32': 100, 'single_int64': '3210987654321' }", JsonFormatter.Settings.Default.WithPreserveProtoFieldNames(true));
+        }
+
+        [Test]
+        public void WriteValue_List()
+        {
+            var value = new RepeatedField<int> { 1, 2, 3 };
+            AssertWriteValue(value, "[ 1, 2, 3 ]");
+        }
+
+        [Test]
+        public void Proto2_DefaultValuesWritten()
+        {
+            var value = new ProtobufTestMessages.Proto2.TestAllTypesProto2() { FieldName13 = 0 };
+            AssertWriteValue(value, "{ 'FieldName13': 0 }");
+        }
+
+        private static void AssertWriteValue(object value, string expectedJson, JsonFormatter.Settings settings = null)
+        {
+            var writer = new StringWriter();
+            new JsonFormatter(settings ?? JsonFormatter.Settings.Default).WriteValue(writer, value);
+            string actual = writer.ToString();
+            AssertJson(expectedJson, actual);
+        }
+
+        /// <summary>
+        /// Checks that the actual JSON is the same as the expected JSON - but after replacing
+        /// all apostrophes in the expected JSON with double quotes. This basically makes the tests easier
+        /// to read.
+        /// </summary>
+        private static void AssertJson(string expectedJsonWithApostrophes, string actualJson)
+        {
+            var expectedJson = expectedJsonWithApostrophes.Replace("'", "\"");
+            Assert.AreEqual(expectedJson, actualJson);
+        }
+    }
+}
diff --git a/csharp/src/Google.Protobuf.Test/TestCornerCases.cs b/csharp/src/Google.Protobuf.Test/TestCornerCases.cs
index 248f5fa..859b49c 100644
--- a/csharp/src/Google.Protobuf.Test/TestCornerCases.cs
+++ b/csharp/src/Google.Protobuf.Test/TestCornerCases.cs
@@ -1,62 +1,62 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-    

-using UnitTest.Issues.TestProtos;

-using NUnit.Framework;

-

-namespace Google.Protobuf

-{

-    public class TestCornerCases

-    {

-        [Test]

-        public void TestRoundTripNegativeEnums()

-        {

-            NegativeEnumMessage msg = new NegativeEnumMessage

-            {

-                Value = NegativeEnum.MinusOne,

-                Values = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow },

-                PackedValues = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow }

-            };

-

-            Assert.AreEqual(58, msg.CalculateSize());

-

-            byte[] bytes = new byte[58];

-            CodedOutputStream output = new CodedOutputStream(bytes);

-

-            msg.WriteTo(output);

-            Assert.AreEqual(0, output.SpaceLeft);

-

-            NegativeEnumMessage copy = NegativeEnumMessage.Parser.ParseFrom(bytes);

-            Assert.AreEqual(msg, copy);

-        }

-    }

-}

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+    
+using UnitTest.Issues.TestProtos;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+    public class TestCornerCases
+    {
+        [Test]
+        public void TestRoundTripNegativeEnums()
+        {
+            NegativeEnumMessage msg = new NegativeEnumMessage
+            {
+                Value = NegativeEnum.MinusOne,
+                Values = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow },
+                PackedValues = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow }
+            };
+
+            Assert.AreEqual(58, msg.CalculateSize());
+
+            byte[] bytes = new byte[58];
+            CodedOutputStream output = new CodedOutputStream(bytes);
+
+            msg.WriteTo(output);
+            Assert.AreEqual(0, output.SpaceLeft);
+
+            NegativeEnumMessage copy = NegativeEnumMessage.Parser.ParseFrom(bytes);
+            Assert.AreEqual(msg, copy);
+        }
+    }
+}
diff --git a/csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs b/csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs
index 4b9a569..c05cb08 100644
--- a/csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs
+++ b/csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs
@@ -1,4 +1,4 @@
-#region Copyright notice and license
+#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/
@@ -142,16 +142,16 @@
         }
 
         [Test]
-        public void IsWrongType()

-        {

-            var any = Any.Pack(SampleMessages.CreateFullTestAllTypes());

-            Assert.False(any.Is(TestOneof.Descriptor));

+        public void IsWrongType()
+        {
+            var any = Any.Pack(SampleMessages.CreateFullTestAllTypes());
+            Assert.False(any.Is(TestOneof.Descriptor));
         }
 
-        public void IsRightType()

-        {

-            var any = Any.Pack(SampleMessages.CreateFullTestAllTypes());

-            Assert.True(any.Is(TestAllTypes.Descriptor));

+        public void IsRightType()
+        {
+            var any = Any.Pack(SampleMessages.CreateFullTestAllTypes());
+            Assert.True(any.Is(TestAllTypes.Descriptor));
         }
     }
 }
diff --git a/csharp/src/Google.Protobuf.Test/testprotos.pb b/csharp/src/Google.Protobuf.Test/testprotos.pb
index 50fd48c..83958c6 100644
--- a/csharp/src/Google.Protobuf.Test/testprotos.pb
+++ b/csharp/src/Google.Protobuf.Test/testprotos.pb
Binary files differ
diff --git a/csharp/src/Google.Protobuf/ByteArray.cs b/csharp/src/Google.Protobuf/ByteArray.cs
index 69b6ef8..094a81e 100644
--- a/csharp/src/Google.Protobuf/ByteArray.cs
+++ b/csharp/src/Google.Protobuf/ByteArray.cs
@@ -1,79 +1,79 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Provides a utility routine to copy small arrays much more quickly than Buffer.BlockCopy

-    /// </summary>

-    internal static class ByteArray

-    {

-        /// <summary>

-        /// The threshold above which you should use Buffer.BlockCopy rather than ByteArray.Copy

-        /// </summary>

-        private const int CopyThreshold = 12;

-

-        /// <summary>

-        /// Determines which copy routine to use based on the number of bytes to be copied.

-        /// </summary>

-        internal static void Copy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count)

-        {

-            if (count > CopyThreshold)

-            {

-                Buffer.BlockCopy(src, srcOffset, dst, dstOffset, count);

-            }

-            else

-            {

-                int stop = srcOffset + count;

-                for (int i = srcOffset; i < stop; i++)

-                {

-                    dst[dstOffset++] = src[i];

-                }

-            }

-        }

-

-        /// <summary>

-        /// Reverses the order of bytes in the array

-        /// </summary>

-        internal static void Reverse(byte[] bytes)

-        {

-            for (int first = 0, last = bytes.Length - 1; first < last; first++, last--)

-            {

-                byte temp = bytes[first];

-                bytes[first] = bytes[last];

-                bytes[last] = temp;

-            }

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Provides a utility routine to copy small arrays much more quickly than Buffer.BlockCopy
+    /// </summary>
+    internal static class ByteArray
+    {
+        /// <summary>
+        /// The threshold above which you should use Buffer.BlockCopy rather than ByteArray.Copy
+        /// </summary>
+        private const int CopyThreshold = 12;
+
+        /// <summary>
+        /// Determines which copy routine to use based on the number of bytes to be copied.
+        /// </summary>
+        internal static void Copy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count)
+        {
+            if (count > CopyThreshold)
+            {
+                Buffer.BlockCopy(src, srcOffset, dst, dstOffset, count);
+            }
+            else
+            {
+                int stop = srcOffset + count;
+                for (int i = srcOffset; i < stop; i++)
+                {
+                    dst[dstOffset++] = src[i];
+                }
+            }
+        }
+
+        /// <summary>
+        /// Reverses the order of bytes in the array
+        /// </summary>
+        internal static void Reverse(byte[] bytes)
+        {
+            for (int first = 0, last = bytes.Length - 1; first < last; first++, last--)
+            {
+                byte temp = bytes[first];
+                bytes[first] = bytes[last];
+                bytes[last] = temp;
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/ByteString.cs b/csharp/src/Google.Protobuf/ByteString.cs
index 063b543..8c6eb5b 100644
--- a/csharp/src/Google.Protobuf/ByteString.cs
+++ b/csharp/src/Google.Protobuf/ByteString.cs
@@ -1,434 +1,434 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.Collections;

-using System.Collections.Generic;

-using System.IO;

-using System.Runtime.InteropServices;

-using System.Security;

-using System.Text;

-#if !NET35

-using System.Threading;

-using System.Threading.Tasks;

-#endif

-#if NET35

-using Google.Protobuf.Compatibility;

-#endif

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Immutable array of bytes.

-    /// </summary>

-    [SecuritySafeCritical]

-    public sealed class ByteString : IEnumerable<byte>, IEquatable<ByteString>

-    {

-        private static readonly ByteString empty = new ByteString(new byte[0]);

-

-        private readonly ReadOnlyMemory<byte> bytes;

-

-        /// <summary>

-        /// Internal use only. Ensure that the provided memory is not mutated and belongs to this instance.

-        /// </summary>

-        internal static ByteString AttachBytes(ReadOnlyMemory<byte> bytes)

-        {

-            return new ByteString(bytes);

-        }

-

-        /// <summary>

-        /// Internal use only. Ensure that the provided memory is not mutated and belongs to this instance.

-        /// This method encapsulates converting array to memory. Reduces need for SecuritySafeCritical

-        /// in .NET Framework.

-        /// </summary>

-        internal static ByteString AttachBytes(byte[] bytes)

-        {

-            return AttachBytes(bytes.AsMemory());

-        }

-

-        /// <summary>

-        /// Constructs a new ByteString from the given memory. The memory is

-        /// *not* copied, and must not be modified after this constructor is called.

-        /// </summary>

-        private ByteString(ReadOnlyMemory<byte> bytes)

-        {

-            this.bytes = bytes;

-        }

-

-        /// <summary>

-        /// Returns an empty ByteString.

-        /// </summary>

-        public static ByteString Empty

-        {

-            get { return empty; }

-        }

-

-        /// <summary>

-        /// Returns the length of this ByteString in bytes.

-        /// </summary>

-        public int Length

-        {

-            get { return bytes.Length; }

-        }

-

-        /// <summary>

-        /// Returns <c>true</c> if this byte string is empty, <c>false</c> otherwise.

-        /// </summary>

-        public bool IsEmpty

-        {

-            get { return Length == 0; }

-        }

-

-        /// <summary>

-        /// Provides read-only access to the data of this <see cref="ByteString"/>.

-        /// No data is copied so this is the most efficient way of accessing.

-        /// </summary>

-        public ReadOnlySpan<byte> Span

-        {

-            get { return bytes.Span; }

-        }

-

-        /// <summary>

-        /// Provides read-only access to the data of this <see cref="ByteString"/>.

-        /// No data is copied so this is the most efficient way of accessing.

-        /// </summary>

-        public ReadOnlyMemory<byte> Memory

-        {

-            get { return bytes; }

-        }

-

-        /// <summary>

-        /// Converts this <see cref="ByteString"/> into a byte array.

-        /// </summary>

-        /// <remarks>The data is copied - changes to the returned array will not be reflected in this <c>ByteString</c>.</remarks>

-        /// <returns>A byte array with the same data as this <c>ByteString</c>.</returns>

-        public byte[] ToByteArray()

-        {

-            return bytes.ToArray();

-        }

-

-        /// <summary>

-        /// Converts this <see cref="ByteString"/> into a standard base64 representation.

-        /// </summary>

-        /// <returns>A base64 representation of this <c>ByteString</c>.</returns>

-        public string ToBase64()

-        {

-            if (MemoryMarshal.TryGetArray(bytes, out ArraySegment<byte> segment))

-            {

-                // Fast path. ByteString was created with an array, so pass the underlying array.

-                return Convert.ToBase64String(segment.Array, segment.Offset, segment.Count);

-            }

-            else

-            {

-                // Slow path. BytesString is not an array. Convert memory and pass result to ToBase64String.

-                return Convert.ToBase64String(bytes.ToArray());

-            }

-        }

-

-        /// <summary>

-        /// Constructs a <see cref="ByteString" /> from the Base64 Encoded String.

-        /// </summary>

-        public static ByteString FromBase64(string bytes)

-        {

-            // By handling the empty string explicitly, we not only optimize but we fix a

-            // problem on CF 2.0. See issue 61 for details.

-            return bytes == "" ? Empty : new ByteString(Convert.FromBase64String(bytes));

-        }

-

-        /// <summary>

-        /// Constructs a <see cref="ByteString"/> from data in the given stream, synchronously.

-        /// </summary>

-        /// <remarks>If successful, <paramref name="stream"/> will be read completely, from the position

-        /// at the start of the call.</remarks>

-        /// <param name="stream">The stream to copy into a ByteString.</param>

-        /// <returns>A ByteString with content read from the given stream.</returns>

-        public static ByteString FromStream(Stream stream)

-        {

-            ProtoPreconditions.CheckNotNull(stream, nameof(stream));

-            int capacity = stream.CanSeek ? checked((int) (stream.Length - stream.Position)) : 0;

-            var memoryStream = new MemoryStream(capacity);

-            stream.CopyTo(memoryStream);

-#if NETSTANDARD1_1 || NETSTANDARD2_0

-            byte[] bytes = memoryStream.ToArray();

-#else

-            // Avoid an extra copy if we can.

-            byte[] bytes = memoryStream.Length == memoryStream.Capacity ? memoryStream.GetBuffer() : memoryStream.ToArray();

-#endif

-            return AttachBytes(bytes);

-        }

-

-#if !NET35

-        /// <summary>

-        /// Constructs a <see cref="ByteString"/> from data in the given stream, asynchronously.

-        /// </summary>

-        /// <remarks>If successful, <paramref name="stream"/> will be read completely, from the position

-        /// at the start of the call.</remarks>

-        /// <param name="stream">The stream to copy into a ByteString.</param>

-        /// <param name="cancellationToken">The cancellation token to use when reading from the stream, if any.</param>

-        /// <returns>A ByteString with content read from the given stream.</returns>

-        public static Task<ByteString> FromStreamAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))

-        {

-            ProtoPreconditions.CheckNotNull(stream, nameof(stream));

-            return ByteStringAsync.FromStreamAsyncCore(stream, cancellationToken);

-        }

-#endif

-

-        /// <summary>

-        /// Constructs a <see cref="ByteString" /> from the given array. The contents

-        /// are copied, so further modifications to the array will not

-        /// be reflected in the returned ByteString.

-        /// This method can also be invoked in <c>ByteString.CopyFrom(0xaa, 0xbb, ...)</c> form

-        /// which is primarily useful for testing.

-        /// </summary>

-        public static ByteString CopyFrom(params byte[] bytes)

-        {

-            return new ByteString((byte[]) bytes.Clone());

-        }

-

-        /// <summary>

-        /// Constructs a <see cref="ByteString" /> from a portion of a byte array.

-        /// </summary>

-        public static ByteString CopyFrom(byte[] bytes, int offset, int count)

-        {

-            byte[] portion = new byte[count];

-            ByteArray.Copy(bytes, offset, portion, 0, count);

-            return new ByteString(portion);

-        }

-

-        /// <summary>

-        /// Constructs a <see cref="ByteString" /> from a read only span. The contents

-        /// are copied, so further modifications to the span will not

-        /// be reflected in the returned <see cref="ByteString" />.

-        /// </summary>

-        public static ByteString CopyFrom(ReadOnlySpan<byte> bytes)

-        {

-            return new ByteString(bytes.ToArray());

-        }

-

-        /// <summary>

-        /// Creates a new <see cref="ByteString" /> by encoding the specified text with

-        /// the given encoding.

-        /// </summary>

-        public static ByteString CopyFrom(string text, Encoding encoding)

-        {

-            return new ByteString(encoding.GetBytes(text));

-        }

-

-        /// <summary>

-        /// Creates a new <see cref="ByteString" /> by encoding the specified text in UTF-8.

-        /// </summary>

-        public static ByteString CopyFromUtf8(string text)

-        {

-            return CopyFrom(text, Encoding.UTF8);

-        }

-

-        /// <summary>

-        /// Returns the byte at the given index.

-        /// </summary>

-        public byte this[int index]

-        {

-            get { return bytes.Span[index]; }

-        }

-

-        /// <summary>

-        /// Converts this <see cref="ByteString"/> into a string by applying the given encoding.

-        /// </summary>

-        /// <remarks>

-        /// This method should only be used to convert binary data which was the result of encoding

-        /// text with the given encoding.

-        /// </remarks>

-        /// <param name="encoding">The encoding to use to decode the binary data into text.</param>

-        /// <returns>The result of decoding the binary data with the given decoding.</returns>

-        public string ToString(Encoding encoding)

-        {

-            if (MemoryMarshal.TryGetArray(bytes, out ArraySegment<byte> segment))

-            {

-                // Fast path. ByteString was created with an array.

-                return encoding.GetString(segment.Array, segment.Offset, segment.Count);

-            }

-            else

-            {

-                // Slow path. BytesString is not an array. Convert memory and pass result to GetString.

-                // TODO: Consider using GetString overload that takes a pointer.

-                byte[] array = bytes.ToArray();

-                return encoding.GetString(array, 0, array.Length);

-            }

-        }

-

-        /// <summary>

-        /// Converts this <see cref="ByteString"/> into a string by applying the UTF-8 encoding.

-        /// </summary>

-        /// <remarks>

-        /// This method should only be used to convert binary data which was the result of encoding

-        /// text with UTF-8.

-        /// </remarks>

-        /// <returns>The result of decoding the binary data with the given decoding.</returns>

-        public string ToStringUtf8()

-        {

-            return ToString(Encoding.UTF8);

-        }

-

-        /// <summary>

-        /// Returns an iterator over the bytes in this <see cref="ByteString"/>.

-        /// </summary>

-        /// <returns>An iterator over the bytes in this object.</returns>

-        [SecuritySafeCritical]

-        public IEnumerator<byte> GetEnumerator()

-        {

-            return MemoryMarshal.ToEnumerable(bytes).GetEnumerator();

-        }

-

-        /// <summary>

-        /// Returns an iterator over the bytes in this <see cref="ByteString"/>.

-        /// </summary>

-        /// <returns>An iterator over the bytes in this object.</returns>

-        IEnumerator IEnumerable.GetEnumerator()

-        {

-            return GetEnumerator();

-        }

-

-        /// <summary>

-        /// Creates a CodedInputStream from this ByteString's data.

-        /// </summary>

-        public CodedInputStream CreateCodedInput()

-        {

-            // We trust CodedInputStream not to reveal the provided byte array or modify it

-            if (MemoryMarshal.TryGetArray(bytes, out ArraySegment<byte> segment) && segment.Count == bytes.Length)

-            {

-                // Fast path. ByteString was created with a complete array.

-                return new CodedInputStream(segment.Array, segment.Offset, segment.Count);

-            }

-            else

-            {

-                // Slow path. BytesString is not an array, or is a slice of an array.

-                // Convert memory and pass result to WriteRawBytes.

-                return new CodedInputStream(bytes.ToArray());

-            }

-        }

-

-        /// <summary>

-        /// Compares two byte strings for equality.

-        /// </summary>

-        /// <param name="lhs">The first byte string to compare.</param>

-        /// <param name="rhs">The second byte string to compare.</param>

-        /// <returns><c>true</c> if the byte strings are equal; false otherwise.</returns>

-        public static bool operator ==(ByteString lhs, ByteString rhs)

-        {

-            if (ReferenceEquals(lhs, rhs))

-            {

-                return true;

-            }

-            if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null))

-            {

-                return false;

-            }

-

-            return lhs.bytes.Span.SequenceEqual(rhs.bytes.Span);

-        }

-

-        /// <summary>

-        /// Compares two byte strings for inequality.

-        /// </summary>

-        /// <param name="lhs">The first byte string to compare.</param>

-        /// <param name="rhs">The second byte string to compare.</param>

-        /// <returns><c>false</c> if the byte strings are equal; true otherwise.</returns>

-        public static bool operator !=(ByteString lhs, ByteString rhs)

-        {

-            return !(lhs == rhs);

-        }

-

-        /// <summary>

-        /// Compares this byte string with another object.

-        /// </summary>

-        /// <param name="obj">The object to compare this with.</param>

-        /// <returns><c>true</c> if <paramref name="obj"/> refers to an equal <see cref="ByteString"/>; <c>false</c> otherwise.</returns>

-        [SecuritySafeCritical]

-        public override bool Equals(object obj)

-        {

-            return this == (obj as ByteString);

-        }

-

-        /// <summary>

-        /// Returns a hash code for this object. Two equal byte strings

-        /// will return the same hash code.

-        /// </summary>

-        /// <returns>A hash code for this object.</returns>

-        [SecuritySafeCritical]

-        public override int GetHashCode()

-        {

-            ReadOnlySpan<byte> b = bytes.Span;

-

-            int ret = 23;

-            for (int i = 0; i < b.Length; i++)

-            {

-                ret = (ret * 31) + b[i];

-            }

-            return ret;

-        }

-

-        /// <summary>

-        /// Compares this byte string with another.

-        /// </summary>

-        /// <param name="other">The <see cref="ByteString"/> to compare this with.</param>

-        /// <returns><c>true</c> if <paramref name="other"/> refers to an equal byte string; <c>false</c> otherwise.</returns>

-        public bool Equals(ByteString other)

-        {

-            return this == other;

-        }

-

-        /// <summary>

-        /// Copies the entire byte array to the destination array provided at the offset specified.

-        /// </summary>

-        public void CopyTo(byte[] array, int position)

-        {

-            bytes.CopyTo(array.AsMemory(position));

-        }

-

-        /// <summary>

-        /// Writes the entire byte array to the provided stream

-        /// </summary>

-        public void WriteTo(Stream outputStream)

-        {

-            if (MemoryMarshal.TryGetArray(bytes, out ArraySegment<byte> segment))

-            {

-                // Fast path. ByteString was created with an array, so pass the underlying array.

-                outputStream.Write(segment.Array, segment.Offset, segment.Count);

-            }

-            else

-            {

-                // Slow path. BytesString is not an array. Convert memory and pass result to WriteRawBytes.

-                var array = bytes.ToArray();

-                outputStream.Write(array, 0, array.Length);

-            }

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Text;
+#if !NET35
+using System.Threading;
+using System.Threading.Tasks;
+#endif
+#if NET35
+using Google.Protobuf.Compatibility;
+#endif
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Immutable array of bytes.
+    /// </summary>
+    [SecuritySafeCritical]
+    public sealed class ByteString : IEnumerable<byte>, IEquatable<ByteString>
+    {
+        private static readonly ByteString empty = new ByteString(new byte[0]);
+
+        private readonly ReadOnlyMemory<byte> bytes;
+
+        /// <summary>
+        /// Internal use only. Ensure that the provided memory is not mutated and belongs to this instance.
+        /// </summary>
+        internal static ByteString AttachBytes(ReadOnlyMemory<byte> bytes)
+        {
+            return new ByteString(bytes);
+        }
+
+        /// <summary>
+        /// Internal use only. Ensure that the provided memory is not mutated and belongs to this instance.
+        /// This method encapsulates converting array to memory. Reduces need for SecuritySafeCritical
+        /// in .NET Framework.
+        /// </summary>
+        internal static ByteString AttachBytes(byte[] bytes)
+        {
+            return AttachBytes(bytes.AsMemory());
+        }
+
+        /// <summary>
+        /// Constructs a new ByteString from the given memory. The memory is
+        /// *not* copied, and must not be modified after this constructor is called.
+        /// </summary>
+        private ByteString(ReadOnlyMemory<byte> bytes)
+        {
+            this.bytes = bytes;
+        }
+
+        /// <summary>
+        /// Returns an empty ByteString.
+        /// </summary>
+        public static ByteString Empty
+        {
+            get { return empty; }
+        }
+
+        /// <summary>
+        /// Returns the length of this ByteString in bytes.
+        /// </summary>
+        public int Length
+        {
+            get { return bytes.Length; }
+        }
+
+        /// <summary>
+        /// Returns <c>true</c> if this byte string is empty, <c>false</c> otherwise.
+        /// </summary>
+        public bool IsEmpty
+        {
+            get { return Length == 0; }
+        }
+
+        /// <summary>
+        /// Provides read-only access to the data of this <see cref="ByteString"/>.
+        /// No data is copied so this is the most efficient way of accessing.
+        /// </summary>
+        public ReadOnlySpan<byte> Span
+        {
+            get { return bytes.Span; }
+        }
+
+        /// <summary>
+        /// Provides read-only access to the data of this <see cref="ByteString"/>.
+        /// No data is copied so this is the most efficient way of accessing.
+        /// </summary>
+        public ReadOnlyMemory<byte> Memory
+        {
+            get { return bytes; }
+        }
+
+        /// <summary>
+        /// Converts this <see cref="ByteString"/> into a byte array.
+        /// </summary>
+        /// <remarks>The data is copied - changes to the returned array will not be reflected in this <c>ByteString</c>.</remarks>
+        /// <returns>A byte array with the same data as this <c>ByteString</c>.</returns>
+        public byte[] ToByteArray()
+        {
+            return bytes.ToArray();
+        }
+
+        /// <summary>
+        /// Converts this <see cref="ByteString"/> into a standard base64 representation.
+        /// </summary>
+        /// <returns>A base64 representation of this <c>ByteString</c>.</returns>
+        public string ToBase64()
+        {
+            if (MemoryMarshal.TryGetArray(bytes, out ArraySegment<byte> segment))
+            {
+                // Fast path. ByteString was created with an array, so pass the underlying array.
+                return Convert.ToBase64String(segment.Array, segment.Offset, segment.Count);
+            }
+            else
+            {
+                // Slow path. BytesString is not an array. Convert memory and pass result to ToBase64String.
+                return Convert.ToBase64String(bytes.ToArray());
+            }
+        }
+
+        /// <summary>
+        /// Constructs a <see cref="ByteString" /> from the Base64 Encoded String.
+        /// </summary>
+        public static ByteString FromBase64(string bytes)
+        {
+            // By handling the empty string explicitly, we not only optimize but we fix a
+            // problem on CF 2.0. See issue 61 for details.
+            return bytes == "" ? Empty : new ByteString(Convert.FromBase64String(bytes));
+        }
+
+        /// <summary>
+        /// Constructs a <see cref="ByteString"/> from data in the given stream, synchronously.
+        /// </summary>
+        /// <remarks>If successful, <paramref name="stream"/> will be read completely, from the position
+        /// at the start of the call.</remarks>
+        /// <param name="stream">The stream to copy into a ByteString.</param>
+        /// <returns>A ByteString with content read from the given stream.</returns>
+        public static ByteString FromStream(Stream stream)
+        {
+            ProtoPreconditions.CheckNotNull(stream, nameof(stream));
+            int capacity = stream.CanSeek ? checked((int) (stream.Length - stream.Position)) : 0;
+            var memoryStream = new MemoryStream(capacity);
+            stream.CopyTo(memoryStream);
+#if NETSTANDARD1_1 || NETSTANDARD2_0
+            byte[] bytes = memoryStream.ToArray();
+#else
+            // Avoid an extra copy if we can.
+            byte[] bytes = memoryStream.Length == memoryStream.Capacity ? memoryStream.GetBuffer() : memoryStream.ToArray();
+#endif
+            return AttachBytes(bytes);
+        }
+
+#if !NET35
+        /// <summary>
+        /// Constructs a <see cref="ByteString"/> from data in the given stream, asynchronously.
+        /// </summary>
+        /// <remarks>If successful, <paramref name="stream"/> will be read completely, from the position
+        /// at the start of the call.</remarks>
+        /// <param name="stream">The stream to copy into a ByteString.</param>
+        /// <param name="cancellationToken">The cancellation token to use when reading from the stream, if any.</param>
+        /// <returns>A ByteString with content read from the given stream.</returns>
+        public static Task<ByteString> FromStreamAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
+        {
+            ProtoPreconditions.CheckNotNull(stream, nameof(stream));
+            return ByteStringAsync.FromStreamAsyncCore(stream, cancellationToken);
+        }
+#endif
+
+        /// <summary>
+        /// Constructs a <see cref="ByteString" /> from the given array. The contents
+        /// are copied, so further modifications to the array will not
+        /// be reflected in the returned ByteString.
+        /// This method can also be invoked in <c>ByteString.CopyFrom(0xaa, 0xbb, ...)</c> form
+        /// which is primarily useful for testing.
+        /// </summary>
+        public static ByteString CopyFrom(params byte[] bytes)
+        {
+            return new ByteString((byte[]) bytes.Clone());
+        }
+
+        /// <summary>
+        /// Constructs a <see cref="ByteString" /> from a portion of a byte array.
+        /// </summary>
+        public static ByteString CopyFrom(byte[] bytes, int offset, int count)
+        {
+            byte[] portion = new byte[count];
+            ByteArray.Copy(bytes, offset, portion, 0, count);
+            return new ByteString(portion);
+        }
+
+        /// <summary>
+        /// Constructs a <see cref="ByteString" /> from a read only span. The contents
+        /// are copied, so further modifications to the span will not
+        /// be reflected in the returned <see cref="ByteString" />.
+        /// </summary>
+        public static ByteString CopyFrom(ReadOnlySpan<byte> bytes)
+        {
+            return new ByteString(bytes.ToArray());
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="ByteString" /> by encoding the specified text with
+        /// the given encoding.
+        /// </summary>
+        public static ByteString CopyFrom(string text, Encoding encoding)
+        {
+            return new ByteString(encoding.GetBytes(text));
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="ByteString" /> by encoding the specified text in UTF-8.
+        /// </summary>
+        public static ByteString CopyFromUtf8(string text)
+        {
+            return CopyFrom(text, Encoding.UTF8);
+        }
+
+        /// <summary>
+        /// Returns the byte at the given index.
+        /// </summary>
+        public byte this[int index]
+        {
+            get { return bytes.Span[index]; }
+        }
+
+        /// <summary>
+        /// Converts this <see cref="ByteString"/> into a string by applying the given encoding.
+        /// </summary>
+        /// <remarks>
+        /// This method should only be used to convert binary data which was the result of encoding
+        /// text with the given encoding.
+        /// </remarks>
+        /// <param name="encoding">The encoding to use to decode the binary data into text.</param>
+        /// <returns>The result of decoding the binary data with the given decoding.</returns>
+        public string ToString(Encoding encoding)
+        {
+            if (MemoryMarshal.TryGetArray(bytes, out ArraySegment<byte> segment))
+            {
+                // Fast path. ByteString was created with an array.
+                return encoding.GetString(segment.Array, segment.Offset, segment.Count);
+            }
+            else
+            {
+                // Slow path. BytesString is not an array. Convert memory and pass result to GetString.
+                // TODO: Consider using GetString overload that takes a pointer.
+                byte[] array = bytes.ToArray();
+                return encoding.GetString(array, 0, array.Length);
+            }
+        }
+
+        /// <summary>
+        /// Converts this <see cref="ByteString"/> into a string by applying the UTF-8 encoding.
+        /// </summary>
+        /// <remarks>
+        /// This method should only be used to convert binary data which was the result of encoding
+        /// text with UTF-8.
+        /// </remarks>
+        /// <returns>The result of decoding the binary data with the given decoding.</returns>
+        public string ToStringUtf8()
+        {
+            return ToString(Encoding.UTF8);
+        }
+
+        /// <summary>
+        /// Returns an iterator over the bytes in this <see cref="ByteString"/>.
+        /// </summary>
+        /// <returns>An iterator over the bytes in this object.</returns>
+        [SecuritySafeCritical]
+        public IEnumerator<byte> GetEnumerator()
+        {
+            return MemoryMarshal.ToEnumerable(bytes).GetEnumerator();
+        }
+
+        /// <summary>
+        /// Returns an iterator over the bytes in this <see cref="ByteString"/>.
+        /// </summary>
+        /// <returns>An iterator over the bytes in this object.</returns>
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        /// <summary>
+        /// Creates a CodedInputStream from this ByteString's data.
+        /// </summary>
+        public CodedInputStream CreateCodedInput()
+        {
+            // We trust CodedInputStream not to reveal the provided byte array or modify it
+            if (MemoryMarshal.TryGetArray(bytes, out ArraySegment<byte> segment) && segment.Count == bytes.Length)
+            {
+                // Fast path. ByteString was created with a complete array.
+                return new CodedInputStream(segment.Array, segment.Offset, segment.Count);
+            }
+            else
+            {
+                // Slow path. BytesString is not an array, or is a slice of an array.
+                // Convert memory and pass result to WriteRawBytes.
+                return new CodedInputStream(bytes.ToArray());
+            }
+        }
+
+        /// <summary>
+        /// Compares two byte strings for equality.
+        /// </summary>
+        /// <param name="lhs">The first byte string to compare.</param>
+        /// <param name="rhs">The second byte string to compare.</param>
+        /// <returns><c>true</c> if the byte strings are equal; false otherwise.</returns>
+        public static bool operator ==(ByteString lhs, ByteString rhs)
+        {
+            if (ReferenceEquals(lhs, rhs))
+            {
+                return true;
+            }
+            if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null))
+            {
+                return false;
+            }
+
+            return lhs.bytes.Span.SequenceEqual(rhs.bytes.Span);
+        }
+
+        /// <summary>
+        /// Compares two byte strings for inequality.
+        /// </summary>
+        /// <param name="lhs">The first byte string to compare.</param>
+        /// <param name="rhs">The second byte string to compare.</param>
+        /// <returns><c>false</c> if the byte strings are equal; true otherwise.</returns>
+        public static bool operator !=(ByteString lhs, ByteString rhs)
+        {
+            return !(lhs == rhs);
+        }
+
+        /// <summary>
+        /// Compares this byte string with another object.
+        /// </summary>
+        /// <param name="obj">The object to compare this with.</param>
+        /// <returns><c>true</c> if <paramref name="obj"/> refers to an equal <see cref="ByteString"/>; <c>false</c> otherwise.</returns>
+        [SecuritySafeCritical]
+        public override bool Equals(object obj)
+        {
+            return this == (obj as ByteString);
+        }
+
+        /// <summary>
+        /// Returns a hash code for this object. Two equal byte strings
+        /// will return the same hash code.
+        /// </summary>
+        /// <returns>A hash code for this object.</returns>
+        [SecuritySafeCritical]
+        public override int GetHashCode()
+        {
+            ReadOnlySpan<byte> b = bytes.Span;
+
+            int ret = 23;
+            for (int i = 0; i < b.Length; i++)
+            {
+                ret = (ret * 31) + b[i];
+            }
+            return ret;
+        }
+
+        /// <summary>
+        /// Compares this byte string with another.
+        /// </summary>
+        /// <param name="other">The <see cref="ByteString"/> to compare this with.</param>
+        /// <returns><c>true</c> if <paramref name="other"/> refers to an equal byte string; <c>false</c> otherwise.</returns>
+        public bool Equals(ByteString other)
+        {
+            return this == other;
+        }
+
+        /// <summary>
+        /// Copies the entire byte array to the destination array provided at the offset specified.
+        /// </summary>
+        public void CopyTo(byte[] array, int position)
+        {
+            bytes.CopyTo(array.AsMemory(position));
+        }
+
+        /// <summary>
+        /// Writes the entire byte array to the provided stream
+        /// </summary>
+        public void WriteTo(Stream outputStream)
+        {
+            if (MemoryMarshal.TryGetArray(bytes, out ArraySegment<byte> segment))
+            {
+                // Fast path. ByteString was created with an array, so pass the underlying array.
+                outputStream.Write(segment.Array, segment.Offset, segment.Count);
+            }
+            else
+            {
+                // Slow path. BytesString is not an array. Convert memory and pass result to WriteRawBytes.
+                var array = bytes.ToArray();
+                outputStream.Write(array, 0, array.Length);
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/CodedInputStream.cs b/csharp/src/Google.Protobuf/CodedInputStream.cs
index 27b23c0..912c11f 100644
--- a/csharp/src/Google.Protobuf/CodedInputStream.cs
+++ b/csharp/src/Google.Protobuf/CodedInputStream.cs
@@ -1,698 +1,698 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using Google.Protobuf.Collections;

-using System;

-using System.Collections.Generic;

-using System.IO;

-using System.Runtime.CompilerServices;

-using System.Runtime.InteropServices;

-using System.Security;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Reads and decodes protocol message fields.

-    /// </summary>

-    /// <remarks>

-    /// <para>

-    /// This class is generally used by generated code to read appropriate

-    /// primitives from the stream. It effectively encapsulates the lowest

-    /// levels of protocol buffer format.

-    /// </para>

-    /// <para>

-    /// Repeated fields and map fields are not handled by this class; use <see cref="RepeatedField{T}"/>

-    /// and <see cref="MapField{TKey, TValue}"/> to serialize such fields.

-    /// </para>

-    /// </remarks>

-    [SecuritySafeCritical]

-    public sealed class CodedInputStream : IDisposable

-    {

-        /// <summary>

-        /// Whether to leave the underlying stream open when disposing of this stream.

-        /// This is always true when there's no stream.

-        /// </summary>

-        private readonly bool leaveOpen;

-

-        /// <summary>

-        /// Buffer of data read from the stream or provided at construction time.

-        /// </summary>

-        private readonly byte[] buffer;

-

-        /// <summary>

-        /// The stream to read further input from, or null if the byte array buffer was provided

-        /// directly on construction, with no further data available.

-        /// </summary>

-        private readonly Stream input;

-

-        /// <summary>

-        /// The parser state is kept separately so that other parse implementations can reuse the same

-        /// parsing primitives.

-        /// </summary>

-        private ParserInternalState state;

-

-        internal const int DefaultRecursionLimit = 100;

-        internal const int DefaultSizeLimit = Int32.MaxValue;

-        internal const int BufferSize = 4096;

-

-        #region Construction

-        // Note that the checks are performed such that we don't end up checking obviously-valid things

-        // like non-null references for arrays we've just created.

-

-        /// <summary>

-        /// Creates a new CodedInputStream reading data from the given byte array.

-        /// </summary>

-        public CodedInputStream(byte[] buffer) : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), 0, buffer.Length, true)

-        {            

-        }

-

-        /// <summary>

-        /// Creates a new <see cref="CodedInputStream"/> that reads from the given byte array slice.

-        /// </summary>

-        public CodedInputStream(byte[] buffer, int offset, int length)

-            : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), offset, offset + length, true)

-        {            

-            if (offset < 0 || offset > buffer.Length)

-            {

-                throw new ArgumentOutOfRangeException("offset", "Offset must be within the buffer");

-            }

-            if (length < 0 || offset + length > buffer.Length)

-            {

-                throw new ArgumentOutOfRangeException("length", "Length must be non-negative and within the buffer");

-            }

-        }

-

-        /// <summary>

-        /// Creates a new <see cref="CodedInputStream"/> reading data from the given stream, which will be disposed

-        /// when the returned object is disposed.

-        /// </summary>

-        /// <param name="input">The stream to read from.</param>

-        public CodedInputStream(Stream input) : this(input, false)

-        {

-        }

-

-        /// <summary>

-        /// Creates a new <see cref="CodedInputStream"/> reading data from the given stream.

-        /// </summary>

-        /// <param name="input">The stream to read from.</param>

-        /// <param name="leaveOpen"><c>true</c> to leave <paramref name="input"/> open when the returned

-        /// <c cref="CodedInputStream"/> is disposed; <c>false</c> to dispose of the given stream when the

-        /// returned object is disposed.</param>

-        public CodedInputStream(Stream input, bool leaveOpen)

-            : this(ProtoPreconditions.CheckNotNull(input, "input"), new byte[BufferSize], 0, 0, leaveOpen)

-        {

-        }

-        

-        /// <summary>

-        /// Creates a new CodedInputStream reading data from the given

-        /// stream and buffer, using the default limits.

-        /// </summary>

-        internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, bool leaveOpen)

-        {

-            this.input = input;

-            this.buffer = buffer;

-            this.state.bufferPos = bufferPos;

-            this.state.bufferSize = bufferSize;

-            this.state.sizeLimit = DefaultSizeLimit;

-            this.state.recursionLimit = DefaultRecursionLimit;

-            SegmentedBufferHelper.Initialize(this, out this.state.segmentedBufferHelper);

-            this.leaveOpen = leaveOpen;

-

-            this.state.currentLimit = int.MaxValue;

-        }

-

-        /// <summary>

-        /// Creates a new CodedInputStream reading data from the given

-        /// stream and buffer, using the specified limits.

-        /// </summary>

-        /// <remarks>

-        /// This chains to the version with the default limits instead of vice versa to avoid

-        /// having to check that the default values are valid every time.

-        /// </remarks>

-        internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, int sizeLimit, int recursionLimit, bool leaveOpen)

-            : this(input, buffer, bufferPos, bufferSize, leaveOpen)

-        {

-            if (sizeLimit <= 0)

-            {

-                throw new ArgumentOutOfRangeException("sizeLimit", "Size limit must be positive");

-            }

-            if (recursionLimit <= 0)

-            {

-                throw new ArgumentOutOfRangeException("recursionLimit!", "Recursion limit must be positive");

-            }

-            this.state.sizeLimit = sizeLimit;

-            this.state.recursionLimit = recursionLimit;

-        }

-        #endregion

-

-        /// <summary>

-        /// Creates a <see cref="CodedInputStream"/> with the specified size and recursion limits, reading

-        /// from an input stream.

-        /// </summary>

-        /// <remarks>

-        /// This method exists separately from the constructor to reduce the number of constructor overloads.

-        /// It is likely to be used considerably less frequently than the constructors, as the default limits

-        /// are suitable for most use cases.

-        /// </remarks>

-        /// <param name="input">The input stream to read from</param>

-        /// <param name="sizeLimit">The total limit of data to read from the stream.</param>

-        /// <param name="recursionLimit">The maximum recursion depth to allow while reading.</param>

-        /// <returns>A <c>CodedInputStream</c> reading from <paramref name="input"/> with the specified size

-        /// and recursion limits.</returns>

-        public static CodedInputStream CreateWithLimits(Stream input, int sizeLimit, int recursionLimit)

-        {

-            // Note: we may want an overload accepting leaveOpen

-            return new CodedInputStream(input, new byte[BufferSize], 0, 0, sizeLimit, recursionLimit, false);

-        }

-

-        /// <summary>

-        /// Returns the current position in the input stream, or the position in the input buffer

-        /// </summary>

-        public long Position 

-        {

-            get

-            {

-                if (input != null)

-                {

-                    return input.Position - ((state.bufferSize + state.bufferSizeAfterLimit) - state.bufferPos);

-                }

-                return state.bufferPos;

-            }

-        }

-

-        /// <summary>

-        /// Returns the last tag read, or 0 if no tags have been read or we've read beyond

-        /// the end of the stream.

-        /// </summary>

-        internal uint LastTag { get { return state.lastTag; } }

-

-        /// <summary>

-        /// Returns the size limit for this stream.

-        /// </summary>

-        /// <remarks>

-        /// This limit is applied when reading from the underlying stream, as a sanity check. It is

-        /// not applied when reading from a byte array data source without an underlying stream.

-        /// The default value is Int32.MaxValue.

-        /// </remarks>

-        /// <value>

-        /// The size limit.

-        /// </value>

-        public int SizeLimit { get { return state.sizeLimit; } }

-

-        /// <summary>

-        /// Returns the recursion limit for this stream. This limit is applied whilst reading messages,

-        /// to avoid maliciously-recursive data.

-        /// </summary>

-        /// <remarks>

-        /// The default limit is 100.

-        /// </remarks>

-        /// <value>

-        /// The recursion limit for this stream.

-        /// </value>

-        public int RecursionLimit { get { return state.recursionLimit; } }

-

-        /// <summary>

-        /// Internal-only property; when set to true, unknown fields will be discarded while parsing.

-        /// </summary>

-        internal bool DiscardUnknownFields

-        {

-            get { return state.DiscardUnknownFields; }

-            set { state.DiscardUnknownFields = value; }

-        }

-

-        /// <summary>

-        /// Internal-only property; provides extension identifiers to compatible messages while parsing.

-        /// </summary>

-        internal ExtensionRegistry ExtensionRegistry

-        {

-            get { return state.ExtensionRegistry; }

-            set { state.ExtensionRegistry = value; }

-        }

-

-        internal byte[] InternalBuffer => buffer;

-

-        internal Stream InternalInputStream => input;

-

-        internal ref ParserInternalState InternalState => ref state;

-

-        /// <summary>

-        /// Disposes of this instance, potentially closing any underlying stream.

-        /// </summary>

-        /// <remarks>

-        /// As there is no flushing to perform here, disposing of a <see cref="CodedInputStream"/> which

-        /// was constructed with the <c>leaveOpen</c> option parameter set to <c>true</c> (or one which

-        /// was constructed to read from a byte array) has no effect.

-        /// </remarks>

-        public void Dispose()

-        {

-            if (!leaveOpen)

-            {

-                input.Dispose();

-            }

-        }

-

-        #region Validation

-        /// <summary>

-        /// Verifies that the last call to ReadTag() returned tag 0 - in other words,

-        /// we've reached the end of the stream when we expected to.

-        /// </summary>

-        /// <exception cref="InvalidProtocolBufferException">The 

-        /// tag read was not the one specified</exception>

-        internal void CheckReadEndOfStreamTag()

-        {

-            ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref state);

-        }

-        #endregion

-

-        #region Reading of tags etc

-

-        /// <summary>

-        /// Peeks at the next field tag. This is like calling <see cref="ReadTag"/>, but the

-        /// tag is not consumed. (So a subsequent call to <see cref="ReadTag"/> will return the

-        /// same value.)

-        /// </summary>

-        public uint PeekTag()

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.PeekTag(ref span, ref state);

-        }

-

-        /// <summary>

-        /// Reads a field tag, returning the tag of 0 for "end of stream".

-        /// </summary>

-        /// <remarks>

-        /// If this method returns 0, it doesn't necessarily mean the end of all

-        /// the data in this CodedInputStream; it may be the end of the logical stream

-        /// for an embedded message, for example.

-        /// </remarks>

-        /// <returns>The next field tag, or 0 for end of stream. (0 is never a valid tag.)</returns>

-        public uint ReadTag()

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.ParseTag(ref span, ref state);

-        }

-

-        /// <summary>

-        /// Skips the data for the field with the tag we've just read.

-        /// This should be called directly after <see cref="ReadTag"/>, when

-        /// the caller wishes to skip an unknown field.

-        /// </summary>

-        /// <remarks>

-        /// This method throws <see cref="InvalidProtocolBufferException"/> if the last-read tag was an end-group tag.

-        /// If a caller wishes to skip a group, they should skip the whole group, by calling this method after reading the

-        /// start-group tag. This behavior allows callers to call this method on any field they don't understand, correctly

-        /// resulting in an error if an end-group tag has not been paired with an earlier start-group tag.

-        /// </remarks>

-        /// <exception cref="InvalidProtocolBufferException">The last tag was an end-group tag</exception>

-        /// <exception cref="InvalidOperationException">The last read operation read to the end of the logical stream</exception>

-        public void SkipLastField()

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            ParsingPrimitivesMessages.SkipLastField(ref span, ref state);

-        }

-

-        /// <summary>

-        /// Skip a group.

-        /// </summary>

-        internal void SkipGroup(uint startGroupTag)

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            ParsingPrimitivesMessages.SkipGroup(ref span, ref state, startGroupTag);

-        }

-

-        /// <summary>

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

-        /// </summary>

-        public double ReadDouble()

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.ParseDouble(ref span, ref state);

-        }

-

-        /// <summary>

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

-        /// </summary>

-        public float ReadFloat()

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.ParseFloat(ref span, ref state);

-        }

-

-        /// <summary>

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

-        /// </summary>

-        public ulong ReadUInt64()

-        {

-            return ReadRawVarint64();

-        }

-

-        /// <summary>

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

-        /// </summary>

-        public long ReadInt64()

-        {

-            return (long) ReadRawVarint64();

-        }

-

-        /// <summary>

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

-        /// </summary>

-        public int ReadInt32()

-        {

-            return (int) ReadRawVarint32();

-        }

-

-        /// <summary>

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

-        /// </summary>

-        public ulong ReadFixed64()

-        {

-            return ReadRawLittleEndian64();

-        }

-

-        /// <summary>

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

-        /// </summary>

-        public uint ReadFixed32()

-        {

-            return ReadRawLittleEndian32();

-        }

-

-        /// <summary>

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

-        /// </summary>

-        public bool ReadBool()

-        {

-            return ReadRawVarint64() != 0;

-        }

-

-        /// <summary>

-        /// Reads a string field from the stream.

-        /// </summary>

-        public string ReadString()

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.ReadString(ref span, ref state);

-        }

-

-        /// <summary>

-        /// Reads an embedded message field value from the stream.

-        /// </summary>

-        public void ReadMessage(IMessage builder)

-        {

-            // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalMergeFrom method),

-            // what we're doing here works fine, but could be more efficient.

-            // What happends is that we first initialize a ParseContext from the current coded input stream only to parse the length of the message, at which point

-            // we will need to switch back again to CodedInputStream-based parsing (which involves copying and storing the state) to be able to

-            // invoke the legacy MergeFrom(CodedInputStream) method.

-            // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it).

-            ParseContext.Initialize(buffer.AsSpan(), ref state, out ParseContext ctx);

-            try

-            {

-                ParsingPrimitivesMessages.ReadMessage(ref ctx, builder);

-            }

-            finally

-            {

-                ctx.CopyStateTo(this);

-            }

-        }

-

-        /// <summary>

-        /// Reads an embedded group field from the stream.

-        /// </summary>

-        public void ReadGroup(IMessage builder)

-        {

-            ParseContext.Initialize(this, out ParseContext ctx);

-            try

-            {

-                ParsingPrimitivesMessages.ReadGroup(ref ctx, builder);

-            }

-            finally

-            {

-                ctx.CopyStateTo(this);

-            }

-        }

-

-        /// <summary>

-        /// Reads a bytes field value from the stream.

-        /// </summary>   

-        public ByteString ReadBytes()

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.ReadBytes(ref span, ref state);

-        }

-

-        /// <summary>

-        /// Reads a uint32 field value from the stream.

-        /// </summary>   

-        public uint ReadUInt32()

-        {

-            return ReadRawVarint32();

-        }

-

-        /// <summary>

-        /// Reads an enum field value from the stream.

-        /// </summary>   

-        public int ReadEnum()

-        {

-            // Currently just a pass-through, but it's nice to separate it logically from WriteInt32.

-            return (int) ReadRawVarint32();

-        }

-

-        /// <summary>

-        /// Reads an sfixed32 field value from the stream.

-        /// </summary>   

-        public int ReadSFixed32()

-        {

-            return (int) ReadRawLittleEndian32();

-        }

-

-        /// <summary>

-        /// Reads an sfixed64 field value from the stream.

-        /// </summary>   

-        public long ReadSFixed64()

-        {

-            return (long) ReadRawLittleEndian64();

-        }

-

-        /// <summary>

-        /// Reads an sint32 field value from the stream.

-        /// </summary>   

-        public int ReadSInt32()

-        {

-            return ParsingPrimitives.DecodeZigZag32(ReadRawVarint32());

-        }

-

-        /// <summary>

-        /// Reads an sint64 field value from the stream.

-        /// </summary>   

-        public long ReadSInt64()

-        {

-            return ParsingPrimitives.DecodeZigZag64(ReadRawVarint64());

-        }

-

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

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.ParseLength(ref span, ref state);

-        }

-

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

-        /// </summary>

-        public bool MaybeConsumeTag(uint tag)

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.MaybeConsumeTag(ref span, ref state, tag);

-        }

-

-#endregion

-

-        #region Underlying reading primitives

-

-        /// <summary>

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

-        internal uint ReadRawVarint32()

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.ParseRawVarint32(ref span, ref state);

-        }

-

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

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

-        internal static uint ReadRawVarint32(Stream input)

-        {

-            return ParsingPrimitives.ReadRawVarint32(input);

-        }

-

-        /// <summary>

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

-        /// </summary>

-        internal ulong ReadRawVarint64()

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.ParseRawVarint64(ref span, ref state);

-        }

-

-        /// <summary>

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

-        /// </summary>

-        internal uint ReadRawLittleEndian32()

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.ParseRawLittleEndian32(ref span, ref state);

-        }

-

-        /// <summary>

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

-        /// </summary>

-        internal ulong ReadRawLittleEndian64()

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.ParseRawLittleEndian64(ref span, ref state);

-        }

-        #endregion

-

-        #region Internal reading and buffer management

-

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

-        internal int PushLimit(int byteLimit)

-        {

-            return SegmentedBufferHelper.PushLimit(ref state, byteLimit);

-        }

-

-        /// <summary>

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

-        /// </summary>

-        internal void PopLimit(int oldLimit)

-        {

-            SegmentedBufferHelper.PopLimit(ref state, oldLimit);

-        }

-

-        /// <summary>

-        /// Returns whether or not all the data before the limit has been read.

-        /// </summary>

-        /// <returns></returns>

-        internal bool ReachedLimit

-        {

-            get

-            {

-                return SegmentedBufferHelper.IsReachedLimit(ref state);

-            }

-        }

-

-        /// <summary>

-        /// Returns true if the stream has reached the end of the input. This is the

-        /// case if either the end of the underlying input source has been reached or

-        /// the stream has reached a limit created using PushLimit.

-        /// </summary>

-        public bool IsAtEnd

-        {

-            get

-            {

-                var span = new ReadOnlySpan<byte>(buffer);

-                return SegmentedBufferHelper.IsAtEnd(ref span, ref state);

-            }

-        }

-

-        /// <summary>

-        /// Called when buffer is empty to read more bytes from the

-        /// input.  If <paramref name="mustSucceed"/> is true, RefillBuffer() guarantees that

-        /// either there will be at least one byte in the buffer when it returns

-        /// or it will throw an exception.  If <paramref name="mustSucceed"/> is false,

-        /// RefillBuffer() returns false if no more bytes were available.

-        /// </summary>

-        /// <param name="mustSucceed"></param>

-        /// <returns></returns>

-        private bool RefillBuffer(bool mustSucceed)

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return state.segmentedBufferHelper.RefillBuffer(ref span, ref state, mustSucceed);

-        }

-

-        /// <summary>

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

-        internal byte[] ReadRawBytes(int size)

-        {

-            var span = new ReadOnlySpan<byte>(buffer);

-            return ParsingPrimitives.ReadRawBytes(ref span, ref state, size);

-        }

-

-        /// <summary>

-        /// Reads a top-level message or a nested message after the limits for this message have been pushed.

-        /// (parser will proceed until the end of the current limit)

-        /// NOTE: this method needs to be public because it's invoked by the generated code - e.g. msg.MergeFrom(CodedInputStream input) method

-        /// </summary>

-        public void ReadRawMessage(IMessage message)

-        {

-            ParseContext.Initialize(this, out ParseContext ctx);

-            try

-            {

-                ParsingPrimitivesMessages.ReadRawMessage(ref ctx, message);

-            }

-            finally

-            {

-                ctx.CopyStateTo(this);

-            }

-        }

-#endregion

-    }

-}

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using Google.Protobuf.Collections;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Reads and decodes protocol message fields.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// This class is generally used by generated code to read appropriate
+    /// primitives from the stream. It effectively encapsulates the lowest
+    /// levels of protocol buffer format.
+    /// </para>
+    /// <para>
+    /// Repeated fields and map fields are not handled by this class; use <see cref="RepeatedField{T}"/>
+    /// and <see cref="MapField{TKey, TValue}"/> to serialize such fields.
+    /// </para>
+    /// </remarks>
+    [SecuritySafeCritical]
+    public sealed class CodedInputStream : IDisposable
+    {
+        /// <summary>
+        /// Whether to leave the underlying stream open when disposing of this stream.
+        /// This is always true when there's no stream.
+        /// </summary>
+        private readonly bool leaveOpen;
+
+        /// <summary>
+        /// Buffer of data read from the stream or provided at construction time.
+        /// </summary>
+        private readonly byte[] buffer;
+
+        /// <summary>
+        /// The stream to read further input from, or null if the byte array buffer was provided
+        /// directly on construction, with no further data available.
+        /// </summary>
+        private readonly Stream input;
+
+        /// <summary>
+        /// The parser state is kept separately so that other parse implementations can reuse the same
+        /// parsing primitives.
+        /// </summary>
+        private ParserInternalState state;
+
+        internal const int DefaultRecursionLimit = 100;
+        internal const int DefaultSizeLimit = Int32.MaxValue;
+        internal const int BufferSize = 4096;
+
+        #region Construction
+        // Note that the checks are performed such that we don't end up checking obviously-valid things
+        // like non-null references for arrays we've just created.
+
+        /// <summary>
+        /// Creates a new CodedInputStream reading data from the given byte array.
+        /// </summary>
+        public CodedInputStream(byte[] buffer) : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), 0, buffer.Length, true)
+        {            
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="CodedInputStream"/> that reads from the given byte array slice.
+        /// </summary>
+        public CodedInputStream(byte[] buffer, int offset, int length)
+            : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), offset, offset + length, true)
+        {            
+            if (offset < 0 || offset > buffer.Length)
+            {
+                throw new ArgumentOutOfRangeException("offset", "Offset must be within the buffer");
+            }
+            if (length < 0 || offset + length > buffer.Length)
+            {
+                throw new ArgumentOutOfRangeException("length", "Length must be non-negative and within the buffer");
+            }
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="CodedInputStream"/> reading data from the given stream, which will be disposed
+        /// when the returned object is disposed.
+        /// </summary>
+        /// <param name="input">The stream to read from.</param>
+        public CodedInputStream(Stream input) : this(input, false)
+        {
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="CodedInputStream"/> reading data from the given stream.
+        /// </summary>
+        /// <param name="input">The stream to read from.</param>
+        /// <param name="leaveOpen"><c>true</c> to leave <paramref name="input"/> open when the returned
+        /// <c cref="CodedInputStream"/> is disposed; <c>false</c> to dispose of the given stream when the
+        /// returned object is disposed.</param>
+        public CodedInputStream(Stream input, bool leaveOpen)
+            : this(ProtoPreconditions.CheckNotNull(input, "input"), new byte[BufferSize], 0, 0, leaveOpen)
+        {
+        }
+        
+        /// <summary>
+        /// Creates a new CodedInputStream reading data from the given
+        /// stream and buffer, using the default limits.
+        /// </summary>
+        internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, bool leaveOpen)
+        {
+            this.input = input;
+            this.buffer = buffer;
+            this.state.bufferPos = bufferPos;
+            this.state.bufferSize = bufferSize;
+            this.state.sizeLimit = DefaultSizeLimit;
+            this.state.recursionLimit = DefaultRecursionLimit;
+            SegmentedBufferHelper.Initialize(this, out this.state.segmentedBufferHelper);
+            this.leaveOpen = leaveOpen;
+
+            this.state.currentLimit = int.MaxValue;
+        }
+
+        /// <summary>
+        /// Creates a new CodedInputStream reading data from the given
+        /// stream and buffer, using the specified limits.
+        /// </summary>
+        /// <remarks>
+        /// This chains to the version with the default limits instead of vice versa to avoid
+        /// having to check that the default values are valid every time.
+        /// </remarks>
+        internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, int sizeLimit, int recursionLimit, bool leaveOpen)
+            : this(input, buffer, bufferPos, bufferSize, leaveOpen)
+        {
+            if (sizeLimit <= 0)
+            {
+                throw new ArgumentOutOfRangeException("sizeLimit", "Size limit must be positive");
+            }
+            if (recursionLimit <= 0)
+            {
+                throw new ArgumentOutOfRangeException("recursionLimit!", "Recursion limit must be positive");
+            }
+            this.state.sizeLimit = sizeLimit;
+            this.state.recursionLimit = recursionLimit;
+        }
+        #endregion
+
+        /// <summary>
+        /// Creates a <see cref="CodedInputStream"/> with the specified size and recursion limits, reading
+        /// from an input stream.
+        /// </summary>
+        /// <remarks>
+        /// This method exists separately from the constructor to reduce the number of constructor overloads.
+        /// It is likely to be used considerably less frequently than the constructors, as the default limits
+        /// are suitable for most use cases.
+        /// </remarks>
+        /// <param name="input">The input stream to read from</param>
+        /// <param name="sizeLimit">The total limit of data to read from the stream.</param>
+        /// <param name="recursionLimit">The maximum recursion depth to allow while reading.</param>
+        /// <returns>A <c>CodedInputStream</c> reading from <paramref name="input"/> with the specified size
+        /// and recursion limits.</returns>
+        public static CodedInputStream CreateWithLimits(Stream input, int sizeLimit, int recursionLimit)
+        {
+            // Note: we may want an overload accepting leaveOpen
+            return new CodedInputStream(input, new byte[BufferSize], 0, 0, sizeLimit, recursionLimit, false);
+        }
+
+        /// <summary>
+        /// Returns the current position in the input stream, or the position in the input buffer
+        /// </summary>
+        public long Position 
+        {
+            get
+            {
+                if (input != null)
+                {
+                    return input.Position - ((state.bufferSize + state.bufferSizeAfterLimit) - state.bufferPos);
+                }
+                return state.bufferPos;
+            }
+        }
+
+        /// <summary>
+        /// Returns the last tag read, or 0 if no tags have been read or we've read beyond
+        /// the end of the stream.
+        /// </summary>
+        internal uint LastTag { get { return state.lastTag; } }
+
+        /// <summary>
+        /// Returns the size limit for this stream.
+        /// </summary>
+        /// <remarks>
+        /// This limit is applied when reading from the underlying stream, as a sanity check. It is
+        /// not applied when reading from a byte array data source without an underlying stream.
+        /// The default value is Int32.MaxValue.
+        /// </remarks>
+        /// <value>
+        /// The size limit.
+        /// </value>
+        public int SizeLimit { get { return state.sizeLimit; } }
+
+        /// <summary>
+        /// Returns the recursion limit for this stream. This limit is applied whilst reading messages,
+        /// to avoid maliciously-recursive data.
+        /// </summary>
+        /// <remarks>
+        /// The default limit is 100.
+        /// </remarks>
+        /// <value>
+        /// The recursion limit for this stream.
+        /// </value>
+        public int RecursionLimit { get { return state.recursionLimit; } }
+
+        /// <summary>
+        /// Internal-only property; when set to true, unknown fields will be discarded while parsing.
+        /// </summary>
+        internal bool DiscardUnknownFields
+        {
+            get { return state.DiscardUnknownFields; }
+            set { state.DiscardUnknownFields = value; }
+        }
+
+        /// <summary>
+        /// Internal-only property; provides extension identifiers to compatible messages while parsing.
+        /// </summary>
+        internal ExtensionRegistry ExtensionRegistry
+        {
+            get { return state.ExtensionRegistry; }
+            set { state.ExtensionRegistry = value; }
+        }
+
+        internal byte[] InternalBuffer => buffer;
+
+        internal Stream InternalInputStream => input;
+
+        internal ref ParserInternalState InternalState => ref state;
+
+        /// <summary>
+        /// Disposes of this instance, potentially closing any underlying stream.
+        /// </summary>
+        /// <remarks>
+        /// As there is no flushing to perform here, disposing of a <see cref="CodedInputStream"/> which
+        /// was constructed with the <c>leaveOpen</c> option parameter set to <c>true</c> (or one which
+        /// was constructed to read from a byte array) has no effect.
+        /// </remarks>
+        public void Dispose()
+        {
+            if (!leaveOpen)
+            {
+                input.Dispose();
+            }
+        }
+
+        #region Validation
+        /// <summary>
+        /// Verifies that the last call to ReadTag() returned tag 0 - in other words,
+        /// we've reached the end of the stream when we expected to.
+        /// </summary>
+        /// <exception cref="InvalidProtocolBufferException">The 
+        /// tag read was not the one specified</exception>
+        internal void CheckReadEndOfStreamTag()
+        {
+            ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref state);
+        }
+        #endregion
+
+        #region Reading of tags etc
+
+        /// <summary>
+        /// Peeks at the next field tag. This is like calling <see cref="ReadTag"/>, but the
+        /// tag is not consumed. (So a subsequent call to <see cref="ReadTag"/> will return the
+        /// same value.)
+        /// </summary>
+        public uint PeekTag()
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.PeekTag(ref span, ref state);
+        }
+
+        /// <summary>
+        /// Reads a field tag, returning the tag of 0 for "end of stream".
+        /// </summary>
+        /// <remarks>
+        /// If this method returns 0, it doesn't necessarily mean the end of all
+        /// the data in this CodedInputStream; it may be the end of the logical stream
+        /// for an embedded message, for example.
+        /// </remarks>
+        /// <returns>The next field tag, or 0 for end of stream. (0 is never a valid tag.)</returns>
+        public uint ReadTag()
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.ParseTag(ref span, ref state);
+        }
+
+        /// <summary>
+        /// Skips the data for the field with the tag we've just read.
+        /// This should be called directly after <see cref="ReadTag"/>, when
+        /// the caller wishes to skip an unknown field.
+        /// </summary>
+        /// <remarks>
+        /// This method throws <see cref="InvalidProtocolBufferException"/> if the last-read tag was an end-group tag.
+        /// If a caller wishes to skip a group, they should skip the whole group, by calling this method after reading the
+        /// start-group tag. This behavior allows callers to call this method on any field they don't understand, correctly
+        /// resulting in an error if an end-group tag has not been paired with an earlier start-group tag.
+        /// </remarks>
+        /// <exception cref="InvalidProtocolBufferException">The last tag was an end-group tag</exception>
+        /// <exception cref="InvalidOperationException">The last read operation read to the end of the logical stream</exception>
+        public void SkipLastField()
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            ParsingPrimitivesMessages.SkipLastField(ref span, ref state);
+        }
+
+        /// <summary>
+        /// Skip a group.
+        /// </summary>
+        internal void SkipGroup(uint startGroupTag)
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            ParsingPrimitivesMessages.SkipGroup(ref span, ref state, startGroupTag);
+        }
+
+        /// <summary>
+        /// Reads a double field from the stream.
+        /// </summary>
+        public double ReadDouble()
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.ParseDouble(ref span, ref state);
+        }
+
+        /// <summary>
+        /// Reads a float field from the stream.
+        /// </summary>
+        public float ReadFloat()
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.ParseFloat(ref span, ref state);
+        }
+
+        /// <summary>
+        /// Reads a uint64 field from the stream.
+        /// </summary>
+        public ulong ReadUInt64()
+        {
+            return ReadRawVarint64();
+        }
+
+        /// <summary>
+        /// Reads an int64 field from the stream.
+        /// </summary>
+        public long ReadInt64()
+        {
+            return (long) ReadRawVarint64();
+        }
+
+        /// <summary>
+        /// Reads an int32 field from the stream.
+        /// </summary>
+        public int ReadInt32()
+        {
+            return (int) ReadRawVarint32();
+        }
+
+        /// <summary>
+        /// Reads a fixed64 field from the stream.
+        /// </summary>
+        public ulong ReadFixed64()
+        {
+            return ReadRawLittleEndian64();
+        }
+
+        /// <summary>
+        /// Reads a fixed32 field from the stream.
+        /// </summary>
+        public uint ReadFixed32()
+        {
+            return ReadRawLittleEndian32();
+        }
+
+        /// <summary>
+        /// Reads a bool field from the stream.
+        /// </summary>
+        public bool ReadBool()
+        {
+            return ReadRawVarint64() != 0;
+        }
+
+        /// <summary>
+        /// Reads a string field from the stream.
+        /// </summary>
+        public string ReadString()
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.ReadString(ref span, ref state);
+        }
+
+        /// <summary>
+        /// Reads an embedded message field value from the stream.
+        /// </summary>
+        public void ReadMessage(IMessage builder)
+        {
+            // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalMergeFrom method),
+            // what we're doing here works fine, but could be more efficient.
+            // What happends is that we first initialize a ParseContext from the current coded input stream only to parse the length of the message, at which point
+            // we will need to switch back again to CodedInputStream-based parsing (which involves copying and storing the state) to be able to
+            // invoke the legacy MergeFrom(CodedInputStream) method.
+            // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it).
+            ParseContext.Initialize(buffer.AsSpan(), ref state, out ParseContext ctx);
+            try
+            {
+                ParsingPrimitivesMessages.ReadMessage(ref ctx, builder);
+            }
+            finally
+            {
+                ctx.CopyStateTo(this);
+            }
+        }
+
+        /// <summary>
+        /// Reads an embedded group field from the stream.
+        /// </summary>
+        public void ReadGroup(IMessage builder)
+        {
+            ParseContext.Initialize(this, out ParseContext ctx);
+            try
+            {
+                ParsingPrimitivesMessages.ReadGroup(ref ctx, builder);
+            }
+            finally
+            {
+                ctx.CopyStateTo(this);
+            }
+        }
+
+        /// <summary>
+        /// Reads a bytes field value from the stream.
+        /// </summary>   
+        public ByteString ReadBytes()
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.ReadBytes(ref span, ref state);
+        }
+
+        /// <summary>
+        /// Reads a uint32 field value from the stream.
+        /// </summary>   
+        public uint ReadUInt32()
+        {
+            return ReadRawVarint32();
+        }
+
+        /// <summary>
+        /// Reads an enum field value from the stream.
+        /// </summary>   
+        public int ReadEnum()
+        {
+            // Currently just a pass-through, but it's nice to separate it logically from WriteInt32.
+            return (int) ReadRawVarint32();
+        }
+
+        /// <summary>
+        /// Reads an sfixed32 field value from the stream.
+        /// </summary>   
+        public int ReadSFixed32()
+        {
+            return (int) ReadRawLittleEndian32();
+        }
+
+        /// <summary>
+        /// Reads an sfixed64 field value from the stream.
+        /// </summary>   
+        public long ReadSFixed64()
+        {
+            return (long) ReadRawLittleEndian64();
+        }
+
+        /// <summary>
+        /// Reads an sint32 field value from the stream.
+        /// </summary>   
+        public int ReadSInt32()
+        {
+            return ParsingPrimitives.DecodeZigZag32(ReadRawVarint32());
+        }
+
+        /// <summary>
+        /// Reads an sint64 field value from the stream.
+        /// </summary>   
+        public long ReadSInt64()
+        {
+            return ParsingPrimitives.DecodeZigZag64(ReadRawVarint64());
+        }
+
+        /// <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()
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.ParseLength(ref span, ref state);
+        }
+
+        /// <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>.
+        /// </summary>
+        public bool MaybeConsumeTag(uint tag)
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.MaybeConsumeTag(ref span, ref state, tag);
+        }
+
+#endregion
+
+        #region Underlying reading primitives
+
+        /// <summary>
+        /// 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>
+        internal uint ReadRawVarint32()
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.ParseRawVarint32(ref span, ref state);
+        }
+
+        /// <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)
+        /// 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>
+        internal static uint ReadRawVarint32(Stream input)
+        {
+            return ParsingPrimitives.ReadRawVarint32(input);
+        }
+
+        /// <summary>
+        /// Reads a raw varint from the stream.
+        /// </summary>
+        internal ulong ReadRawVarint64()
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.ParseRawVarint64(ref span, ref state);
+        }
+
+        /// <summary>
+        /// Reads a 32-bit little-endian integer from the stream.
+        /// </summary>
+        internal uint ReadRawLittleEndian32()
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.ParseRawLittleEndian32(ref span, ref state);
+        }
+
+        /// <summary>
+        /// Reads a 64-bit little-endian integer from the stream.
+        /// </summary>
+        internal ulong ReadRawLittleEndian64()
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.ParseRawLittleEndian64(ref span, ref state);
+        }
+        #endregion
+
+        #region Internal reading and buffer management
+
+        /// <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>
+        internal int PushLimit(int byteLimit)
+        {
+            return SegmentedBufferHelper.PushLimit(ref state, byteLimit);
+        }
+
+        /// <summary>
+        /// Discards the current limit, returning the previous limit.
+        /// </summary>
+        internal void PopLimit(int oldLimit)
+        {
+            SegmentedBufferHelper.PopLimit(ref state, oldLimit);
+        }
+
+        /// <summary>
+        /// Returns whether or not all the data before the limit has been read.
+        /// </summary>
+        /// <returns></returns>
+        internal bool ReachedLimit
+        {
+            get
+            {
+                return SegmentedBufferHelper.IsReachedLimit(ref state);
+            }
+        }
+
+        /// <summary>
+        /// Returns true if the stream has reached the end of the input. This is the
+        /// case if either the end of the underlying input source has been reached or
+        /// the stream has reached a limit created using PushLimit.
+        /// </summary>
+        public bool IsAtEnd
+        {
+            get
+            {
+                var span = new ReadOnlySpan<byte>(buffer);
+                return SegmentedBufferHelper.IsAtEnd(ref span, ref state);
+            }
+        }
+
+        /// <summary>
+        /// Called when buffer is empty to read more bytes from the
+        /// input.  If <paramref name="mustSucceed"/> is true, RefillBuffer() guarantees that
+        /// either there will be at least one byte in the buffer when it returns
+        /// or it will throw an exception.  If <paramref name="mustSucceed"/> is false,
+        /// RefillBuffer() returns false if no more bytes were available.
+        /// </summary>
+        /// <param name="mustSucceed"></param>
+        /// <returns></returns>
+        private bool RefillBuffer(bool mustSucceed)
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return state.segmentedBufferHelper.RefillBuffer(ref span, ref state, mustSucceed);
+        }
+
+        /// <summary>
+        /// 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>
+        internal byte[] ReadRawBytes(int size)
+        {
+            var span = new ReadOnlySpan<byte>(buffer);
+            return ParsingPrimitives.ReadRawBytes(ref span, ref state, size);
+        }
+
+        /// <summary>
+        /// Reads a top-level message or a nested message after the limits for this message have been pushed.
+        /// (parser will proceed until the end of the current limit)
+        /// NOTE: this method needs to be public because it's invoked by the generated code - e.g. msg.MergeFrom(CodedInputStream input) method
+        /// </summary>
+        public void ReadRawMessage(IMessage message)
+        {
+            ParseContext.Initialize(this, out ParseContext ctx);
+            try
+            {
+                ParsingPrimitivesMessages.ReadRawMessage(ref ctx, message);
+            }
+            finally
+            {
+                ctx.CopyStateTo(this);
+            }
+        }
+#endregion
+    }
+}
diff --git a/csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs b/csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs
index cb92354..6f18afb 100644
--- a/csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs
+++ b/csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs
@@ -1,308 +1,308 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-

-namespace Google.Protobuf

-{

-    // This part of CodedOutputStream provides all the static entry points that are used

-    // by generated code and internally to compute the size of messages prior to being

-    // written to an instance of CodedOutputStream.

-    public sealed partial class CodedOutputStream

-    {

-        private const int LittleEndian64Size = 8;

-        private const int LittleEndian32Size = 4;        

-

-        internal const int DoubleSize = LittleEndian64Size;

-        internal const int FloatSize = LittleEndian32Size;

-        internal const int BoolSize = 1;

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode a

-        /// double field, including the tag.

-        /// </summary>

-        public static int ComputeDoubleSize(double value)

-        {

-            return DoubleSize;

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode a

-        /// float field, including the tag.

-        /// </summary>

-        public static int ComputeFloatSize(float value)

-        {

-            return FloatSize;

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode a

-        /// uint64 field, including the tag.

-        /// </summary>

-        public static int ComputeUInt64Size(ulong value)

-        {

-            return ComputeRawVarint64Size(value);

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode an

-        /// int64 field, including the tag.

-        /// </summary>

-        public static int ComputeInt64Size(long value)

-        {

-            return ComputeRawVarint64Size((ulong) value);

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode an

-        /// int32 field, including the tag.

-        /// </summary>

-        public static int ComputeInt32Size(int value)

-        {

-            if (value >= 0)

-            {

-                return ComputeRawVarint32Size((uint) value);

-            }

-            else

-            {

-                // Must sign-extend.

-                return 10;

-            }

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode a

-        /// fixed64 field, including the tag.

-        /// </summary>

-        public static int ComputeFixed64Size(ulong value)

-        {

-            return LittleEndian64Size;

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode a

-        /// fixed32 field, including the tag.

-        /// </summary>

-        public static int ComputeFixed32Size(uint value)

-        {

-            return LittleEndian32Size;

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode a

-        /// bool field, including the tag.

-        /// </summary>

-        public static int ComputeBoolSize(bool value)

-        {

-            return BoolSize;

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode a

-        /// string field, including the tag.

-        /// </summary>

-        public static int ComputeStringSize(String value)

-        {

-            int byteArraySize = WritingPrimitives.Utf8Encoding.GetByteCount(value);

-            return ComputeLengthSize(byteArraySize) + byteArraySize;

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode a

-        /// group field, including the tag.

-        /// </summary>

-        public static int ComputeGroupSize(IMessage value)

-        {

-            return value.CalculateSize();

-        }

-

-        /// <summary>

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

-        {

-            int size = value.CalculateSize();

-            return ComputeLengthSize(size) + size;

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode a

-        /// bytes field, including the tag.

-        /// </summary>

-        public static int ComputeBytesSize(ByteString value)

-        {

-            return ComputeLengthSize(value.Length) + value.Length;

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode a

-        /// uint32 field, including the tag.

-        /// </summary>

-        public static int ComputeUInt32Size(uint value)

-        {

-            return ComputeRawVarint32Size(value);

-        }

-

-        /// <summary>

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

-        public static int ComputeEnumSize(int value)

-        {

-            // Currently just a pass-through, but it's nice to separate it logically.

-            return ComputeInt32Size(value);

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode an

-        /// sfixed32 field, including the tag.

-        /// </summary>

-        public static int ComputeSFixed32Size(int value)

-        {

-            return LittleEndian32Size;

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode an

-        /// sfixed64 field, including the tag.

-        /// </summary>

-        public static int ComputeSFixed64Size(long value)

-        {

-            return LittleEndian64Size;

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode an

-        /// sint32 field, including the tag.

-        /// </summary>

-        public static int ComputeSInt32Size(int value)

-        {

-            return ComputeRawVarint32Size(WritingPrimitives.EncodeZigZag32(value));

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode an

-        /// sint64 field, including the tag.

-        /// </summary>

-        public static int ComputeSInt64Size(long value)

-        {

-            return ComputeRawVarint64Size(WritingPrimitives.EncodeZigZag64(value));

-        }

-

-        /// <summary>

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

-        {

-            if ((value & (0xffffffff << 7)) == 0)

-            {

-                return 1;

-            }

-            if ((value & (0xffffffff << 14)) == 0)

-            {

-                return 2;

-            }

-            if ((value & (0xffffffff << 21)) == 0)

-            {

-                return 3;

-            }

-            if ((value & (0xffffffff << 28)) == 0)

-            {

-                return 4;

-            }

-            return 5;

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode a varint.

-        /// </summary>

-        public static int ComputeRawVarint64Size(ulong value)

-        {

-            if ((value & (0xffffffffffffffffL << 7)) == 0)

-            {

-                return 1;

-            }

-            if ((value & (0xffffffffffffffffL << 14)) == 0)

-            {

-                return 2;

-            }

-            if ((value & (0xffffffffffffffffL << 21)) == 0)

-            {

-                return 3;

-            }

-            if ((value & (0xffffffffffffffffL << 28)) == 0)

-            {

-                return 4;

-            }

-            if ((value & (0xffffffffffffffffL << 35)) == 0)

-            {

-                return 5;

-            }

-            if ((value & (0xffffffffffffffffL << 42)) == 0)

-            {

-                return 6;

-            }

-            if ((value & (0xffffffffffffffffL << 49)) == 0)

-            {

-                return 7;

-            }

-            if ((value & (0xffffffffffffffffL << 56)) == 0)

-            {

-                return 8;

-            }

-            if ((value & (0xffffffffffffffffL << 63)) == 0)

-            {

-                return 9;

-            }

-            return 10;

-        }

-

-        /// <summary>

-        /// Computes the number of bytes that would be needed to encode a tag.

-        /// </summary>

-        public static int ComputeTagSize(int fieldNumber)

-        {

-            return ComputeRawVarint32Size(WireFormat.MakeTag(fieldNumber, 0));

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+
+namespace Google.Protobuf
+{
+    // This part of CodedOutputStream provides all the static entry points that are used
+    // by generated code and internally to compute the size of messages prior to being
+    // written to an instance of CodedOutputStream.
+    public sealed partial class CodedOutputStream
+    {
+        private const int LittleEndian64Size = 8;
+        private const int LittleEndian32Size = 4;        
+
+        internal const int DoubleSize = LittleEndian64Size;
+        internal const int FloatSize = LittleEndian32Size;
+        internal const int BoolSize = 1;
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// double field, including the tag.
+        /// </summary>
+        public static int ComputeDoubleSize(double value)
+        {
+            return DoubleSize;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// float field, including the tag.
+        /// </summary>
+        public static int ComputeFloatSize(float value)
+        {
+            return FloatSize;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// uint64 field, including the tag.
+        /// </summary>
+        public static int ComputeUInt64Size(ulong value)
+        {
+            return ComputeRawVarint64Size(value);
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// int64 field, including the tag.
+        /// </summary>
+        public static int ComputeInt64Size(long value)
+        {
+            return ComputeRawVarint64Size((ulong) value);
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// int32 field, including the tag.
+        /// </summary>
+        public static int ComputeInt32Size(int value)
+        {
+            if (value >= 0)
+            {
+                return ComputeRawVarint32Size((uint) value);
+            }
+            else
+            {
+                // Must sign-extend.
+                return 10;
+            }
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// fixed64 field, including the tag.
+        /// </summary>
+        public static int ComputeFixed64Size(ulong value)
+        {
+            return LittleEndian64Size;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// fixed32 field, including the tag.
+        /// </summary>
+        public static int ComputeFixed32Size(uint value)
+        {
+            return LittleEndian32Size;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// bool field, including the tag.
+        /// </summary>
+        public static int ComputeBoolSize(bool value)
+        {
+            return BoolSize;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// string field, including the tag.
+        /// </summary>
+        public static int ComputeStringSize(String value)
+        {
+            int byteArraySize = WritingPrimitives.Utf8Encoding.GetByteCount(value);
+            return ComputeLengthSize(byteArraySize) + byteArraySize;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// group field, including the tag.
+        /// </summary>
+        public static int ComputeGroupSize(IMessage value)
+        {
+            return value.CalculateSize();
+        }
+
+        /// <summary>
+        /// 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)
+        {
+            int size = value.CalculateSize();
+            return ComputeLengthSize(size) + size;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// bytes field, including the tag.
+        /// </summary>
+        public static int ComputeBytesSize(ByteString value)
+        {
+            return ComputeLengthSize(value.Length) + value.Length;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// uint32 field, including the tag.
+        /// </summary>
+        public static int ComputeUInt32Size(uint value)
+        {
+            return ComputeRawVarint32Size(value);
+        }
+
+        /// <summary>
+        /// 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>
+        public static int ComputeEnumSize(int value)
+        {
+            // Currently just a pass-through, but it's nice to separate it logically.
+            return ComputeInt32Size(value);
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// sfixed32 field, including the tag.
+        /// </summary>
+        public static int ComputeSFixed32Size(int value)
+        {
+            return LittleEndian32Size;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// sfixed64 field, including the tag.
+        /// </summary>
+        public static int ComputeSFixed64Size(long value)
+        {
+            return LittleEndian64Size;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// sint32 field, including the tag.
+        /// </summary>
+        public static int ComputeSInt32Size(int value)
+        {
+            return ComputeRawVarint32Size(WritingPrimitives.EncodeZigZag32(value));
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// sint64 field, including the tag.
+        /// </summary>
+        public static int ComputeSInt64Size(long value)
+        {
+            return ComputeRawVarint64Size(WritingPrimitives.EncodeZigZag64(value));
+        }
+
+        /// <summary>
+        /// 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)
+        {
+            if ((value & (0xffffffff << 7)) == 0)
+            {
+                return 1;
+            }
+            if ((value & (0xffffffff << 14)) == 0)
+            {
+                return 2;
+            }
+            if ((value & (0xffffffff << 21)) == 0)
+            {
+                return 3;
+            }
+            if ((value & (0xffffffff << 28)) == 0)
+            {
+                return 4;
+            }
+            return 5;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a varint.
+        /// </summary>
+        public static int ComputeRawVarint64Size(ulong value)
+        {
+            if ((value & (0xffffffffffffffffL << 7)) == 0)
+            {
+                return 1;
+            }
+            if ((value & (0xffffffffffffffffL << 14)) == 0)
+            {
+                return 2;
+            }
+            if ((value & (0xffffffffffffffffL << 21)) == 0)
+            {
+                return 3;
+            }
+            if ((value & (0xffffffffffffffffL << 28)) == 0)
+            {
+                return 4;
+            }
+            if ((value & (0xffffffffffffffffL << 35)) == 0)
+            {
+                return 5;
+            }
+            if ((value & (0xffffffffffffffffL << 42)) == 0)
+            {
+                return 6;
+            }
+            if ((value & (0xffffffffffffffffL << 49)) == 0)
+            {
+                return 7;
+            }
+            if ((value & (0xffffffffffffffffL << 56)) == 0)
+            {
+                return 8;
+            }
+            if ((value & (0xffffffffffffffffL << 63)) == 0)
+            {
+                return 9;
+            }
+            return 10;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a tag.
+        /// </summary>
+        public static int ComputeTagSize(int fieldNumber)
+        {
+            return ComputeRawVarint32Size(WireFormat.MakeTag(fieldNumber, 0));
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/CodedOutputStream.cs b/csharp/src/Google.Protobuf/CodedOutputStream.cs
index 20d88ea..5b8cca1 100644
--- a/csharp/src/Google.Protobuf/CodedOutputStream.cs
+++ b/csharp/src/Google.Protobuf/CodedOutputStream.cs
@@ -1,607 +1,607 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using Google.Protobuf.Collections;

-using System;

-using System.IO;

-using System.Security;

-using System.Text;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Encodes and writes protocol message fields.

-    /// </summary>

-    /// <remarks>

-    /// <para>

-    /// This class is generally used by generated code to write appropriate

-    /// primitives to the stream. It effectively encapsulates the lowest

-    /// levels of protocol buffer format. Unlike some other implementations,

-    /// this does not include combined "write tag and value" methods. Generated

-    /// code knows the exact byte representations of the tags they're going to write,

-    /// so there's no need to re-encode them each time. Manually-written code calling

-    /// this class should just call one of the <c>WriteTag</c> overloads before each value.

-    /// </para>

-    /// <para>

-    /// Repeated fields and map fields are not handled by this class; use <c>RepeatedField&lt;T&gt;</c>

-    /// and <c>MapField&lt;TKey, TValue&gt;</c> to serialize such fields.

-    /// </para>

-    /// </remarks>

-    [SecuritySafeCritical]

-    public sealed partial class CodedOutputStream : IDisposable

-    {

-        /// <summary>

-        /// The buffer size used by CreateInstance(Stream).

-        /// </summary>

-        public static readonly int DefaultBufferSize = 4096;

-

-        private readonly bool leaveOpen;

-        private readonly byte[] buffer;

-        private WriterInternalState state;

-

-        private readonly Stream output;

-

-        #region Construction

-        /// <summary>

-        /// Creates a new CodedOutputStream that writes directly to the given

-        /// byte array. If more bytes are written than fit in the array,

-        /// OutOfSpaceException will be thrown.

-        /// </summary>

-        public CodedOutputStream(byte[] flatArray) : this(flatArray, 0, flatArray.Length)

-        {

-        }

-

-        /// <summary>

-        /// Creates a new CodedOutputStream that writes directly to the given

-        /// byte array slice. If more bytes are written than fit in the array,

-        /// OutOfSpaceException will be thrown.

-        /// </summary>

-        private CodedOutputStream(byte[] buffer, int offset, int length)

-        {

-            this.output = null;

-            this.buffer = ProtoPreconditions.CheckNotNull(buffer, nameof(buffer));

-            this.state.position = offset;

-            this.state.limit = offset + length;

-            WriteBufferHelper.Initialize(this, out this.state.writeBufferHelper);

-            leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference

-        }

-

-        private CodedOutputStream(Stream output, byte[] buffer, bool leaveOpen)

-        {

-            this.output = ProtoPreconditions.CheckNotNull(output, nameof(output));

-            this.buffer = buffer;

-            this.state.position = 0;

-            this.state.limit = buffer.Length;

-            WriteBufferHelper.Initialize(this, out this.state.writeBufferHelper);

-            this.leaveOpen = leaveOpen;

-        }

-

-        /// <summary>

-        /// Creates a new <see cref="CodedOutputStream" /> which write to the given stream, and disposes of that

-        /// stream when the returned <c>CodedOutputStream</c> is disposed.

-        /// </summary>

-        /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param>

-        public CodedOutputStream(Stream output) : this(output, DefaultBufferSize, false)

-        {

-        }

-

-        /// <summary>

-        /// Creates a new CodedOutputStream which write to the given stream and uses

-        /// the specified buffer size.

-        /// </summary>

-        /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param>

-        /// <param name="bufferSize">The size of buffer to use internally.</param>

-        public CodedOutputStream(Stream output, int bufferSize) : this(output, new byte[bufferSize], false)

-        {

-        }

-

-        /// <summary>

-        /// Creates a new CodedOutputStream which write to the given stream.

-        /// </summary>

-        /// <param name="output">The stream to write to.</param>

-        /// <param name="leaveOpen">If <c>true</c>, <paramref name="output"/> is left open when the returned <c>CodedOutputStream</c> is disposed;

-        /// if <c>false</c>, the provided stream is disposed as well.</param>

-        public CodedOutputStream(Stream output, bool leaveOpen) : this(output, DefaultBufferSize, leaveOpen)

-        {

-        }

-

-        /// <summary>

-        /// Creates a new CodedOutputStream which write to the given stream and uses

-        /// the specified buffer size.

-        /// </summary>

-        /// <param name="output">The stream to write to.</param>

-        /// <param name="bufferSize">The size of buffer to use internally.</param>

-        /// <param name="leaveOpen">If <c>true</c>, <paramref name="output"/> is left open when the returned <c>CodedOutputStream</c> is disposed;

-        /// if <c>false</c>, the provided stream is disposed as well.</param>

-        public CodedOutputStream(Stream output, int bufferSize, bool leaveOpen) : this(output, new byte[bufferSize], leaveOpen)

-        {

-        }

-        #endregion

-

-        /// <summary>

-        /// Returns the current position in the stream, or the position in the output buffer

-        /// </summary>

-        public long Position

-        {

-            get

-            {

-                if (output != null)

-                {

-                    return output.Position + state.position;

-                }

-                return state.position;

-            }

-        }

-

-        #region Writing of values (not including tags)

-

-        /// <summary>

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteDouble(ref span, ref state, value);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteFloat(ref span, ref state, value);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteUInt64(ref span, ref state, value);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteInt64(ref span, ref state, value);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteInt32(ref span, ref state, value);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteFixed64(ref span, ref state, value);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteFixed32(ref span, ref state, value);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteBool(ref span, ref state, value);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteString(ref span, ref state, value);

-        }

-

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

-        {

-            // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalWriteTo method),

-            // what we're doing here works fine, but could be more efficient.

-            // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it).

-            var span = new Span<byte>(buffer);

-            WriteContext.Initialize(ref span, ref state, out WriteContext ctx);

-            try

-            {

-                WritingPrimitivesMessages.WriteMessage(ref ctx, value);

-            }

-            finally

-            {

-                ctx.CopyStateTo(this);

-            }

-        }

-

-        /// <summary>

-        /// Writes a message, without a tag, to the stream.

-        /// Only the message data is written, without a length-delimiter.

-        /// </summary>

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

-        public void WriteRawMessage(IMessage value)

-        {

-            // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalWriteTo method),

-            // what we're doing here works fine, but could be more efficient.

-            // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it).

-            var span = new Span<byte>(buffer);

-            WriteContext.Initialize(ref span, ref state, out WriteContext ctx);

-            try

-            {

-                WritingPrimitivesMessages.WriteRawMessage(ref ctx, value);

-            }

-            finally

-            {

-                ctx.CopyStateTo(this);

-            }

-        }

-

-        /// <summary>

-        /// Writes a group, without a tag, to the stream.

-        /// </summary>

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

-        public void WriteGroup(IMessage value)

-        {

-            var span = new Span<byte>(buffer);

-            WriteContext.Initialize(ref span, ref state, out WriteContext ctx);

-            try

-            {

-                WritingPrimitivesMessages.WriteGroup(ref ctx, value);

-            }

-            finally

-            {

-                ctx.CopyStateTo(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)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteBytes(ref span, ref state, value);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteUInt32(ref span, ref state, 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)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteEnum(ref span, ref state, value);

-        }

-

-        /// <summary>

-        /// Writes an sfixed32 value, without a tag, to the stream.

-        /// </summary>

-        /// <param name="value">The value to write.</param>

-        public void WriteSFixed32(int value)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteSFixed32(ref span, ref state, 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)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteSFixed64(ref span, ref state, 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)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteSInt32(ref span, ref state, 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)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteSInt64(ref span, ref state, 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)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteLength(ref span, ref state, 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)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteTag(ref span, ref state, fieldNumber, type);

-        }

-

-        /// <summary>

-        /// Writes an already-encoded tag.

-        /// </summary>

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

-        public void WriteTag(uint tag)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteTag(ref span, ref state, tag);

-        }

-

-        /// <summary>

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

-        /// </summary>

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

-        public void WriteRawTag(byte b1)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteRawTag(ref span, ref state, b1);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4);

-        }

-

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4, b5);

-        }

-        #endregion

-

-        #region Underlying writing primitives

-        

-        /// <summary>

-        /// Writes a 32 bit value as a varint. The fast route is taken when

-        /// there's enough buffer space left to whizz through without checking

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

-        /// </summary>

-        internal void WriteRawVarint32(uint value)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteRawVarint32(ref span, ref state, value);

-        }

-

-        internal void WriteRawVarint64(ulong value)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteRawVarint64(ref span, ref state, value);

-        }

-

-        internal void WriteRawLittleEndian32(uint value)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteRawLittleEndian32(ref span, ref state, value);

-        }

-

-        internal void WriteRawLittleEndian64(ulong value)

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteRawLittleEndian64(ref span, ref state, value);

-        }

-

-        /// <summary>

-        /// Writes out an array of bytes.

-        /// </summary>

-        internal void WriteRawBytes(byte[] value)

-        {

-            WriteRawBytes(value, 0, value.Length);

-        }

-

-        /// <summary>

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

-        /// </summary>

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

-        {

-            var span = new Span<byte>(buffer);

-            WritingPrimitives.WriteRawBytes(ref span, ref state, value, offset, length);

-        }

-

-        #endregion

-

-        /// <summary>

-        /// Indicates that a CodedOutputStream wrapping a flat byte array

-        /// ran out of space.

-        /// </summary>

-        public sealed class OutOfSpaceException : IOException

-        {

-            internal OutOfSpaceException()

-                : base("CodedOutputStream was writing to a flat byte array and ran out of space.")

-            {

-            }

-        }

-

-        /// <summary>

-        /// Flushes any buffered data and optionally closes the underlying stream, if any.

-        /// </summary>

-        /// <remarks>

-        /// <para>

-        /// By default, any underlying stream is closed by this method. To configure this behaviour,

-        /// use a constructor overload with a <c>leaveOpen</c> parameter. If this instance does not

-        /// have an underlying stream, this method does nothing.

-        /// </para>

-        /// <para>

-        /// For the sake of efficiency, calling this method does not prevent future write calls - but

-        /// if a later write ends up writing to a stream which has been disposed, that is likely to

-        /// fail. It is recommend that you not call any other methods after this.

-        /// </para>

-        /// </remarks>

-        public void Dispose()

-        {

-            Flush();

-            if (!leaveOpen)

-            {

-                output.Dispose();

-            }

-        }

-

-        /// <summary>

-        /// Flushes any buffered data to the underlying stream (if there is one).

-        /// </summary>

-        public void Flush()

-        {

-            var span = new Span<byte>(buffer);

-            WriteBufferHelper.Flush(ref span, ref state);

-        }

-

-        /// <summary>

-        /// Verifies that SpaceLeft returns zero. It's common to create a byte array

-        /// that is exactly big enough to hold a message, then write to it with

-        /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that

-        /// the message was actually as big as expected, which can help finding bugs.

-        /// </summary>

-        public void CheckNoSpaceLeft()

-        {

-            WriteBufferHelper.CheckNoSpaceLeft(ref state);

-        }

-

-        /// <summary>

-        /// If writing to a flat array, returns the space left in the array. Otherwise,

-        /// throws an InvalidOperationException.

-        /// </summary>

-        public int SpaceLeft => WriteBufferHelper.GetSpaceLeft(ref state);

-

-        internal byte[] InternalBuffer => buffer;

-

-        internal Stream InternalOutputStream => output;

-

-        internal ref WriterInternalState InternalState => ref state;

-    }

-}

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using Google.Protobuf.Collections;
+using System;
+using System.IO;
+using System.Security;
+using System.Text;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Encodes and writes protocol message fields.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// This class is generally used by generated code to write appropriate
+    /// primitives to the stream. It effectively encapsulates the lowest
+    /// levels of protocol buffer format. Unlike some other implementations,
+    /// this does not include combined "write tag and value" methods. Generated
+    /// code knows the exact byte representations of the tags they're going to write,
+    /// so there's no need to re-encode them each time. Manually-written code calling
+    /// this class should just call one of the <c>WriteTag</c> overloads before each value.
+    /// </para>
+    /// <para>
+    /// Repeated fields and map fields are not handled by this class; use <c>RepeatedField&lt;T&gt;</c>
+    /// and <c>MapField&lt;TKey, TValue&gt;</c> to serialize such fields.
+    /// </para>
+    /// </remarks>
+    [SecuritySafeCritical]
+    public sealed partial class CodedOutputStream : IDisposable
+    {
+        /// <summary>
+        /// The buffer size used by CreateInstance(Stream).
+        /// </summary>
+        public static readonly int DefaultBufferSize = 4096;
+
+        private readonly bool leaveOpen;
+        private readonly byte[] buffer;
+        private WriterInternalState state;
+
+        private readonly Stream output;
+
+        #region Construction
+        /// <summary>
+        /// Creates a new CodedOutputStream that writes directly to the given
+        /// byte array. If more bytes are written than fit in the array,
+        /// OutOfSpaceException will be thrown.
+        /// </summary>
+        public CodedOutputStream(byte[] flatArray) : this(flatArray, 0, flatArray.Length)
+        {
+        }
+
+        /// <summary>
+        /// Creates a new CodedOutputStream that writes directly to the given
+        /// byte array slice. If more bytes are written than fit in the array,
+        /// OutOfSpaceException will be thrown.
+        /// </summary>
+        private CodedOutputStream(byte[] buffer, int offset, int length)
+        {
+            this.output = null;
+            this.buffer = ProtoPreconditions.CheckNotNull(buffer, nameof(buffer));
+            this.state.position = offset;
+            this.state.limit = offset + length;
+            WriteBufferHelper.Initialize(this, out this.state.writeBufferHelper);
+            leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference
+        }
+
+        private CodedOutputStream(Stream output, byte[] buffer, bool leaveOpen)
+        {
+            this.output = ProtoPreconditions.CheckNotNull(output, nameof(output));
+            this.buffer = buffer;
+            this.state.position = 0;
+            this.state.limit = buffer.Length;
+            WriteBufferHelper.Initialize(this, out this.state.writeBufferHelper);
+            this.leaveOpen = leaveOpen;
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="CodedOutputStream" /> which write to the given stream, and disposes of that
+        /// stream when the returned <c>CodedOutputStream</c> is disposed.
+        /// </summary>
+        /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param>
+        public CodedOutputStream(Stream output) : this(output, DefaultBufferSize, false)
+        {
+        }
+
+        /// <summary>
+        /// Creates a new CodedOutputStream which write to the given stream and uses
+        /// the specified buffer size.
+        /// </summary>
+        /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param>
+        /// <param name="bufferSize">The size of buffer to use internally.</param>
+        public CodedOutputStream(Stream output, int bufferSize) : this(output, new byte[bufferSize], false)
+        {
+        }
+
+        /// <summary>
+        /// Creates a new CodedOutputStream which write to the given stream.
+        /// </summary>
+        /// <param name="output">The stream to write to.</param>
+        /// <param name="leaveOpen">If <c>true</c>, <paramref name="output"/> is left open when the returned <c>CodedOutputStream</c> is disposed;
+        /// if <c>false</c>, the provided stream is disposed as well.</param>
+        public CodedOutputStream(Stream output, bool leaveOpen) : this(output, DefaultBufferSize, leaveOpen)
+        {
+        }
+
+        /// <summary>
+        /// Creates a new CodedOutputStream which write to the given stream and uses
+        /// the specified buffer size.
+        /// </summary>
+        /// <param name="output">The stream to write to.</param>
+        /// <param name="bufferSize">The size of buffer to use internally.</param>
+        /// <param name="leaveOpen">If <c>true</c>, <paramref name="output"/> is left open when the returned <c>CodedOutputStream</c> is disposed;
+        /// if <c>false</c>, the provided stream is disposed as well.</param>
+        public CodedOutputStream(Stream output, int bufferSize, bool leaveOpen) : this(output, new byte[bufferSize], leaveOpen)
+        {
+        }
+        #endregion
+
+        /// <summary>
+        /// Returns the current position in the stream, or the position in the output buffer
+        /// </summary>
+        public long Position
+        {
+            get
+            {
+                if (output != null)
+                {
+                    return output.Position + state.position;
+                }
+                return state.position;
+            }
+        }
+
+        #region Writing of values (not including tags)
+
+        /// <summary>
+        /// 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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteDouble(ref span, ref state, value);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteFloat(ref span, ref state, value);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteUInt64(ref span, ref state, value);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteInt64(ref span, ref state, value);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteInt32(ref span, ref state, value);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteFixed64(ref span, ref state, value);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteFixed32(ref span, ref state, value);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteBool(ref span, ref state, value);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteString(ref span, ref state, value);
+        }
+
+        /// <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)
+        {
+            // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalWriteTo method),
+            // what we're doing here works fine, but could be more efficient.
+            // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it).
+            var span = new Span<byte>(buffer);
+            WriteContext.Initialize(ref span, ref state, out WriteContext ctx);
+            try
+            {
+                WritingPrimitivesMessages.WriteMessage(ref ctx, value);
+            }
+            finally
+            {
+                ctx.CopyStateTo(this);
+            }
+        }
+
+        /// <summary>
+        /// Writes a message, without a tag, to the stream.
+        /// Only the message data is written, without a length-delimiter.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteRawMessage(IMessage value)
+        {
+            // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalWriteTo method),
+            // what we're doing here works fine, but could be more efficient.
+            // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it).
+            var span = new Span<byte>(buffer);
+            WriteContext.Initialize(ref span, ref state, out WriteContext ctx);
+            try
+            {
+                WritingPrimitivesMessages.WriteRawMessage(ref ctx, value);
+            }
+            finally
+            {
+                ctx.CopyStateTo(this);
+            }
+        }
+
+        /// <summary>
+        /// Writes a group, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteGroup(IMessage value)
+        {
+            var span = new Span<byte>(buffer);
+            WriteContext.Initialize(ref span, ref state, out WriteContext ctx);
+            try
+            {
+                WritingPrimitivesMessages.WriteGroup(ref ctx, value);
+            }
+            finally
+            {
+                ctx.CopyStateTo(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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteBytes(ref span, ref state, value);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteUInt32(ref span, ref state, 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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteEnum(ref span, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes an sfixed32 value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write.</param>
+        public void WriteSFixed32(int value)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteSFixed32(ref span, ref state, 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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteSFixed64(ref span, ref state, 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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteSInt32(ref span, ref state, 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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteSInt64(ref span, ref state, 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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteLength(ref span, ref state, 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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteTag(ref span, ref state, fieldNumber, type);
+        }
+
+        /// <summary>
+        /// Writes an already-encoded tag.
+        /// </summary>
+        /// <param name="tag">The encoded tag</param>
+        public void WriteTag(uint tag)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteTag(ref span, ref state, tag);
+        }
+
+        /// <summary>
+        /// Writes the given single-byte tag directly to the stream.
+        /// </summary>
+        /// <param name="b1">The encoded tag</param>
+        public void WriteRawTag(byte b1)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawTag(ref span, ref state, b1);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4);
+        }
+
+        /// <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)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4, b5);
+        }
+        #endregion
+
+        #region Underlying writing primitives
+        
+        /// <summary>
+        /// Writes a 32 bit value as a varint. The fast route is taken when
+        /// there's enough buffer space left to whizz through without checking
+        /// for each byte; otherwise, we resort to calling WriteRawByte each time.
+        /// </summary>
+        internal void WriteRawVarint32(uint value)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawVarint32(ref span, ref state, value);
+        }
+
+        internal void WriteRawVarint64(ulong value)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawVarint64(ref span, ref state, value);
+        }
+
+        internal void WriteRawLittleEndian32(uint value)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawLittleEndian32(ref span, ref state, value);
+        }
+
+        internal void WriteRawLittleEndian64(ulong value)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawLittleEndian64(ref span, ref state, value);
+        }
+
+        /// <summary>
+        /// Writes out an array of bytes.
+        /// </summary>
+        internal void WriteRawBytes(byte[] value)
+        {
+            WriteRawBytes(value, 0, value.Length);
+        }
+
+        /// <summary>
+        /// Writes out part of an array of bytes.
+        /// </summary>
+        internal void WriteRawBytes(byte[] value, int offset, int length)
+        {
+            var span = new Span<byte>(buffer);
+            WritingPrimitives.WriteRawBytes(ref span, ref state, value, offset, length);
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Indicates that a CodedOutputStream wrapping a flat byte array
+        /// ran out of space.
+        /// </summary>
+        public sealed class OutOfSpaceException : IOException
+        {
+            internal OutOfSpaceException()
+                : base("CodedOutputStream was writing to a flat byte array and ran out of space.")
+            {
+            }
+        }
+
+        /// <summary>
+        /// Flushes any buffered data and optionally closes the underlying stream, if any.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// By default, any underlying stream is closed by this method. To configure this behaviour,
+        /// use a constructor overload with a <c>leaveOpen</c> parameter. If this instance does not
+        /// have an underlying stream, this method does nothing.
+        /// </para>
+        /// <para>
+        /// For the sake of efficiency, calling this method does not prevent future write calls - but
+        /// if a later write ends up writing to a stream which has been disposed, that is likely to
+        /// fail. It is recommend that you not call any other methods after this.
+        /// </para>
+        /// </remarks>
+        public void Dispose()
+        {
+            Flush();
+            if (!leaveOpen)
+            {
+                output.Dispose();
+            }
+        }
+
+        /// <summary>
+        /// Flushes any buffered data to the underlying stream (if there is one).
+        /// </summary>
+        public void Flush()
+        {
+            var span = new Span<byte>(buffer);
+            WriteBufferHelper.Flush(ref span, ref state);
+        }
+
+        /// <summary>
+        /// Verifies that SpaceLeft returns zero. It's common to create a byte array
+        /// that is exactly big enough to hold a message, then write to it with
+        /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
+        /// the message was actually as big as expected, which can help finding bugs.
+        /// </summary>
+        public void CheckNoSpaceLeft()
+        {
+            WriteBufferHelper.CheckNoSpaceLeft(ref state);
+        }
+
+        /// <summary>
+        /// If writing to a flat array, returns the space left in the array. Otherwise,
+        /// throws an InvalidOperationException.
+        /// </summary>
+        public int SpaceLeft => WriteBufferHelper.GetSpaceLeft(ref state);
+
+        internal byte[] InternalBuffer => buffer;
+
+        internal Stream InternalOutputStream => output;
+
+        internal ref WriterInternalState InternalState => ref state;
+    }
+}
diff --git a/csharp/src/Google.Protobuf/Collections/MapField.cs b/csharp/src/Google.Protobuf/Collections/MapField.cs
index 6b7d0f1..e5217f4 100644
--- a/csharp/src/Google.Protobuf/Collections/MapField.cs
+++ b/csharp/src/Google.Protobuf/Collections/MapField.cs
@@ -1,762 +1,762 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2015 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using Google.Protobuf.Compatibility;

-using Google.Protobuf.Reflection;

-using System;

-using System.Buffers;

-using System.Collections;

-using System.Collections.Generic;

-using System.IO;

-using System.Linq;

-using System.Security;

-

-namespace Google.Protobuf.Collections

-{

-    /// <summary>

-    /// Representation of a map field in a Protocol Buffer message.

-    /// </summary>

-    /// <typeparam name="TKey">Key type in the map. Must be a type supported by Protocol Buffer map keys.</typeparam>

-    /// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam>

-    /// <remarks>

-    /// <para>

-    /// For string keys, the equality comparison is provided by <see cref="StringComparer.Ordinal" />.

-    /// </para>

-    /// <para>

-    /// Null values are not permitted in the map, either for wrapper types or regular messages.

-    /// If a map is deserialized from a data stream and the value is missing from an entry, a default value

-    /// is created instead. For primitive types, that is the regular default value (0, the empty string and so

-    /// on); for message types, an empty instance of the message is created, as if the map entry contained a 0-length

-    /// encoded value for the field.

-    /// </para>

-    /// <para>

-    /// This implementation does not generally prohibit the use of key/value types which are not

-    /// supported by Protocol Buffers (e.g. using a key type of <code>byte</code>) but nor does it guarantee

-    /// that all operations will work in such cases.

-    /// </para>

-    /// <para>

-    /// The order in which entries are returned when iterating over this object is undefined, and may change

-    /// in future versions.

-    /// </para>

-    /// </remarks>

-    public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary

-#if !NET35

-        , IReadOnlyDictionary<TKey, TValue>

-#endif

-    {

-        private static readonly EqualityComparer<TValue> ValueEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TValue>();

-        private static readonly EqualityComparer<TKey> KeyEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TKey>();

-

-        // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)

-        private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> map =

-            new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>(KeyEqualityComparer);

-        private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new LinkedList<KeyValuePair<TKey, TValue>>();

-

-        /// <summary>

-        /// Creates a deep clone of this object.

-        /// </summary>

-        /// <returns>

-        /// A deep clone of this object.

-        /// </returns>

-        public MapField<TKey, TValue> Clone()

-        {

-            var clone = new MapField<TKey, TValue>();

-            // Keys are never cloneable. Values might be.

-            if (typeof(IDeepCloneable<TValue>).IsAssignableFrom(typeof(TValue)))

-            {

-                foreach (var pair in list)

-                {

-                    clone.Add(pair.Key, ((IDeepCloneable<TValue>)pair.Value).Clone());

-                }

-            }

-            else

-            {

-                // Nothing is cloneable, so we don't need to worry.

-                clone.Add(this);

-            }

-            return clone;

-        }

-

-        /// <summary>

-        /// Adds the specified key/value pair to the map.

-        /// </summary>

-        /// <remarks>

-        /// This operation fails if the key already exists in the map. To replace an existing entry, use the indexer.

-        /// </remarks>

-        /// <param name="key">The key to add</param>

-        /// <param name="value">The value to add.</param>

-        /// <exception cref="System.ArgumentException">The given key already exists in map.</exception>

-        public void Add(TKey key, TValue value)

-        {

-            // Validation of arguments happens in ContainsKey and the indexer

-            if (ContainsKey(key))

-            {

-                throw new ArgumentException("Key already exists in map", nameof(key));

-            }

-            this[key] = value;

-        }

-

-        /// <summary>

-        /// Determines whether the specified key is present in the map.

-        /// </summary>

-        /// <param name="key">The key to check.</param>

-        /// <returns><c>true</c> if the map contains the given key; <c>false</c> otherwise.</returns>

-        public bool ContainsKey(TKey key)

-        {

-            ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));

-            return map.ContainsKey(key);

-        }

-

-        private bool ContainsValue(TValue value) =>

-            list.Any(pair => ValueEqualityComparer.Equals(pair.Value, value));

-

-        /// <summary>

-        /// Removes the entry identified by the given key from the map.

-        /// </summary>

-        /// <param name="key">The key indicating the entry to remove from the map.</param>

-        /// <returns><c>true</c> if the map contained the given key before the entry was removed; <c>false</c> otherwise.</returns>

-        public bool Remove(TKey key)

-        {

-            ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));

-            LinkedListNode<KeyValuePair<TKey, TValue>> node;

-            if (map.TryGetValue(key, out node))

-            {

-                map.Remove(key);

-                node.List.Remove(node);

-                return true;

-            }

-            else

-            {

-                return false;

-            }

-        }

-

-        /// <summary>

-        /// Gets the value associated with the specified key.

-        /// </summary>

-        /// <param name="key">The key whose value to get.</param>

-        /// <param name="value">When this method returns, the value associated with the specified key, if the key is found;

-        /// otherwise, the default value for the type of the <paramref name="value"/> parameter.

-        /// This parameter is passed uninitialized.</param>

-        /// <returns><c>true</c> if the map contains an element with the specified key; otherwise, <c>false</c>.</returns>

-        public bool TryGetValue(TKey key, out TValue value)

-        {

-            LinkedListNode<KeyValuePair<TKey, TValue>> node;

-            if (map.TryGetValue(key, out node))

-            {

-                value = node.Value.Value;

-                return true;

-            }

-            else

-            {

-                value = default(TValue);

-                return false;

-            }

-        }

-

-        /// <summary>

-        /// Gets or sets the value associated with the specified key.

-        /// </summary>

-        /// <param name="key">The key of the value to get or set.</param>

-        /// <exception cref="KeyNotFoundException">The property is retrieved and key does not exist in the collection.</exception>

-        /// <returns>The value associated with the specified key. If the specified key is not found,

-        /// a get operation throws a <see cref="KeyNotFoundException"/>, and a set operation creates a new element with the specified key.</returns>

-        public TValue this[TKey key]

-        {

-            get

-            {

-                ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));

-                TValue value;

-                if (TryGetValue(key, out value))

-                {

-                    return value;

-                }

-                throw new KeyNotFoundException();

-            }

-            set

-            {

-                ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));

-                // value == null check here is redundant, but avoids boxing.

-                if (value == null)

-                {

-                    ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));

-                }

-                LinkedListNode<KeyValuePair<TKey, TValue>> node;

-                var pair = new KeyValuePair<TKey, TValue>(key, value);

-                if (map.TryGetValue(key, out node))

-                {

-                    node.Value = pair;

-                }

-                else

-                {

-                    node = list.AddLast(pair);

-                    map[key] = node;

-                }

-            }

-        }

-

-        /// <summary>

-        /// Gets a collection containing the keys in the map.

-        /// </summary>

-        public ICollection<TKey> Keys { get { return new MapView<TKey>(this, pair => pair.Key, ContainsKey); } }

-

-        /// <summary>

-        /// Gets a collection containing the values in the map.

-        /// </summary>

-        public ICollection<TValue> Values { get { return new MapView<TValue>(this, pair => pair.Value, ContainsValue); } }

-

-        /// <summary>

-        /// Adds the specified entries to the map. The keys and values are not automatically cloned.

-        /// </summary>

-        /// <param name="entries">The entries to add to the map.</param>

-        public void Add(IDictionary<TKey, TValue> entries)

-        {

-            ProtoPreconditions.CheckNotNull(entries, nameof(entries));

-            foreach (var pair in entries)

-            {

-                Add(pair.Key, pair.Value);

-            }

-        }

-

-        /// <summary>

-        /// Returns an enumerator that iterates through the collection.

-        /// </summary>

-        /// <returns>

-        /// An enumerator that can be used to iterate through the collection.

-        /// </returns>

-        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()

-        {

-            return list.GetEnumerator();

-        }

-

-        /// <summary>

-        /// Returns an enumerator that iterates through a collection.

-        /// </summary>

-        /// <returns>

-        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.

-        /// </returns>

-        IEnumerator IEnumerable.GetEnumerator()

-        {

-            return GetEnumerator();

-        }

-

-        /// <summary>

-        /// Adds the specified item to the map.

-        /// </summary>

-        /// <param name="item">The item to add to the map.</param>

-        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)

-        {

-            Add(item.Key, item.Value);

-        }

-

-        /// <summary>

-        /// Removes all items from the map.

-        /// </summary>

-        public void Clear()

-        {

-            list.Clear();

-            map.Clear();

-        }

-

-        /// <summary>

-        /// Determines whether map contains an entry equivalent to the given key/value pair.

-        /// </summary>

-        /// <param name="item">The key/value pair to find.</param>

-        /// <returns></returns>

-        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)

-        {

-            TValue value;

-            return TryGetValue(item.Key, out value) && ValueEqualityComparer.Equals(item.Value, value);

-        }

-

-        /// <summary>

-        /// Copies the key/value pairs in this map to an array.

-        /// </summary>

-        /// <param name="array">The array to copy the entries into.</param>

-        /// <param name="arrayIndex">The index of the array at which to start copying values.</param>

-        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)

-        {

-            list.CopyTo(array, arrayIndex);

-        }

-

-        /// <summary>

-        /// Removes the specified key/value pair from the map.

-        /// </summary>

-        /// <remarks>Both the key and the value must be found for the entry to be removed.</remarks>

-        /// <param name="item">The key/value pair to remove.</param>

-        /// <returns><c>true</c> if the key/value pair was found and removed; <c>false</c> otherwise.</returns>

-        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)

-        {

-            if (item.Key == null)

-            {

-                throw new ArgumentException("Key is null", nameof(item));

-            }

-            LinkedListNode<KeyValuePair<TKey, TValue>> node;

-            if (map.TryGetValue(item.Key, out node) &&

-                EqualityComparer<TValue>.Default.Equals(item.Value, node.Value.Value))

-            {

-                map.Remove(item.Key);

-                node.List.Remove(node);

-                return true;

-            }

-            else

-            {

-                return false;

-            }

-        }

-

-        /// <summary>

-        /// Gets the number of elements contained in the map.

-        /// </summary>

-        public int Count { get { return list.Count; } }

-

-        /// <summary>

-        /// Gets a value indicating whether the map is read-only.

-        /// </summary>

-        public bool IsReadOnly { get { return false; } }

-

-        /// <summary>

-        /// Determines whether the specified <see cref="System.Object" />, is equal to this instance.

-        /// </summary>

-        /// <param name="other">The <see cref="System.Object" /> to compare with this instance.</param>

-        /// <returns>

-        ///   <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.

-        /// </returns>

-        public override bool Equals(object other)

-        {

-            return Equals(other as MapField<TKey, TValue>);

-        }

-

-        /// <summary>

-        /// Returns a hash code for this instance.

-        /// </summary>

-        /// <returns>

-        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 

-        /// </returns>

-        public override int GetHashCode()

-        {

-            var keyComparer = KeyEqualityComparer;

-            var valueComparer = ValueEqualityComparer;

-            int hash = 0;

-            foreach (var pair in list)

-            {

-                hash ^= keyComparer.GetHashCode(pair.Key) * 31 + valueComparer.GetHashCode(pair.Value);

-            }

-            return hash;

-        }

-

-        /// <summary>

-        /// Compares this map with another for equality.

-        /// </summary>

-        /// <remarks>

-        /// The order of the key/value pairs in the maps is not deemed significant in this comparison.

-        /// </remarks>

-        /// <param name="other">The map to compare this with.</param>

-        /// <returns><c>true</c> if <paramref name="other"/> refers to an equal map; <c>false</c> otherwise.</returns>

-        public bool Equals(MapField<TKey, TValue> other)

-        {

-            if (other == null)

-            {

-                return false;

-            }

-            if (other == this)

-            {

-                return true;

-            }

-            if (other.Count != this.Count)

-            {

-                return false;

-            }

-            var valueComparer = ValueEqualityComparer;

-            foreach (var pair in this)

-            {

-                TValue value;

-                if (!other.TryGetValue(pair.Key, out value))

-                {

-                    return false;

-                }

-                if (!valueComparer.Equals(value, pair.Value))

-                {

-                    return false;

-                }

-            }

-            return true;

-        }

-

-        /// <summary>

-        /// Adds entries to the map from the given stream.

-        /// </summary>

-        /// <remarks>

-        /// It is assumed that the stream is initially positioned after the tag specified by the codec.

-        /// This method will continue reading entries from the stream until the end is reached, or

-        /// a different tag is encountered.

-        /// </remarks>

-        /// <param name="input">Stream to read from</param>

-        /// <param name="codec">Codec describing how the key/value pairs are encoded</param>

-        public void AddEntriesFrom(CodedInputStream input, Codec codec)

-        {

-            ParseContext.Initialize(input, out ParseContext ctx);

-            try

-            {

-                AddEntriesFrom(ref ctx, codec);

-            }

-            finally

-            {

-                ctx.CopyStateTo(input);

-            }

-        }

-

-        /// <summary>

-        /// Adds entries to the map from the given parse context.

-        /// </summary>

-        /// <remarks>

-        /// It is assumed that the input is initially positioned after the tag specified by the codec.

-        /// This method will continue reading entries from the input until the end is reached, or

-        /// a different tag is encountered.

-        /// </remarks>

-        /// <param name="ctx">Input to read from</param>

-        /// <param name="codec">Codec describing how the key/value pairs are encoded</param>

-        [SecuritySafeCritical]

-        public void AddEntriesFrom(ref ParseContext ctx, Codec codec)

-        {

-            do

-            {

-                KeyValuePair<TKey, TValue> entry = ParsingPrimitivesMessages.ReadMapEntry(ref ctx, codec);

-                this[entry.Key] = entry.Value;

-            } while (ParsingPrimitives.MaybeConsumeTag(ref ctx.buffer, ref ctx.state, codec.MapTag));

-        }

-

-        /// <summary>

-        /// Writes the contents of this map to the given coded output stream, using the specified codec

-        /// to encode each entry.

-        /// </summary>

-        /// <param name="output">The output stream to write to.</param>

-        /// <param name="codec">The codec to use for each entry.</param>

-        public void WriteTo(CodedOutputStream output, Codec codec)

-        {

-            WriteContext.Initialize(output, out WriteContext ctx);

-            try

-            {

-                WriteTo(ref ctx, codec);

-            }

-            finally

-            {

-                ctx.CopyStateTo(output);

-            }

-        }

-

-        /// <summary>

-        /// Writes the contents of this map to the given write context, using the specified codec

-        /// to encode each entry.

-        /// </summary>

-        /// <param name="ctx">The write context to write to.</param>

-        /// <param name="codec">The codec to use for each entry.</param>

-        [SecuritySafeCritical]

-        public void WriteTo(ref WriteContext ctx, Codec codec)

-        {

-            foreach (var entry in list)

-            {

-                ctx.WriteTag(codec.MapTag);

-

-                WritingPrimitives.WriteLength(ref ctx.buffer, ref ctx.state, CalculateEntrySize(codec, entry));

-                codec.KeyCodec.WriteTagAndValue(ref ctx, entry.Key);

-                codec.ValueCodec.WriteTagAndValue(ref ctx, entry.Value);

-            }

-        }

-

-        /// <summary>

-        /// Calculates the size of this map based on the given entry codec.

-        /// </summary>

-        /// <param name="codec">The codec to use to encode each entry.</param>

-        /// <returns></returns>

-        public int CalculateSize(Codec codec)

-        {

-            if (Count == 0)

-            {

-                return 0;

-            }

-            int size = 0;

-            foreach (var entry in list)

-            {

-                int entrySize = CalculateEntrySize(codec, entry);

-

-                size += CodedOutputStream.ComputeRawVarint32Size(codec.MapTag);

-                size += CodedOutputStream.ComputeLengthSize(entrySize) + entrySize;

-            }

-            return size;

-        }

-

-        private static int CalculateEntrySize(Codec codec, KeyValuePair<TKey, TValue> entry)

-        {

-            return codec.KeyCodec.CalculateSizeWithTag(entry.Key) + codec.ValueCodec.CalculateSizeWithTag(entry.Value);

-        }

-

-        /// <summary>

-        /// Returns a string representation of this repeated field, in the same

-        /// way as it would be represented by the default JSON formatter.

-        /// </summary>

-        public override string ToString()

-        {

-            var writer = new StringWriter();

-            JsonFormatter.Default.WriteDictionary(writer, this);

-            return writer.ToString();

-        }

-

-        #region IDictionary explicit interface implementation

-        void IDictionary.Add(object key, object value)

-        {

-            Add((TKey)key, (TValue)value);

-        }

-

-        bool IDictionary.Contains(object key)

-        {

-            if (!(key is TKey))

-            {

-                return false;

-            }

-            return ContainsKey((TKey)key);

-        }

-

-        IDictionaryEnumerator IDictionary.GetEnumerator()

-        {

-            return new DictionaryEnumerator(GetEnumerator());

-        }

-

-        void IDictionary.Remove(object key)

-        {

-            ProtoPreconditions.CheckNotNull(key, nameof(key));

-            if (!(key is TKey))

-            {

-                return;

-            }

-            Remove((TKey)key);

-        }

-

-        void ICollection.CopyTo(Array array, int index)

-        {

-            // This is ugly and slow as heck, but with any luck it will never be used anyway.

-            ICollection temp = this.Select(pair => new DictionaryEntry(pair.Key, pair.Value)).ToList();

-            temp.CopyTo(array, index);

-        }

-

-        bool IDictionary.IsFixedSize { get { return false; } }

-

-        ICollection IDictionary.Keys { get { return (ICollection)Keys; } }

-

-        ICollection IDictionary.Values { get { return (ICollection)Values; } }

-

-        bool ICollection.IsSynchronized { get { return false; } }

-

-        object ICollection.SyncRoot { get { return this; } }

-

-        object IDictionary.this[object key]

-        {

-            get

-            {

-                ProtoPreconditions.CheckNotNull(key, nameof(key));

-                if (!(key is TKey))

-                {

-                    return null;

-                }

-                TValue value;

-                TryGetValue((TKey)key, out value);

-                return value;

-            }

-

-            set

-            {

-                this[(TKey)key] = (TValue)value;

-            }

-        }

-        #endregion

-

-        #region IReadOnlyDictionary explicit interface implementation

-#if !NET35

-        IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys;

-

-        IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;

-#endif

-        #endregion

-

-        private class DictionaryEnumerator : IDictionaryEnumerator

-        {

-            private readonly IEnumerator<KeyValuePair<TKey, TValue>> enumerator;

-

-            internal DictionaryEnumerator(IEnumerator<KeyValuePair<TKey, TValue>> enumerator)

-            {

-                this.enumerator = enumerator;

-            }

-

-            public bool MoveNext()

-            {

-                return enumerator.MoveNext();

-            }

-

-            public void Reset()

-            {

-                enumerator.Reset();

-            }

-

-            public object Current { get { return Entry; } }

-            public DictionaryEntry Entry { get { return new DictionaryEntry(Key, Value); } }

-            public object Key { get { return enumerator.Current.Key; } }

-            public object Value { get { return enumerator.Current.Value; } }

-        }

-

-        /// <summary>

-        /// A codec for a specific map field. This contains all the information required to encode and

-        /// decode the nested messages.

-        /// </summary>

-        public sealed class Codec

-        {

-            private readonly FieldCodec<TKey> keyCodec;

-            private readonly FieldCodec<TValue> valueCodec;

-            private readonly uint mapTag;

-

-            /// <summary>

-            /// Creates a new entry codec based on a separate key codec and value codec,

-            /// and the tag to use for each map entry.

-            /// </summary>

-            /// <param name="keyCodec">The key codec.</param>

-            /// <param name="valueCodec">The value codec.</param>

-            /// <param name="mapTag">The map tag to use to introduce each map entry.</param>

-            public Codec(FieldCodec<TKey> keyCodec, FieldCodec<TValue> valueCodec, uint mapTag)

-            {

-                this.keyCodec = keyCodec;

-                this.valueCodec = valueCodec;

-                this.mapTag = mapTag;

-            }

-

-            /// <summary>

-            /// The key codec.

-            /// </summary>

-            internal FieldCodec<TKey> KeyCodec => keyCodec;

-

-            /// <summary>

-            /// The value codec.

-            /// </summary>

-            internal FieldCodec<TValue> ValueCodec => valueCodec;

-

-            /// <summary>

-            /// The tag used in the enclosing message to indicate map entries.

-            /// </summary>

-            internal uint MapTag => mapTag;

-        }

-

-        private class MapView<T> : ICollection<T>, ICollection

-        {

-            private readonly MapField<TKey, TValue> parent;

-            private readonly Func<KeyValuePair<TKey, TValue>, T> projection;

-            private readonly Func<T, bool> containsCheck;

-

-            internal MapView(

-                MapField<TKey, TValue> parent,

-                Func<KeyValuePair<TKey, TValue>, T> projection,

-                Func<T, bool> containsCheck)

-            {

-                this.parent = parent;

-                this.projection = projection;

-                this.containsCheck = containsCheck;

-            }

-

-            public int Count { get { return parent.Count; } }

-

-            public bool IsReadOnly { get { return true; } }

-

-            public bool IsSynchronized { get { return false; } }

-

-            public object SyncRoot { get { return parent; } }

-

-            public void Add(T item)

-            {

-                throw new NotSupportedException();

-            }

-

-            public void Clear()

-            {

-                throw new NotSupportedException();

-            }

-

-            public bool Contains(T item)

-            {

-                return containsCheck(item);

-            }

-

-            public void CopyTo(T[] array, int arrayIndex)

-            {

-                if (arrayIndex < 0)

-                {

-                    throw new ArgumentOutOfRangeException(nameof(arrayIndex));

-                }

-                if (arrayIndex + Count > array.Length)

-                {

-                    throw new ArgumentException("Not enough space in the array", nameof(array));

-                }

-                foreach (var item in this)

-                {

-                    array[arrayIndex++] = item;

-                }

-            }

-

-            public IEnumerator<T> GetEnumerator()

-            {

-                return parent.list.Select(projection).GetEnumerator();

-            }

-

-            public bool Remove(T item)

-            {

-                throw new NotSupportedException();

-            }

-

-            IEnumerator IEnumerable.GetEnumerator()

-            {

-                return GetEnumerator();

-            }

-

-            public void CopyTo(Array array, int index)

-            {

-                if (index < 0)

-                {

-                    throw new ArgumentOutOfRangeException(nameof(index));

-                }

-                if (index + Count > array.Length)

-                {

-                    throw new ArgumentException("Not enough space in the array", nameof(array));

-                }

-                foreach (var item in this)

-                {

-                    array.SetValue(item, index++);

-                }

-            }

-        }

-    }

-}

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

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.Collections;

-using System.Collections.Generic;

-

-namespace Google.Protobuf.Collections

-{

-    /// <summary>

-    /// Read-only wrapper around another dictionary.

-    /// </summary>

-    internal sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>

-    {

-        private readonly IDictionary<TKey, TValue> wrapped;

-

-        public ReadOnlyDictionary(IDictionary<TKey, TValue> wrapped)

-        {

-            this.wrapped = wrapped;

-        }

-

-        public void Add(TKey key, TValue value)

-        {

-            throw new InvalidOperationException();

-        }

-

-        public bool ContainsKey(TKey key)

-        {

-            return wrapped.ContainsKey(key);

-        }

-

-        public ICollection<TKey> Keys

-        {

-            get { return wrapped.Keys; }

-        }

-

-        public bool Remove(TKey key)

-        {

-            throw new InvalidOperationException();

-        }

-

-        public bool TryGetValue(TKey key, out TValue value)

-        {

-            return wrapped.TryGetValue(key, out value);

-        }

-

-        public ICollection<TValue> Values

-        {

-            get { return wrapped.Values; }

-        }

-

-        public TValue this[TKey key]

-        {

-            get { return wrapped[key]; }

-            set { throw new InvalidOperationException(); }

-        }

-

-        public void Add(KeyValuePair<TKey, TValue> item)

-        {

-            throw new InvalidOperationException();

-        }

-

-        public void Clear()

-        {

-            throw new InvalidOperationException();

-        }

-

-        public bool Contains(KeyValuePair<TKey, TValue> item)

-        {

-            return wrapped.Contains(item);

-        }

-

-        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)

-        {

-            wrapped.CopyTo(array, arrayIndex);

-        }

-

-        public int Count

-        {

-            get { return wrapped.Count; }

-        }

-

-        public bool IsReadOnly

-        {

-            get { return true; }

-        }

-

-        public bool Remove(KeyValuePair<TKey, TValue> item)

-        {

-            throw new InvalidOperationException();

-        }

-

-        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()

-        {

-            return wrapped.GetEnumerator();

-        }

-

-        IEnumerator IEnumerable.GetEnumerator()

-        {

-            return ((IEnumerable) wrapped).GetEnumerator();

-        }

-

-        public override bool Equals(object obj)

-        {

-            return wrapped.Equals(obj);

-        }

-

-        public override int GetHashCode()

-        {

-            return wrapped.GetHashCode();

-        }

-

-        public override string ToString()

-        {

-            return wrapped.ToString();

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Google.Protobuf.Collections
+{
+    /// <summary>
+    /// Read-only wrapper around another dictionary.
+    /// </summary>
+    internal sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
+    {
+        private readonly IDictionary<TKey, TValue> wrapped;
+
+        public ReadOnlyDictionary(IDictionary<TKey, TValue> wrapped)
+        {
+            this.wrapped = wrapped;
+        }
+
+        public void Add(TKey key, TValue value)
+        {
+            throw new InvalidOperationException();
+        }
+
+        public bool ContainsKey(TKey key)
+        {
+            return wrapped.ContainsKey(key);
+        }
+
+        public ICollection<TKey> Keys
+        {
+            get { return wrapped.Keys; }
+        }
+
+        public bool Remove(TKey key)
+        {
+            throw new InvalidOperationException();
+        }
+
+        public bool TryGetValue(TKey key, out TValue value)
+        {
+            return wrapped.TryGetValue(key, out value);
+        }
+
+        public ICollection<TValue> Values
+        {
+            get { return wrapped.Values; }
+        }
+
+        public TValue this[TKey key]
+        {
+            get { return wrapped[key]; }
+            set { throw new InvalidOperationException(); }
+        }
+
+        public void Add(KeyValuePair<TKey, TValue> item)
+        {
+            throw new InvalidOperationException();
+        }
+
+        public void Clear()
+        {
+            throw new InvalidOperationException();
+        }
+
+        public bool Contains(KeyValuePair<TKey, TValue> item)
+        {
+            return wrapped.Contains(item);
+        }
+
+        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+        {
+            wrapped.CopyTo(array, arrayIndex);
+        }
+
+        public int Count
+        {
+            get { return wrapped.Count; }
+        }
+
+        public bool IsReadOnly
+        {
+            get { return true; }
+        }
+
+        public bool Remove(KeyValuePair<TKey, TValue> item)
+        {
+            throw new InvalidOperationException();
+        }
+
+        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+        {
+            return wrapped.GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return ((IEnumerable) wrapped).GetEnumerator();
+        }
+
+        public override bool Equals(object obj)
+        {
+            return wrapped.Equals(obj);
+        }
+
+        public override int GetHashCode()
+        {
+            return wrapped.GetHashCode();
+        }
+
+        public override string ToString()
+        {
+            return wrapped.ToString();
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
index 19114ca..9269c74 100644
--- a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
+++ b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
@@ -1,698 +1,698 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2015 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.Collections;

-using System.Collections.Generic;

-using System.IO;

-using System.Security;

-using System.Threading;

-

-namespace Google.Protobuf.Collections

-{

-    /// <summary>

-    /// The contents of a repeated field: essentially, a collection with some extra

-    /// restrictions (no null values) and capabilities (deep cloning).

-    /// </summary>

-    /// <remarks>

-    /// This implementation does not generally prohibit the use of types which are not

-    /// supported by Protocol Buffers but nor does it guarantee that all operations will work in such cases.

-    /// </remarks>

-    /// <typeparam name="T">The element type of the repeated field.</typeparam>

-    public sealed class RepeatedField<T> : IList<T>, IList, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>

-#if !NET35

-        , IReadOnlyList<T>

-#endif

-    {

-        private static readonly EqualityComparer<T> EqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<T>();

-        private static readonly T[] EmptyArray = new T[0];

-        private const int MinArraySize = 8;

-

-        private T[] array = EmptyArray;

-        private int count = 0;

-

-        /// <summary>

-        /// Creates a deep clone of this repeated field.

-        /// </summary>

-        /// <remarks>

-        /// If the field type is

-        /// a message type, each element is also cloned; otherwise, it is

-        /// assumed that the field type is primitive (including string and

-        /// bytes, both of which are immutable) and so a simple copy is

-        /// equivalent to a deep clone.

-        /// </remarks>

-        /// <returns>A deep clone of this repeated field.</returns>

-        public RepeatedField<T> Clone()

-        {

-            RepeatedField<T> clone = new RepeatedField<T>();

-            if (array != EmptyArray)

-            {

-                clone.array = (T[])array.Clone();

-                IDeepCloneable<T>[] cloneableArray = clone.array as IDeepCloneable<T>[];

-                if (cloneableArray != null)

-                {

-                    for (int i = 0; i < count; i++)

-                    {

-                        clone.array[i] = cloneableArray[i].Clone();

-                    }

-                }

-            }

-            clone.count = count;

-            return clone;

-        }

-

-        /// <summary>

-        /// Adds the entries from the given input stream, decoding them with the specified codec.

-        /// </summary>

-        /// <param name="input">The input stream to read from.</param>

-        /// <param name="codec">The codec to use in order to read each entry.</param>

-        public void AddEntriesFrom(CodedInputStream input, FieldCodec<T> codec)

-        {

-            ParseContext.Initialize(input, out ParseContext ctx);

-            try

-            {

-                AddEntriesFrom(ref ctx, codec);

-            }

-            finally

-            {

-                ctx.CopyStateTo(input);

-            }

-        }

-

-        /// <summary>

-        /// Adds the entries from the given parse context, decoding them with the specified codec.

-        /// </summary>

-        /// <param name="ctx">The input to read from.</param>

-        /// <param name="codec">The codec to use in order to read each entry.</param>

-        [SecuritySafeCritical]

-        public void AddEntriesFrom(ref ParseContext ctx, FieldCodec<T> codec)

-        {

-            // TODO: Inline some of the Add code, so we can avoid checking the size on every

-            // iteration.

-            uint tag = ctx.state.lastTag;

-            var reader = codec.ValueReader;

-            // Non-nullable value types can be packed or not.

-            if (FieldCodec<T>.IsPackedRepeatedField(tag))

-            {

-                int length = ctx.ReadLength();

-                if (length > 0)

-                {

-                    int oldLimit = SegmentedBufferHelper.PushLimit(ref ctx.state, length);

-

-                    // If the content is fixed size then we can calculate the length

-                    // of the repeated field and pre-initialize the underlying collection.

-                    //

-                    // Check that the supplied length doesn't exceed the underlying buffer.

-                    // That prevents a malicious length from initializing a very large collection.

-                    if (codec.FixedSize > 0 && length % codec.FixedSize == 0 && ParsingPrimitives.IsDataAvailable(ref ctx.state, length))

-                    {

-                        EnsureSize(count + (length / codec.FixedSize));

-

-                        while (!SegmentedBufferHelper.IsReachedLimit(ref ctx.state))

-                        {

-                            // Only FieldCodecs with a fixed size can reach here, and they are all known

-                            // types that don't allow the user to specify a custom reader action.

-                            // reader action will never return null.

-                            array[count++] = reader(ref ctx);

-                        }

-                    }

-                    else

-                    {

-                        // Content is variable size so add until we reach the limit.

-                        while (!SegmentedBufferHelper.IsReachedLimit(ref ctx.state))

-                        {

-                            Add(reader(ref ctx));

-                        }

-                    }

-                    SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit);

-                }

-                // Empty packed field. Odd, but valid - just ignore.

-            }

-            else

-            {

-                // Not packed... (possibly not packable)

-                do

-                {

-                    Add(reader(ref ctx));

-                } while (ParsingPrimitives.MaybeConsumeTag(ref ctx.buffer, ref ctx.state, tag));

-            }

-        }

-

-        /// <summary>

-        /// Calculates the size of this collection based on the given codec.

-        /// </summary>

-        /// <param name="codec">The codec to use when encoding each field.</param>

-        /// <returns>The number of bytes that would be written to an output by one of the <c>WriteTo</c> methods,

-        /// using the same codec.</returns>

-        public int CalculateSize(FieldCodec<T> codec)

-        {

-            if (count == 0)

-            {

-                return 0;

-            }

-            uint tag = codec.Tag;

-            if (codec.PackedRepeatedField)

-            {

-                int dataSize = CalculatePackedDataSize(codec);

-                return CodedOutputStream.ComputeRawVarint32Size(tag) +

-                    CodedOutputStream.ComputeLengthSize(dataSize) +

-                    dataSize;

-            }

-            else

-            {

-                var sizeCalculator = codec.ValueSizeCalculator;

-                int size = count * CodedOutputStream.ComputeRawVarint32Size(tag);

-                if (codec.EndTag != 0)

-                {

-                    size += count * CodedOutputStream.ComputeRawVarint32Size(codec.EndTag);

-                }

-                for (int i = 0; i < count; i++)

-                {

-                    size += sizeCalculator(array[i]);

-                }

-                return size;

-            }

-        }

-

-        private int CalculatePackedDataSize(FieldCodec<T> codec)

-        {

-            int fixedSize = codec.FixedSize;

-            if (fixedSize == 0)

-            {

-                var calculator = codec.ValueSizeCalculator;

-                int tmp = 0;

-                for (int i = 0; i < count; i++)

-                {

-                    tmp += calculator(array[i]);

-                }

-                return tmp;

-            }

-            else

-            {

-                return fixedSize * Count;

-            }

-        }

-

-        /// <summary>

-        /// Writes the contents of this collection to the given <see cref="CodedOutputStream"/>,

-        /// encoding each value using the specified codec.

-        /// </summary>

-        /// <param name="output">The output stream to write to.</param>

-        /// <param name="codec">The codec to use when encoding each value.</param>

-        public void WriteTo(CodedOutputStream output, FieldCodec<T> codec)

-        {

-            WriteContext.Initialize(output, out WriteContext ctx);

-            try

-            {

-                WriteTo(ref ctx, codec);

-            }

-            finally

-            {

-                ctx.CopyStateTo(output);

-            }

-        }

-

-        /// <summary>

-        /// Writes the contents of this collection to the given write context,

-        /// encoding each value using the specified codec.

-        /// </summary>

-        /// <param name="ctx">The write context to write to.</param>

-        /// <param name="codec">The codec to use when encoding each value.</param>

-        [SecuritySafeCritical]

-        public void WriteTo(ref WriteContext ctx, FieldCodec<T> codec)

-        {

-            if (count == 0)

-            {

-                return;

-            }

-            var writer = codec.ValueWriter;

-            var tag = codec.Tag;

-            if (codec.PackedRepeatedField)

-            {

-                // Packed primitive type

-                int size = CalculatePackedDataSize(codec);

-                ctx.WriteTag(tag);

-                ctx.WriteLength(size);

-                for (int i = 0; i < count; i++)

-                {

-                    writer(ref ctx, array[i]);

-                }

-            }

-            else

-            {

-                // Not packed: a simple tag/value pair for each value.

-                // Can't use codec.WriteTagAndValue, as that omits default values.

-                for (int i = 0; i < count; i++)

-                {

-                    ctx.WriteTag(tag);

-                    writer(ref ctx, array[i]);

-                    if (codec.EndTag != 0)

-                    {

-                        ctx.WriteTag(codec.EndTag);

-                    }

-                }

-            }

-        }

-

-        /// <summary>

-        /// Gets and sets the capacity of the RepeatedField's internal array.  WHen set, the internal array is reallocated to the given capacity.

-        /// <exception cref="ArgumentOutOfRangeException">The new value is less than Count -or- when Count is less than 0.</exception>

-        /// </summary>

-        public int Capacity

-        {

-            get { return array.Length; }

-            set

-            {

-                if (value < count)

-                {

-                    throw new ArgumentOutOfRangeException("Capacity", value,

-                        $"Cannot set Capacity to a value smaller than the current item count, {count}");

-                }

-

-                if (value >= 0 && value != array.Length)

-                {

-                    SetSize(value);

-                }

-            }

-        }

-

-        // May increase the size of the internal array, but will never shrink it.

-        private void EnsureSize(int size)

-        {

-            if (array.Length < size)

-            {

-                size = Math.Max(size, MinArraySize);

-                int newSize = Math.Max(array.Length * 2, size);

-                SetSize(newSize);

-            }

-        }

-

-        // Sets the internal array to an exact size.

-        private void SetSize(int size)

-        {

-            if (size != array.Length)

-            {

-                var tmp = new T[size];

-                Array.Copy(array, 0, tmp, 0, count);

-                array = tmp;

-            }

-        }

-

-        /// <summary>

-        /// Adds the specified item to the collection.

-        /// </summary>

-        /// <param name="item">The item to add.</param>

-        public void Add(T item)

-        {

-            ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));

-            EnsureSize(count + 1);

-            array[count++] = item;

-        }

-

-        /// <summary>

-        /// Removes all items from the collection.

-        /// </summary>

-        public void Clear()

-        {

-            array = EmptyArray;

-            count = 0;

-        }

-

-        /// <summary>

-        /// Determines whether this collection contains the given item.

-        /// </summary>

-        /// <param name="item">The item to find.</param>

-        /// <returns><c>true</c> if this collection contains the given item; <c>false</c> otherwise.</returns>

-        public bool Contains(T item)

-        {

-            return IndexOf(item) != -1;

-        }

-

-        /// <summary>

-        /// Copies this collection to the given array.

-        /// </summary>

-        /// <param name="array">The array to copy to.</param>

-        /// <param name="arrayIndex">The first index of the array to copy to.</param>

-        public void CopyTo(T[] array, int arrayIndex)

-        {

-            Array.Copy(this.array, 0, array, arrayIndex, count);

-        }

-

-        /// <summary>

-        /// Removes the specified item from the collection

-        /// </summary>

-        /// <param name="item">The item to remove.</param>

-        /// <returns><c>true</c> if the item was found and removed; <c>false</c> otherwise.</returns>

-        public bool Remove(T item)

-        {

-            int index = IndexOf(item);

-            if (index == -1)

-            {

-                return false;

-            }            

-            Array.Copy(array, index + 1, array, index, count - index - 1);

-            count--;

-            array[count] = default(T);

-            return true;

-        }

-

-        /// <summary>

-        /// Gets the number of elements contained in the collection.

-        /// </summary>

-        public int Count => count;

-

-        /// <summary>

-        /// Gets a value indicating whether the collection is read-only.

-        /// </summary>

-        public bool IsReadOnly => false;

-

-        /// <summary>

-        /// Adds all of the specified values into this collection.

-        /// </summary>

-        /// <param name="values">The values to add to this collection.</param>

-        public void AddRange(IEnumerable<T> values)

-        {

-            ProtoPreconditions.CheckNotNull(values, nameof(values));

-

-            // Optimization 1: If the collection we're adding is already a RepeatedField<T>,

-            // we know the values are valid.

-            var otherRepeatedField = values as RepeatedField<T>;

-            if (otherRepeatedField != null)

-            {

-                EnsureSize(count + otherRepeatedField.count);

-                Array.Copy(otherRepeatedField.array, 0, array, count, otherRepeatedField.count);

-                count += otherRepeatedField.count;

-                return;

-            }

-

-            // Optimization 2: The collection is an ICollection, so we can expand

-            // just once and ask the collection to copy itself into the array.

-            var collection = values as ICollection;

-            if (collection != null)

-            {

-                var extraCount = collection.Count;

-                // For reference types and nullable value types, we need to check that there are no nulls

-                // present. (This isn't a thread-safe approach, but we don't advertise this is thread-safe.)

-                // We expect the JITter to optimize this test to true/false, so it's effectively conditional

-                // specialization.

-                if (default(T) == null)

-                {

-                    // TODO: Measure whether iterating once to check and then letting the collection copy

-                    // itself is faster or slower than iterating and adding as we go. For large

-                    // collections this will not be great in terms of cache usage... but the optimized

-                    // copy may be significantly faster than doing it one at a time.

-                    foreach (var item in collection)

-                    {

-                        if (item == null)

-                        {

-                            throw new ArgumentException("Sequence contained null element", nameof(values));

-                        }

-                    }

-                }

-                EnsureSize(count + extraCount);

-                collection.CopyTo(array, count);

-                count += extraCount;

-                return;

-            }

-

-            // We *could* check for ICollection<T> as well, but very very few collections implement

-            // ICollection<T> but not ICollection. (HashSet<T> does, for one...)

-

-            // Fall back to a slower path of adding items one at a time.

-            foreach (T item in values)

-            {

-                Add(item);

-            }

-        }

-

-        /// <summary>

-        /// Adds all of the specified values into this collection. This method is present to

-        /// allow repeated fields to be constructed from queries within collection initializers.

-        /// Within non-collection-initializer code, consider using the equivalent <see cref="AddRange"/>

-        /// method instead for clarity.

-        /// </summary>

-        /// <param name="values">The values to add to this collection.</param>

-        public void Add(IEnumerable<T> values)

-        {

-            AddRange(values);

-        }

-

-        /// <summary>

-        /// Returns an enumerator that iterates through the collection.

-        /// </summary>

-        /// <returns>

-        /// An enumerator that can be used to iterate through the collection.

-        /// </returns>

-        public IEnumerator<T> GetEnumerator()

-        {

-            for (int i = 0; i < count; i++)

-            {

-                yield return array[i];

-            }

-        }

-

-        /// <summary>

-        /// Determines whether the specified <see cref="System.Object" />, is equal to this instance.

-        /// </summary>

-        /// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>

-        /// <returns>

-        ///   <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.

-        /// </returns>

-        public override bool Equals(object obj)

-        {

-            return Equals(obj as RepeatedField<T>);

-        }

-

-        /// <summary>

-        /// Returns an enumerator that iterates through a collection.

-        /// </summary>

-        /// <returns>

-        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.

-        /// </returns>

-        IEnumerator IEnumerable.GetEnumerator()

-        {

-            return GetEnumerator();

-        }

-

-        /// <summary>

-        /// Returns a hash code for this instance.

-        /// </summary>

-        /// <returns>

-        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 

-        /// </returns>

-        public override int GetHashCode()

-        {

-            int hash = 0;

-            for (int i = 0; i < count; i++)

-            {

-                hash = hash * 31 + array[i].GetHashCode();

-            }

-            return hash;

-        }

-

-        /// <summary>

-        /// Compares this repeated field with another for equality.

-        /// </summary>

-        /// <param name="other">The repeated field to compare this with.</param>

-        /// <returns><c>true</c> if <paramref name="other"/> refers to an equal repeated field; <c>false</c> otherwise.</returns>

-        public bool Equals(RepeatedField<T> other)

-        {

-            if (ReferenceEquals(other, null))

-            {

-                return false;

-            }

-            if (ReferenceEquals(other, this))

-            {

-                return true;

-            }

-            if (other.Count != this.Count)

-            {

-                return false;

-            }

-            EqualityComparer<T> comparer = EqualityComparer;

-            for (int i = 0; i < count; i++)

-            {

-                if (!comparer.Equals(array[i], other.array[i]))

-                {

-                    return false;

-                }

-            }

-            return true;

-        }

-

-        /// <summary>

-        /// Returns the index of the given item within the collection, or -1 if the item is not

-        /// present.

-        /// </summary>

-        /// <param name="item">The item to find in the collection.</param>

-        /// <returns>The zero-based index of the item, or -1 if it is not found.</returns>

-        public int IndexOf(T item)

-        {

-            ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));

-            EqualityComparer<T> comparer = EqualityComparer;

-            for (int i = 0; i < count; i++)

-            {

-                if (comparer.Equals(array[i], item))

-                {

-                    return i;

-                }

-            }

-            return -1;

-        }

-

-        /// <summary>

-        /// Inserts the given item at the specified index.

-        /// </summary>

-        /// <param name="index">The index at which to insert the item.</param>

-        /// <param name="item">The item to insert.</param>

-        public void Insert(int index, T item)

-        {

-            ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));

-            if (index < 0 || index > count)

-            {

-                throw new ArgumentOutOfRangeException(nameof(index));

-            }

-            EnsureSize(count + 1);

-            Array.Copy(array, index, array, index + 1, count - index);

-            array[index] = item;

-            count++;

-        }

-

-        /// <summary>

-        /// Removes the item at the given index.

-        /// </summary>

-        /// <param name="index">The zero-based index of the item to remove.</param>

-        public void RemoveAt(int index)

-        {

-            if (index < 0 || index >= count)

-            {

-                throw new ArgumentOutOfRangeException(nameof(index));

-            }

-            Array.Copy(array, index + 1, array, index, count - index - 1);

-            count--;

-            array[count] = default(T);

-        }

-

-        /// <summary>

-        /// Returns a string representation of this repeated field, in the same

-        /// way as it would be represented by the default JSON formatter.

-        /// </summary>

-        public override string ToString()

-        {

-            var writer = new StringWriter();

-            JsonFormatter.Default.WriteList(writer, this);

-            return writer.ToString();

-        }

-

-        /// <summary>

-        /// Gets or sets the item at the specified index.

-        /// </summary>

-        /// <value>

-        /// The element at the specified index.

-        /// </value>

-        /// <param name="index">The zero-based index of the element to get or set.</param>

-        /// <returns>The item at the specified index.</returns>

-        public T this[int index]

-        {

-            get

-            {

-                if (index < 0 || index >= count)

-                {

-                    throw new ArgumentOutOfRangeException(nameof(index));

-                }

-                return array[index];

-            }

-            set

-            {

-                if (index < 0 || index >= count)

-                {

-                    throw new ArgumentOutOfRangeException(nameof(index));

-                }

-                ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));

-                array[index] = value;

-            }

-        }

-

-        #region Explicit interface implementation for IList and ICollection.

-        bool IList.IsFixedSize => false;

-

-        void ICollection.CopyTo(Array array, int index)

-        {

-            Array.Copy(this.array, 0, array, index, count);

-        }

-

-        bool ICollection.IsSynchronized => false;

-

-        object ICollection.SyncRoot => this;

-

-        object IList.this[int index]

-        {

-            get { return this[index]; }

-            set { this[index] = (T)value; }

-        }

-

-        int IList.Add(object value)

-        {

-            Add((T) value);

-            return count - 1;

-        }

-

-        bool IList.Contains(object value)

-        {

-            return (value is T && Contains((T)value));

-        }

-

-        int IList.IndexOf(object value)

-        {

-            if (!(value is T))

-            {

-                return -1;

-            }

-            return IndexOf((T)value);

-        }

-

-        void IList.Insert(int index, object value)

-        {

-            Insert(index, (T) value);

-        }

-

-        void IList.Remove(object value)

-        {

-            if (!(value is T))

-            {

-                return;

-            }

-            Remove((T)value);

-        }

-        #endregion        

-    }

-}

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

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using Google.Protobuf.Collections;

-using System;

-using System.Collections.Generic;

-using System.Linq;

-using System.Reflection;

-using System.Security;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Methods for managing <see cref="ExtensionSet{TTarget}"/>s with null checking.

-    /// 

-    /// Most users will not use this class directly and its API is experimental and subject to change.

-    /// </summary>

-    public static class ExtensionSet

-    {

-        private static bool TryGetValue<TTarget>(ref ExtensionSet<TTarget> set, Extension extension, out IExtensionValue value) where TTarget : IExtendableMessage<TTarget>

-        {

-            if (set == null)

-            {

-                value = null;

-                return false;

-            }

-            return set.ValuesByNumber.TryGetValue(extension.FieldNumber, out value);

-        }

-

-        /// <summary>

-        /// Gets the value of the specified extension

-        /// </summary>

-        public static TValue Get<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>

-        {

-            IExtensionValue value;

-            if (TryGetValue(ref set, extension, out value))

-            {

-                // The stored ExtensionValue can be a different type to what is being requested.

-                // This happens when the same extension proto is compiled in different assemblies.

-                // To allow consuming assemblies to still get the value when the TValue type is

-                // different, this get method:

-                // 1. Attempts to cast the value to the expected ExtensionValue<TValue>.

-                //    This is the usual case. It is used first because it avoids possibly boxing the value.

-                // 2. Fallback to get the value as object from IExtensionValue then casting.

-                //    This allows for someone to specify a TValue of object. They can then convert

-                //    the values to bytes and reparse using expected value.

-                // 3. If neither of these work, throw a user friendly error that the types aren't compatible.

-                if (value is ExtensionValue<TValue> extensionValue)

-                {

-                    return extensionValue.GetValue();

-                }

-                else if (value.GetValue() is TValue underlyingValue)

-                {

-                    return underlyingValue;

-                }

-                else

-                {

-                    var valueType = value.GetType().GetTypeInfo();

-                    if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(ExtensionValue<>))

-                    {

-                        var storedType = valueType.GenericTypeArguments[0];

-                        throw new InvalidOperationException(

-                            "The stored extension value has a type of '" + storedType.AssemblyQualifiedName + "'. " +

-                            "This a different from the requested type of '" + typeof(TValue).AssemblyQualifiedName + "'.");

-                    }

-                    else

-                    {

-                        throw new InvalidOperationException("Unexpected extension value type: " + valueType.AssemblyQualifiedName);

-                    }

-                }

-            }

-            else 

-            {

-                return extension.DefaultValue;

-            }

-        }

-

-        /// <summary>

-        /// Gets the value of the specified repeated extension or null if it doesn't exist in this set

-        /// </summary>

-        public static RepeatedField<TValue> Get<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>

-        {

-            IExtensionValue value;

-            if (TryGetValue(ref set, extension, out value))

-            {

-                if (value is RepeatedExtensionValue<TValue> extensionValue)

-                {

-                    return extensionValue.GetValue();

-                }

-                else

-                {

-                    var valueType = value.GetType().GetTypeInfo();

-                    if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(RepeatedExtensionValue<>))

-                    {

-                        var storedType = valueType.GenericTypeArguments[0];

-                        throw new InvalidOperationException(

-                            "The stored extension value has a type of '" + storedType.AssemblyQualifiedName + "'. " +

-                            "This a different from the requested type of '" + typeof(TValue).AssemblyQualifiedName + "'.");

-                    }

-                    else

-                    {

-                        throw new InvalidOperationException("Unexpected extension value type: " + valueType.AssemblyQualifiedName);

-                    }

-                }

-            }

-            else 

-            {

-                return null;

-            }

-        }

-

-        /// <summary>

-        /// Gets the value of the specified repeated extension, registering it if it doesn't exist

-        /// </summary>

-        public static RepeatedField<TValue> GetOrInitialize<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>

-        {

-            IExtensionValue value;

-            if (set == null)

-            {

-                value = extension.CreateValue();

-                set = new ExtensionSet<TTarget>();

-                set.ValuesByNumber.Add(extension.FieldNumber, value);

-            }

-            else

-            {

-                if (!set.ValuesByNumber.TryGetValue(extension.FieldNumber, out value))

-                {

-                    value = extension.CreateValue();

-                    set.ValuesByNumber.Add(extension.FieldNumber, value);

-                }

-            }

-

-            return ((RepeatedExtensionValue<TValue>)value).GetValue();

-        }

-

-        /// <summary>

-        /// Sets the value of the specified extension. This will make a new instance of ExtensionSet if the set is null.

-        /// </summary>

-        public static void Set<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension, TValue value) where TTarget : IExtendableMessage<TTarget>

-        {

-            ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));

-

-            IExtensionValue extensionValue;

-            if (set == null)

-            {

-                extensionValue = extension.CreateValue();

-                set = new ExtensionSet<TTarget>();

-                set.ValuesByNumber.Add(extension.FieldNumber, extensionValue);

-            }

-            else

-            {

-                if (!set.ValuesByNumber.TryGetValue(extension.FieldNumber, out extensionValue))

-                {

-                    extensionValue = extension.CreateValue();

-                    set.ValuesByNumber.Add(extension.FieldNumber, extensionValue);

-                }

-            }

-            

-            ((ExtensionValue<TValue>)extensionValue).SetValue(value);

-        }

-

-        /// <summary>

-        /// Gets whether the value of the specified extension is set

-        /// </summary>

-        public static bool Has<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>

-        {

-            IExtensionValue value;

-            return TryGetValue(ref set, extension, out value);

-        }

-

-        /// <summary>

-        /// Clears the value of the specified extension

-        /// </summary>

-        public static void Clear<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>

-        {

-            if (set == null)

-            {

-                return;

-            }

-            set.ValuesByNumber.Remove(extension.FieldNumber);

-            if (set.ValuesByNumber.Count == 0)

-            {

-                set = null;

-            }

-        }

-

-        /// <summary>

-        /// Clears the value of the specified extension

-        /// </summary>

-        public static void Clear<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>

-        {

-            if (set == null)

-            {

-                return;

-            }

-            set.ValuesByNumber.Remove(extension.FieldNumber);

-            if (set.ValuesByNumber.Count == 0)

-            {

-                set = null;

-            }

-        }

-

-        /// <summary>

-        /// Tries to merge a field from the coded input, returning true if the field was merged.

-        /// If the set is null or the field was not otherwise merged, this returns false.

-        /// </summary>

-        public static bool TryMergeFieldFrom<TTarget>(ref ExtensionSet<TTarget> set, CodedInputStream stream) where TTarget : IExtendableMessage<TTarget>

-        {

-            ParseContext.Initialize(stream, out ParseContext ctx);

-            try

-            {

-                return TryMergeFieldFrom<TTarget>(ref set, ref ctx);

-            }

-            finally

-            {

-                ctx.CopyStateTo(stream);

-            }

-        }

-

-        /// <summary>

-        /// Tries to merge a field from the coded input, returning true if the field was merged.

-        /// If the set is null or the field was not otherwise merged, this returns false.

-        /// </summary>

-        public static bool TryMergeFieldFrom<TTarget>(ref ExtensionSet<TTarget> set, ref ParseContext ctx) where TTarget : IExtendableMessage<TTarget>

-        {

-            Extension extension;

-            int lastFieldNumber = WireFormat.GetTagFieldNumber(ctx.LastTag);

-

-            IExtensionValue extensionValue;

-            if (set != null && set.ValuesByNumber.TryGetValue(lastFieldNumber, out extensionValue))

-            {

-                extensionValue.MergeFrom(ref ctx);

-                return true;

-            }

-            else if (ctx.ExtensionRegistry != null && ctx.ExtensionRegistry.ContainsInputField(ctx.LastTag, typeof(TTarget), out extension))

-            {

-                IExtensionValue value = extension.CreateValue();

-                value.MergeFrom(ref ctx);

-                set = (set ?? new ExtensionSet<TTarget>());

-                set.ValuesByNumber.Add(extension.FieldNumber, value);

-                return true;

-            }

-            else

-            {

-                return false;

-            }

-        }

-

-        /// <summary>

-        /// Merges the second set into the first set, creating a new instance if first is null

-        /// </summary>

-        public static void MergeFrom<TTarget>(ref ExtensionSet<TTarget> first, ExtensionSet<TTarget> second) where TTarget : IExtendableMessage<TTarget>

-        {

-            if (second == null)

-            {

-                return;

-            }

-            if (first == null)

-            {

-                first = new ExtensionSet<TTarget>();

-            }

-            foreach (var pair in second.ValuesByNumber)

-            {

-                IExtensionValue value;

-                if (first.ValuesByNumber.TryGetValue(pair.Key, out value))

-                {

-                    value.MergeFrom(pair.Value);

-                }

-                else

-                {

-                    var cloned = pair.Value.Clone();

-                    first.ValuesByNumber[pair.Key] = cloned;

-                }

-            }

-        }

-

-        /// <summary>

-        /// Clones the set into a new set. If the set is null, this returns null

-        /// </summary>

-        public static ExtensionSet<TTarget> Clone<TTarget>(ExtensionSet<TTarget> set) where TTarget : IExtendableMessage<TTarget>

-        {

-            if (set == null)

-            {

-                return null;

-            }

-

-            var newSet = new ExtensionSet<TTarget>();

-            foreach (var pair in set.ValuesByNumber)

-            {

-                var cloned = pair.Value.Clone();

-                newSet.ValuesByNumber[pair.Key] = cloned;

-            }

-            return newSet;

-        }

-    }

-

-    /// <summary>

-    /// Used for keeping track of extensions in messages. 

-    /// <see cref="IExtendableMessage{T}"/> methods route to this set.

-    /// 

-    /// Most users will not need to use this class directly

-    /// </summary>

-    /// <typeparam name="TTarget">The message type that extensions in this set target</typeparam>

-    public sealed class ExtensionSet<TTarget> where TTarget : IExtendableMessage<TTarget>

-    {

-        internal Dictionary<int, IExtensionValue> ValuesByNumber { get; } = new Dictionary<int, IExtensionValue>();

-

-        /// <summary>

-        /// Gets a hash code of the set

-        /// </summary>

-        public override int GetHashCode()

-        {

-            int ret = typeof(TTarget).GetHashCode();

-            foreach (KeyValuePair<int, IExtensionValue> field in ValuesByNumber)

-            {

-                // Use ^ here to make the field order irrelevant.

-                int hash = field.Key.GetHashCode() ^ field.Value.GetHashCode();

-                ret ^= hash;

-            }

-            return ret;

-        }

-

-        /// <summary>

-        /// Returns whether this set is equal to the other object

-        /// </summary>

-        public override bool Equals(object other)

-        {

-            if (ReferenceEquals(this, other))

-            {

-                return true;

-            }

-            ExtensionSet<TTarget> otherSet = other as ExtensionSet<TTarget>;

-            if (ValuesByNumber.Count != otherSet.ValuesByNumber.Count)

-            {

-                return false;

-            }

-            foreach (var pair in ValuesByNumber)

-            {

-                IExtensionValue secondValue;

-                if (!otherSet.ValuesByNumber.TryGetValue(pair.Key, out secondValue))

-                {

-                    return false;

-                }

-                if (!pair.Value.Equals(secondValue))

-                {

-                    return false;

-                }

-            }

-            return true;

-        }

-

-        /// <summary>

-        /// Calculates the size of this extension set

-        /// </summary>

-        public int CalculateSize()

-        {

-            int size = 0;

-            foreach (var value in ValuesByNumber.Values)

-            {

-                size += value.CalculateSize();

-            }

-            return size;

-        }

-

-        /// <summary>

-        /// Writes the extension values in this set to the output stream

-        /// </summary>

-        public void WriteTo(CodedOutputStream stream)

-        {

-            

-            WriteContext.Initialize(stream, out WriteContext ctx);

-            try

-            {

-                WriteTo(ref ctx);

-            }

-            finally

-            {

-                ctx.CopyStateTo(stream);

-            }

-        }

-

-        /// <summary>

-        /// Writes the extension values in this set to the write context

-        /// </summary>

-        [SecuritySafeCritical]

-        public void WriteTo(ref WriteContext ctx)

-        {

-            foreach (var value in ValuesByNumber.Values)

-            {

-                value.WriteTo(ref ctx);

-            }

-        }

-

-        internal bool IsInitialized()

-        {

-            return ValuesByNumber.Values.All(v => v.IsInitialized());

-        }

-    }

-}

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using Google.Protobuf.Collections;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Security;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Methods for managing <see cref="ExtensionSet{TTarget}"/>s with null checking.
+    /// 
+    /// Most users will not use this class directly and its API is experimental and subject to change.
+    /// </summary>
+    public static class ExtensionSet
+    {
+        private static bool TryGetValue<TTarget>(ref ExtensionSet<TTarget> set, Extension extension, out IExtensionValue value) where TTarget : IExtendableMessage<TTarget>
+        {
+            if (set == null)
+            {
+                value = null;
+                return false;
+            }
+            return set.ValuesByNumber.TryGetValue(extension.FieldNumber, out value);
+        }
+
+        /// <summary>
+        /// Gets the value of the specified extension
+        /// </summary>
+        public static TValue Get<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
+        {
+            IExtensionValue value;
+            if (TryGetValue(ref set, extension, out value))
+            {
+                // The stored ExtensionValue can be a different type to what is being requested.
+                // This happens when the same extension proto is compiled in different assemblies.
+                // To allow consuming assemblies to still get the value when the TValue type is
+                // different, this get method:
+                // 1. Attempts to cast the value to the expected ExtensionValue<TValue>.
+                //    This is the usual case. It is used first because it avoids possibly boxing the value.
+                // 2. Fallback to get the value as object from IExtensionValue then casting.
+                //    This allows for someone to specify a TValue of object. They can then convert
+                //    the values to bytes and reparse using expected value.
+                // 3. If neither of these work, throw a user friendly error that the types aren't compatible.
+                if (value is ExtensionValue<TValue> extensionValue)
+                {
+                    return extensionValue.GetValue();
+                }
+                else if (value.GetValue() is TValue underlyingValue)
+                {
+                    return underlyingValue;
+                }
+                else
+                {
+                    var valueType = value.GetType().GetTypeInfo();
+                    if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(ExtensionValue<>))
+                    {
+                        var storedType = valueType.GenericTypeArguments[0];
+                        throw new InvalidOperationException(
+                            "The stored extension value has a type of '" + storedType.AssemblyQualifiedName + "'. " +
+                            "This a different from the requested type of '" + typeof(TValue).AssemblyQualifiedName + "'.");
+                    }
+                    else
+                    {
+                        throw new InvalidOperationException("Unexpected extension value type: " + valueType.AssemblyQualifiedName);
+                    }
+                }
+            }
+            else 
+            {
+                return extension.DefaultValue;
+            }
+        }
+
+        /// <summary>
+        /// Gets the value of the specified repeated extension or null if it doesn't exist in this set
+        /// </summary>
+        public static RepeatedField<TValue> Get<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
+        {
+            IExtensionValue value;
+            if (TryGetValue(ref set, extension, out value))
+            {
+                if (value is RepeatedExtensionValue<TValue> extensionValue)
+                {
+                    return extensionValue.GetValue();
+                }
+                else
+                {
+                    var valueType = value.GetType().GetTypeInfo();
+                    if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(RepeatedExtensionValue<>))
+                    {
+                        var storedType = valueType.GenericTypeArguments[0];
+                        throw new InvalidOperationException(
+                            "The stored extension value has a type of '" + storedType.AssemblyQualifiedName + "'. " +
+                            "This a different from the requested type of '" + typeof(TValue).AssemblyQualifiedName + "'.");
+                    }
+                    else
+                    {
+                        throw new InvalidOperationException("Unexpected extension value type: " + valueType.AssemblyQualifiedName);
+                    }
+                }
+            }
+            else 
+            {
+                return null;
+            }
+        }
+
+        /// <summary>
+        /// Gets the value of the specified repeated extension, registering it if it doesn't exist
+        /// </summary>
+        public static RepeatedField<TValue> GetOrInitialize<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
+        {
+            IExtensionValue value;
+            if (set == null)
+            {
+                value = extension.CreateValue();
+                set = new ExtensionSet<TTarget>();
+                set.ValuesByNumber.Add(extension.FieldNumber, value);
+            }
+            else
+            {
+                if (!set.ValuesByNumber.TryGetValue(extension.FieldNumber, out value))
+                {
+                    value = extension.CreateValue();
+                    set.ValuesByNumber.Add(extension.FieldNumber, value);
+                }
+            }
+
+            return ((RepeatedExtensionValue<TValue>)value).GetValue();
+        }
+
+        /// <summary>
+        /// Sets the value of the specified extension. This will make a new instance of ExtensionSet if the set is null.
+        /// </summary>
+        public static void Set<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension, TValue value) where TTarget : IExtendableMessage<TTarget>
+        {
+            ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
+
+            IExtensionValue extensionValue;
+            if (set == null)
+            {
+                extensionValue = extension.CreateValue();
+                set = new ExtensionSet<TTarget>();
+                set.ValuesByNumber.Add(extension.FieldNumber, extensionValue);
+            }
+            else
+            {
+                if (!set.ValuesByNumber.TryGetValue(extension.FieldNumber, out extensionValue))
+                {
+                    extensionValue = extension.CreateValue();
+                    set.ValuesByNumber.Add(extension.FieldNumber, extensionValue);
+                }
+            }
+            
+            ((ExtensionValue<TValue>)extensionValue).SetValue(value);
+        }
+
+        /// <summary>
+        /// Gets whether the value of the specified extension is set
+        /// </summary>
+        public static bool Has<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
+        {
+            IExtensionValue value;
+            return TryGetValue(ref set, extension, out value);
+        }
+
+        /// <summary>
+        /// Clears the value of the specified extension
+        /// </summary>
+        public static void Clear<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
+        {
+            if (set == null)
+            {
+                return;
+            }
+            set.ValuesByNumber.Remove(extension.FieldNumber);
+            if (set.ValuesByNumber.Count == 0)
+            {
+                set = null;
+            }
+        }
+
+        /// <summary>
+        /// Clears the value of the specified extension
+        /// </summary>
+        public static void Clear<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
+        {
+            if (set == null)
+            {
+                return;
+            }
+            set.ValuesByNumber.Remove(extension.FieldNumber);
+            if (set.ValuesByNumber.Count == 0)
+            {
+                set = null;
+            }
+        }
+
+        /// <summary>
+        /// Tries to merge a field from the coded input, returning true if the field was merged.
+        /// If the set is null or the field was not otherwise merged, this returns false.
+        /// </summary>
+        public static bool TryMergeFieldFrom<TTarget>(ref ExtensionSet<TTarget> set, CodedInputStream stream) where TTarget : IExtendableMessage<TTarget>
+        {
+            ParseContext.Initialize(stream, out ParseContext ctx);
+            try
+            {
+                return TryMergeFieldFrom<TTarget>(ref set, ref ctx);
+            }
+            finally
+            {
+                ctx.CopyStateTo(stream);
+            }
+        }
+
+        /// <summary>
+        /// Tries to merge a field from the coded input, returning true if the field was merged.
+        /// If the set is null or the field was not otherwise merged, this returns false.
+        /// </summary>
+        public static bool TryMergeFieldFrom<TTarget>(ref ExtensionSet<TTarget> set, ref ParseContext ctx) where TTarget : IExtendableMessage<TTarget>
+        {
+            Extension extension;
+            int lastFieldNumber = WireFormat.GetTagFieldNumber(ctx.LastTag);
+
+            IExtensionValue extensionValue;
+            if (set != null && set.ValuesByNumber.TryGetValue(lastFieldNumber, out extensionValue))
+            {
+                extensionValue.MergeFrom(ref ctx);
+                return true;
+            }
+            else if (ctx.ExtensionRegistry != null && ctx.ExtensionRegistry.ContainsInputField(ctx.LastTag, typeof(TTarget), out extension))
+            {
+                IExtensionValue value = extension.CreateValue();
+                value.MergeFrom(ref ctx);
+                set = (set ?? new ExtensionSet<TTarget>());
+                set.ValuesByNumber.Add(extension.FieldNumber, value);
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Merges the second set into the first set, creating a new instance if first is null
+        /// </summary>
+        public static void MergeFrom<TTarget>(ref ExtensionSet<TTarget> first, ExtensionSet<TTarget> second) where TTarget : IExtendableMessage<TTarget>
+        {
+            if (second == null)
+            {
+                return;
+            }
+            if (first == null)
+            {
+                first = new ExtensionSet<TTarget>();
+            }
+            foreach (var pair in second.ValuesByNumber)
+            {
+                IExtensionValue value;
+                if (first.ValuesByNumber.TryGetValue(pair.Key, out value))
+                {
+                    value.MergeFrom(pair.Value);
+                }
+                else
+                {
+                    var cloned = pair.Value.Clone();
+                    first.ValuesByNumber[pair.Key] = cloned;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Clones the set into a new set. If the set is null, this returns null
+        /// </summary>
+        public static ExtensionSet<TTarget> Clone<TTarget>(ExtensionSet<TTarget> set) where TTarget : IExtendableMessage<TTarget>
+        {
+            if (set == null)
+            {
+                return null;
+            }
+
+            var newSet = new ExtensionSet<TTarget>();
+            foreach (var pair in set.ValuesByNumber)
+            {
+                var cloned = pair.Value.Clone();
+                newSet.ValuesByNumber[pair.Key] = cloned;
+            }
+            return newSet;
+        }
+    }
+
+    /// <summary>
+    /// Used for keeping track of extensions in messages. 
+    /// <see cref="IExtendableMessage{T}"/> methods route to this set.
+    /// 
+    /// Most users will not need to use this class directly
+    /// </summary>
+    /// <typeparam name="TTarget">The message type that extensions in this set target</typeparam>
+    public sealed class ExtensionSet<TTarget> where TTarget : IExtendableMessage<TTarget>
+    {
+        internal Dictionary<int, IExtensionValue> ValuesByNumber { get; } = new Dictionary<int, IExtensionValue>();
+
+        /// <summary>
+        /// Gets a hash code of the set
+        /// </summary>
+        public override int GetHashCode()
+        {
+            int ret = typeof(TTarget).GetHashCode();
+            foreach (KeyValuePair<int, IExtensionValue> field in ValuesByNumber)
+            {
+                // Use ^ here to make the field order irrelevant.
+                int hash = field.Key.GetHashCode() ^ field.Value.GetHashCode();
+                ret ^= hash;
+            }
+            return ret;
+        }
+
+        /// <summary>
+        /// Returns whether this set is equal to the other object
+        /// </summary>
+        public override bool Equals(object other)
+        {
+            if (ReferenceEquals(this, other))
+            {
+                return true;
+            }
+            ExtensionSet<TTarget> otherSet = other as ExtensionSet<TTarget>;
+            if (ValuesByNumber.Count != otherSet.ValuesByNumber.Count)
+            {
+                return false;
+            }
+            foreach (var pair in ValuesByNumber)
+            {
+                IExtensionValue secondValue;
+                if (!otherSet.ValuesByNumber.TryGetValue(pair.Key, out secondValue))
+                {
+                    return false;
+                }
+                if (!pair.Value.Equals(secondValue))
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /// <summary>
+        /// Calculates the size of this extension set
+        /// </summary>
+        public int CalculateSize()
+        {
+            int size = 0;
+            foreach (var value in ValuesByNumber.Values)
+            {
+                size += value.CalculateSize();
+            }
+            return size;
+        }
+
+        /// <summary>
+        /// Writes the extension values in this set to the output stream
+        /// </summary>
+        public void WriteTo(CodedOutputStream stream)
+        {
+            
+            WriteContext.Initialize(stream, out WriteContext ctx);
+            try
+            {
+                WriteTo(ref ctx);
+            }
+            finally
+            {
+                ctx.CopyStateTo(stream);
+            }
+        }
+
+        /// <summary>
+        /// Writes the extension values in this set to the write context
+        /// </summary>
+        [SecuritySafeCritical]
+        public void WriteTo(ref WriteContext ctx)
+        {
+            foreach (var value in ValuesByNumber.Values)
+            {
+                value.WriteTo(ref ctx);
+            }
+        }
+
+        internal bool IsInitialized()
+        {
+            return ValuesByNumber.Values.All(v => v.IsInitialized());
+        }
+    }
+}
diff --git a/csharp/src/Google.Protobuf/FrameworkPortability.cs b/csharp/src/Google.Protobuf/FrameworkPortability.cs
index 9498dbe..1606411 100644
--- a/csharp/src/Google.Protobuf/FrameworkPortability.cs
+++ b/csharp/src/Google.Protobuf/FrameworkPortability.cs
@@ -1,49 +1,49 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.Text.RegularExpressions;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Class containing helpful workarounds for various platform compatibility

-    /// </summary>

-    internal static class FrameworkPortability

-    {

-        // The value of RegexOptions.Compiled is 8. We can test for the presence at

-        // execution time using Enum.IsDefined, so a single build will do the right thing

-        // on each platform. (RegexOptions.Compiled isn't supported by PCLs.)

-        internal static readonly RegexOptions CompiledRegexWhereAvailable =

-            Enum.IsDefined(typeof(RegexOptions), 8) ? (RegexOptions)8 : RegexOptions.None;

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Text.RegularExpressions;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Class containing helpful workarounds for various platform compatibility
+    /// </summary>
+    internal static class FrameworkPortability
+    {
+        // The value of RegexOptions.Compiled is 8. We can test for the presence at
+        // execution time using Enum.IsDefined, so a single build will do the right thing
+        // on each platform. (RegexOptions.Compiled isn't supported by PCLs.)
+        internal static readonly RegexOptions CompiledRegexWhereAvailable =
+            Enum.IsDefined(typeof(RegexOptions), 8) ? (RegexOptions)8 : RegexOptions.None;
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj
index db5329e..22253cb 100644
--- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj
+++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj
@@ -4,7 +4,7 @@
     <Description>C# runtime library for Protocol Buffers - Google's data interchange format.</Description>
     <Copyright>Copyright 2015, Google Inc.</Copyright>
     <AssemblyTitle>Google Protocol Buffers</AssemblyTitle>
-    <VersionPrefix>3.20.0-rc1</VersionPrefix>
+    <VersionPrefix>3.20.0</VersionPrefix>
     <!-- C# 7.2 is required for Span/BufferWriter/ReadOnlySequence -->
     <LangVersion>7.2</LangVersion>
     <Authors>Google Inc.</Authors>
diff --git a/csharp/src/Google.Protobuf/IMessage.cs b/csharp/src/Google.Protobuf/IMessage.cs
index d089f94..3e644c1 100644
--- a/csharp/src/Google.Protobuf/IMessage.cs
+++ b/csharp/src/Google.Protobuf/IMessage.cs
@@ -1,87 +1,87 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using Google.Protobuf.Reflection;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Interface for a Protocol Buffers message, supporting

-    /// basic operations required for serialization.

-    /// </summary>

-    public interface IMessage

-    {

-        /// <summary>

-        /// Merges the data from the specified coded input stream with the current message.

-        /// </summary>

-        /// <remarks>See the user guide for precise merge semantics.</remarks>

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

-        void MergeFrom(CodedInputStream input);

-

-        /// <summary>

-        /// Writes the data to the given coded output stream.

-        /// </summary>

-        /// <param name="output">Coded output stream to write the data to. Must not be null.</param>

-        void WriteTo(CodedOutputStream output);

-

-        /// <summary>

-        /// Calculates the size of this message in Protocol Buffer wire format, in bytes.

-        /// </summary>

-        /// <returns>The number of bytes required to write this message

-        /// to a coded output stream.</returns>

-        int CalculateSize();

-

-        /// <summary>

-        /// Descriptor for this message. All instances are expected to return the same descriptor,

-        /// and for generated types this will be an explicitly-implemented member, returning the

-        /// same value as the static property declared on the type.

-        /// </summary>

-        MessageDescriptor Descriptor { get; }

-    }

-

-    /// <summary>

-    /// Generic interface for a Protocol Buffers message,

-    /// where the type parameter is expected to be the same type as

-    /// the implementation class.

-    /// </summary>

-    /// <typeparam name="T">The message type.</typeparam>

-    public interface IMessage<T> : IMessage, IEquatable<T>, IDeepCloneable<T> where T : IMessage<T>

-    {

-        /// <summary>

-        /// Merges the given message into this one.

-        /// </summary>

-        /// <remarks>See the user guide for precise merge semantics.</remarks>

-        /// <param name="message">The message to merge with this one. Must not be null.</param>

-        void MergeFrom(T message);

-    }

-}

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using Google.Protobuf.Reflection;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Interface for a Protocol Buffers message, supporting
+    /// basic operations required for serialization.
+    /// </summary>
+    public interface IMessage
+    {
+        /// <summary>
+        /// Merges the data from the specified coded input stream with the current message.
+        /// </summary>
+        /// <remarks>See the user guide for precise merge semantics.</remarks>
+        /// <param name="input"></param>
+        void MergeFrom(CodedInputStream input);
+
+        /// <summary>
+        /// Writes the data to the given coded output stream.
+        /// </summary>
+        /// <param name="output">Coded output stream to write the data to. Must not be null.</param>
+        void WriteTo(CodedOutputStream output);
+
+        /// <summary>
+        /// Calculates the size of this message in Protocol Buffer wire format, in bytes.
+        /// </summary>
+        /// <returns>The number of bytes required to write this message
+        /// to a coded output stream.</returns>
+        int CalculateSize();
+
+        /// <summary>
+        /// Descriptor for this message. All instances are expected to return the same descriptor,
+        /// and for generated types this will be an explicitly-implemented member, returning the
+        /// same value as the static property declared on the type.
+        /// </summary>
+        MessageDescriptor Descriptor { get; }
+    }
+
+    /// <summary>
+    /// Generic interface for a Protocol Buffers message,
+    /// where the type parameter is expected to be the same type as
+    /// the implementation class.
+    /// </summary>
+    /// <typeparam name="T">The message type.</typeparam>
+    public interface IMessage<T> : IMessage, IEquatable<T>, IDeepCloneable<T> where T : IMessage<T>
+    {
+        /// <summary>
+        /// Merges the given message into this one.
+        /// </summary>
+        /// <remarks>See the user guide for precise merge semantics.</remarks>
+        /// <param name="message">The message to merge with this one. Must not be null.</param>
+        void MergeFrom(T message);
+    }
+}
diff --git a/csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs b/csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs
index c5ffe9b..6a3dbd6 100644
--- a/csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs
+++ b/csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs
@@ -1,140 +1,140 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-using System.IO;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Thrown when a protocol message being parsed is invalid in some way,

-    /// e.g. it contains a malformed varint or a negative byte length.

-    /// </summary>

-    public sealed class InvalidProtocolBufferException : IOException

-    {

-        internal InvalidProtocolBufferException(string message)

-            : base(message)

-        {

-        }

-

-        internal InvalidProtocolBufferException(string message, Exception innerException)

-            : base(message, innerException)

-        {

-        }

-

-        internal static InvalidProtocolBufferException MoreDataAvailable()

-        {

-            return new InvalidProtocolBufferException(

-                "Completed reading a message while more data was available in the stream.");

-        }

-

-        internal static InvalidProtocolBufferException TruncatedMessage()

-        {

-            return new InvalidProtocolBufferException(

-                "While parsing a protocol message, the input ended unexpectedly " +

-                "in the middle of a field.  This could mean either that the " +

-                "input has been truncated or that an embedded message " +

-                "misreported its own length.");

-        }

-

-        internal static InvalidProtocolBufferException NegativeSize()

-        {

-            return new InvalidProtocolBufferException(

-                "CodedInputStream encountered an embedded string or message " +

-                "which claimed to have negative size.");

-        }

-

-        internal static InvalidProtocolBufferException MalformedVarint()

-        {

-            return new InvalidProtocolBufferException(

-                "CodedInputStream encountered a malformed varint.");

-        }

-

-        /// <summary>

-        /// Creates an exception for an error condition of an invalid tag being encountered.

-        /// </summary>

-        internal static InvalidProtocolBufferException InvalidTag()

-        {

-            return new InvalidProtocolBufferException(

-                "Protocol message contained an invalid tag (zero).");

-        }

-

-        internal static InvalidProtocolBufferException InvalidWireType()

-        {

-            return new InvalidProtocolBufferException(

-                "Protocol message contained a tag with an invalid wire type.");

-        }

-

-        internal static InvalidProtocolBufferException InvalidBase64(Exception innerException)

-        {

-            return new InvalidProtocolBufferException("Invalid base64 data", innerException);

-        }

-

-        internal static InvalidProtocolBufferException InvalidEndTag()

-        {

-            return new InvalidProtocolBufferException(

-                "Protocol message end-group tag did not match expected tag.");

-        }

-

-        internal static InvalidProtocolBufferException RecursionLimitExceeded()

-        {

-            return new InvalidProtocolBufferException(

-                "Protocol message had too many levels of nesting.  May be malicious.  " +

-                "Use CodedInputStream.SetRecursionLimit() to increase the depth limit.");

-        }

-

-        internal static InvalidProtocolBufferException JsonRecursionLimitExceeded()

-        {

-            return new InvalidProtocolBufferException(

-                "Protocol message had too many levels of nesting.  May be malicious.  " +

-                "Use JsonParser.Settings to increase the depth limit.");

-        }

-

-        internal static InvalidProtocolBufferException SizeLimitExceeded()

-        {

-            return new InvalidProtocolBufferException(

-                "Protocol message was too large.  May be malicious.  " +

-                "Use CodedInputStream.SetSizeLimit() to increase the size limit.");

-        }

-

-        internal static InvalidProtocolBufferException InvalidMessageStreamTag()

-        {

-            return new InvalidProtocolBufferException(

-                "Stream of protocol messages had invalid tag. Expected tag is length-delimited field 1.");

-        }

-

-        internal static InvalidProtocolBufferException MissingFields()

-        {

-            return new InvalidProtocolBufferException("Message was missing required fields");

-        }

-}

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.IO;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Thrown when a protocol message being parsed is invalid in some way,
+    /// e.g. it contains a malformed varint or a negative byte length.
+    /// </summary>
+    public sealed class InvalidProtocolBufferException : IOException
+    {
+        internal InvalidProtocolBufferException(string message)
+            : base(message)
+        {
+        }
+
+        internal InvalidProtocolBufferException(string message, Exception innerException)
+            : base(message, innerException)
+        {
+        }
+
+        internal static InvalidProtocolBufferException MoreDataAvailable()
+        {
+            return new InvalidProtocolBufferException(
+                "Completed reading a message while more data was available in the stream.");
+        }
+
+        internal static InvalidProtocolBufferException TruncatedMessage()
+        {
+            return new InvalidProtocolBufferException(
+                "While parsing a protocol message, the input ended unexpectedly " +
+                "in the middle of a field.  This could mean either that the " +
+                "input has been truncated or that an embedded message " +
+                "misreported its own length.");
+        }
+
+        internal static InvalidProtocolBufferException NegativeSize()
+        {
+            return new InvalidProtocolBufferException(
+                "CodedInputStream encountered an embedded string or message " +
+                "which claimed to have negative size.");
+        }
+
+        internal static InvalidProtocolBufferException MalformedVarint()
+        {
+            return new InvalidProtocolBufferException(
+                "CodedInputStream encountered a malformed varint.");
+        }
+
+        /// <summary>
+        /// Creates an exception for an error condition of an invalid tag being encountered.
+        /// </summary>
+        internal static InvalidProtocolBufferException InvalidTag()
+        {
+            return new InvalidProtocolBufferException(
+                "Protocol message contained an invalid tag (zero).");
+        }
+
+        internal static InvalidProtocolBufferException InvalidWireType()
+        {
+            return new InvalidProtocolBufferException(
+                "Protocol message contained a tag with an invalid wire type.");
+        }
+
+        internal static InvalidProtocolBufferException InvalidBase64(Exception innerException)
+        {
+            return new InvalidProtocolBufferException("Invalid base64 data", innerException);
+        }
+
+        internal static InvalidProtocolBufferException InvalidEndTag()
+        {
+            return new InvalidProtocolBufferException(
+                "Protocol message end-group tag did not match expected tag.");
+        }
+
+        internal static InvalidProtocolBufferException RecursionLimitExceeded()
+        {
+            return new InvalidProtocolBufferException(
+                "Protocol message had too many levels of nesting.  May be malicious.  " +
+                "Use CodedInputStream.SetRecursionLimit() to increase the depth limit.");
+        }
+
+        internal static InvalidProtocolBufferException JsonRecursionLimitExceeded()
+        {
+            return new InvalidProtocolBufferException(
+                "Protocol message had too many levels of nesting.  May be malicious.  " +
+                "Use JsonParser.Settings to increase the depth limit.");
+        }
+
+        internal static InvalidProtocolBufferException SizeLimitExceeded()
+        {
+            return new InvalidProtocolBufferException(
+                "Protocol message was too large.  May be malicious.  " +
+                "Use CodedInputStream.SetSizeLimit() to increase the size limit.");
+        }
+
+        internal static InvalidProtocolBufferException InvalidMessageStreamTag()
+        {
+            return new InvalidProtocolBufferException(
+                "Stream of protocol messages had invalid tag. Expected tag is length-delimited field 1.");
+        }
+
+        internal static InvalidProtocolBufferException MissingFields()
+        {
+            return new InvalidProtocolBufferException("Message was missing required fields");
+        }
+}
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs
index db7dc5c..16f7c5a 100644
--- a/csharp/src/Google.Protobuf/JsonFormatter.cs
+++ b/csharp/src/Google.Protobuf/JsonFormatter.cs
@@ -1,4 +1,4 @@
-#region Copyright notice and license
+#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/
@@ -233,13 +233,13 @@
                     writer.Write(PropertySeparator);
                 }
 
-                if (settings.PreserveProtoFieldNames)

-                {

-                    WriteString(writer, accessor.Descriptor.Name);

+                if (settings.PreserveProtoFieldNames)
+                {
+                    WriteString(writer, accessor.Descriptor.Name);
                 }
-                else

-                {

-                    WriteString(writer, accessor.Descriptor.JsonName);

+                else
+                {
+                    WriteString(writer, accessor.Descriptor.JsonName);
                 }
                 writer.Write(NameValueSeparator);
                 WriteValue(writer, value);
@@ -823,8 +823,8 @@
             /// </summary>
             public bool FormatEnumsAsIntegers { get; }
 
-            /// <summary>

-            /// Whether to use the original proto field names as defined in the .proto file. Defaults to false.

+            /// <summary>
+            /// Whether to use the original proto field names as defined in the .proto file. Defaults to false.
             /// </summary>
             public bool PreserveProtoFieldNames { get; }
 
@@ -884,9 +884,9 @@
             /// <param name="formatEnumsAsIntegers"><c>true</c> to format the enums as integers; <c>false</c> to format enums as enum names.</param>
             public Settings WithFormatEnumsAsIntegers(bool formatEnumsAsIntegers) => new Settings(FormatDefaultValues, TypeRegistry, formatEnumsAsIntegers, PreserveProtoFieldNames);
 
-            /// <summary>

-            /// Creates a new <see cref="Settings"/> object with the specified field name formatting option and the current settings.

-            /// </summary>

+            /// <summary>
+            /// Creates a new <see cref="Settings"/> object with the specified field name formatting option and the current settings.
+            /// </summary>
             /// <param name="preserveProtoFieldNames"><c>true</c> to preserve proto field names; <c>false</c> to convert them to lowerCamelCase.</param>
             public Settings WithPreserveProtoFieldNames(bool preserveProtoFieldNames) => new Settings(FormatDefaultValues, TypeRegistry, FormatEnumsAsIntegers, preserveProtoFieldNames);
         }
diff --git a/csharp/src/Google.Protobuf/ObjectIntPair.cs b/csharp/src/Google.Protobuf/ObjectIntPair.cs
index b98d93a..b61fc68 100644
--- a/csharp/src/Google.Protobuf/ObjectIntPair.cs
+++ b/csharp/src/Google.Protobuf/ObjectIntPair.cs
@@ -1,40 +1,40 @@
-using System;

-

-namespace Google.Protobuf

-{

+using System;
+
+namespace Google.Protobuf
+{
     /// <summary>
     /// Struct used to hold the keys for the fieldByNumber table in DescriptorPool and the keys for the 
     /// extensionByNumber table in ExtensionRegistry.
-    /// </summary>

-    internal struct ObjectIntPair<T> : IEquatable<ObjectIntPair<T>> where T : class

-    {

-        private readonly int number;

-        private readonly T obj;

-

-        internal ObjectIntPair(T obj, int number)

-        {

-            this.number = number;

-            this.obj = obj;

-        }

-

-        public bool Equals(ObjectIntPair<T> other)

-        {

-            return obj == other.obj

-                   && number == other.number;

-        }

-

-        public override bool Equals(object obj)

-        {

-            if (obj is ObjectIntPair<T>)

-            {

-                return Equals((ObjectIntPair<T>)obj);

-            }

-            return false;

-        }

-

-        public override int GetHashCode()

-        {

-            return obj.GetHashCode() * ((1 << 16) - 1) + number;

-        }

-    }

-}

+    /// </summary>
+    internal struct ObjectIntPair<T> : IEquatable<ObjectIntPair<T>> where T : class
+    {
+        private readonly int number;
+        private readonly T obj;
+
+        internal ObjectIntPair(T obj, int number)
+        {
+            this.number = number;
+            this.obj = obj;
+        }
+
+        public bool Equals(ObjectIntPair<T> other)
+        {
+            return obj == other.obj
+                   && number == other.number;
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (obj is ObjectIntPair<T>)
+            {
+                return Equals((ObjectIntPair<T>)obj);
+            }
+            return false;
+        }
+
+        public override int GetHashCode()
+        {
+            return obj.GetHashCode() * ((1 << 16) - 1) + number;
+        }
+    }
+}
diff --git a/csharp/src/Google.Protobuf/Properties/AssemblyInfo.cs b/csharp/src/Google.Protobuf/Properties/AssemblyInfo.cs
index 130bcf0..4328e2e 100644
--- a/csharp/src/Google.Protobuf/Properties/AssemblyInfo.cs
+++ b/csharp/src/Google.Protobuf/Properties/AssemblyInfo.cs
@@ -1,56 +1,56 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System.Runtime.CompilerServices;

-using System.Security;

-

-// General Information about an assembly is controlled through the following 

-// set of attributes. Change these attribute values to modify the information

-// associated with an assembly.

-

-#if !NCRUNCH

-[assembly: AllowPartiallyTrustedCallers]

-#endif

-

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

-    "002400000480000094000000060200000024000052534131000400000100010025800fbcfc63a1" +

-    "7c66b303aae80b03a6beaa176bb6bef883be436f2a1579edd80ce23edf151a1f4ced97af83abcd" +

-    "981207041fd5b2da3b498346fcfcd94910d52f25537c4a43ce3fbe17dc7d43e6cbdb4d8f1242dc" +

-    "b6bd9b5906be74da8daa7d7280f97130f318a16c07baf118839b156299a48522f9fae2371c9665" +

-    "c5ae9cb6")]

-

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

-    "002400000480000094000000060200000024000052534131000400000100010025800fbcfc63a1" +

-    "7c66b303aae80b03a6beaa176bb6bef883be436f2a1579edd80ce23edf151a1f4ced97af83abcd" +

-    "981207041fd5b2da3b498346fcfcd94910d52f25537c4a43ce3fbe17dc7d43e6cbdb4d8f1242dc" +

-    "b6bd9b5906be74da8daa7d7280f97130f318a16c07baf118839b156299a48522f9fae2371c9665" +

-    "c5ae9cb6")]

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System.Runtime.CompilerServices;
+using System.Security;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+
+#if !NCRUNCH
+[assembly: AllowPartiallyTrustedCallers]
+#endif
+
+[assembly: InternalsVisibleTo("Google.Protobuf.Test, PublicKey=" +
+    "002400000480000094000000060200000024000052534131000400000100010025800fbcfc63a1" +
+    "7c66b303aae80b03a6beaa176bb6bef883be436f2a1579edd80ce23edf151a1f4ced97af83abcd" +
+    "981207041fd5b2da3b498346fcfcd94910d52f25537c4a43ce3fbe17dc7d43e6cbdb4d8f1242dc" +
+    "b6bd9b5906be74da8daa7d7280f97130f318a16c07baf118839b156299a48522f9fae2371c9665" +
+    "c5ae9cb6")]
+
+[assembly: InternalsVisibleTo("Google.Protobuf.Benchmarks, PublicKey=" +
+    "002400000480000094000000060200000024000052534131000400000100010025800fbcfc63a1" +
+    "7c66b303aae80b03a6beaa176bb6bef883be436f2a1579edd80ce23edf151a1f4ced97af83abcd" +
+    "981207041fd5b2da3b498346fcfcd94910d52f25537c4a43ce3fbe17dc7d43e6cbdb4d8f1242dc" +
+    "b6bd9b5906be74da8daa7d7280f97130f318a16c07baf118839b156299a48522f9fae2371c9665" +
+    "c5ae9cb6")]
diff --git a/csharp/src/Google.Protobuf/ProtoPreconditions.cs b/csharp/src/Google.Protobuf/ProtoPreconditions.cs
index 590a3bb..1814b8b 100644
--- a/csharp/src/Google.Protobuf/ProtoPreconditions.cs
+++ b/csharp/src/Google.Protobuf/ProtoPreconditions.cs
@@ -1,79 +1,79 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-using System;

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// Helper methods for throwing exceptions when preconditions are not met.

-    /// </summary>

-    /// <remarks>

-    /// This class is used internally and by generated code; it is not particularly

-    /// expected to be used from application code, although nothing prevents it

-    /// from being used that way.

-    /// </remarks>

-    public static class ProtoPreconditions

-    {

-        /// <summary>

-        /// Throws an ArgumentNullException if the given value is null, otherwise

-        /// return the value to the caller.

-        /// </summary>

-        public static T CheckNotNull<T>(T value, string name) where T : class

-        {

-            if (value == null)

-            {

-                throw new ArgumentNullException(name);

-            }

-            return value;

-        }

-

-        /// <summary>

-        /// Throws an ArgumentNullException if the given value is null, otherwise

-        /// return the value to the caller.

-        /// </summary>

-        /// <remarks>

-        /// This is equivalent to <see cref="CheckNotNull{T}(T, string)"/> but without the type parameter

-        /// constraint. In most cases, the constraint is useful to prevent you from calling CheckNotNull

-        /// with a value type - but it gets in the way if either you want to use it with a nullable

-        /// value type, or you want to use it with an unconstrained type parameter.

-        /// </remarks>

-        internal static T CheckNotNullUnconstrained<T>(T value, string name)

-        {

-            if (value == null)

-            {

-                throw new ArgumentNullException(name);

-            }

-            return value;

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Helper methods for throwing exceptions when preconditions are not met.
+    /// </summary>
+    /// <remarks>
+    /// This class is used internally and by generated code; it is not particularly
+    /// expected to be used from application code, although nothing prevents it
+    /// from being used that way.
+    /// </remarks>
+    public static class ProtoPreconditions
+    {
+        /// <summary>
+        /// Throws an ArgumentNullException if the given value is null, otherwise
+        /// return the value to the caller.
+        /// </summary>
+        public static T CheckNotNull<T>(T value, string name) where T : class
+        {
+            if (value == null)
+            {
+                throw new ArgumentNullException(name);
+            }
+            return value;
+        }
+
+        /// <summary>
+        /// Throws an ArgumentNullException if the given value is null, otherwise
+        /// return the value to the caller.
+        /// </summary>
+        /// <remarks>
+        /// This is equivalent to <see cref="CheckNotNull{T}(T, string)"/> but without the type parameter
+        /// constraint. In most cases, the constraint is useful to prevent you from calling CheckNotNull
+        /// with a value type - but it gets in the way if either you want to use it with a nullable
+        /// value type, or you want to use it with an unconstrained type parameter.
+        /// </remarks>
+        internal static T CheckNotNullUnconstrained<T>(T value, string name)
+        {
+            if (value == null)
+            {
+                throw new ArgumentNullException(name);
+            }
+            return value;
+        }
+    }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/Reflection/Descriptor.cs b/csharp/src/Google.Protobuf/Reflection/Descriptor.cs
index cd4d095..5967a4f 100644
--- a/csharp/src/Google.Protobuf/Reflection/Descriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/Descriptor.cs
@@ -9611,8 +9611,8 @@
       /// The name of the uninterpreted option.  Each string represents a segment in
       /// a dot-separated name.  is_extension is true iff a segment represents an
       /// extension (denoted with parentheses in options specs in .proto files).
-      /// E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
-      /// "foo.(bar.baz).qux".
+      /// E.g.,{ ["foo", false], ["bar.baz", true], ["moo", false] } represents
+      /// "foo.(bar.baz).moo".
       /// </summary>
       public sealed partial class NamePart : pb::IMessage<NamePart>
       #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
@@ -10241,13 +10241,13 @@
         ///   // Comment attached to baz.
         ///   // Another line attached to baz.
         ///
-        ///   // Comment attached to qux.
+        ///   // Comment attached to moo.
         ///   //
-        ///   // Another line attached to qux.
-        ///   optional double qux = 4;
+        ///   // Another line attached to moo.
+        ///   optional double moo = 4;
         ///
         ///   // Detached comment for corge. This is not leading or trailing comments
-        ///   // to qux or corge because there are blank lines separating it from
+        ///   // to moo or corge because there are blank lines separating it from
         ///   // both.
         ///
         ///   // Detached comment for corge paragraph 2.
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs
index 49f2599..5e7b6d5 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs
@@ -1,4 +1,4 @@
-#region Copyright notice and license
+#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/
@@ -67,15 +67,15 @@
             return lastSlash == -1 ? "" : typeUrl.Substring(lastSlash + 1);
         }
 
-        /// <summary>

-        /// Returns a bool indictating whether this Any message is of the target message type

-        /// </summary>

-        /// <param name="descriptor">The descriptor of the message type</param>

+        /// <summary>
+        /// Returns a bool indictating whether this Any message is of the target message type
+        /// </summary>
+        /// <param name="descriptor">The descriptor of the message type</param>
         /// <returns><c>true</c> if the type name matches the descriptor's full name or <c>false</c> otherwise</returns>
-        public bool Is(MessageDescriptor descriptor)

-        {

-            ProtoPreconditions.CheckNotNull(descriptor, nameof(descriptor));

-            return GetTypeName(TypeUrl) == descriptor.FullName;

+        public bool Is(MessageDescriptor descriptor)
+        {
+            ProtoPreconditions.CheckNotNull(descriptor, nameof(descriptor));
+            return GetTypeName(TypeUrl) == descriptor.FullName;
         }
 
         /// <summary>
diff --git a/csharp/src/Google.Protobuf/WireFormat.cs b/csharp/src/Google.Protobuf/WireFormat.cs
index 201fd16..7cd2ada 100644
--- a/csharp/src/Google.Protobuf/WireFormat.cs
+++ b/csharp/src/Google.Protobuf/WireFormat.cs
@@ -1,104 +1,104 @@
-#region Copyright notice and license

-// Protocol Buffers - Google's data interchange format

-// Copyright 2008 Google Inc.  All rights reserved.

-// https://developers.google.com/protocol-buffers/

-//

-// Redistribution and use in source and binary forms, with or without

-// modification, are permitted provided that the following conditions are

-// met:

-//

-//     * Redistributions of source code must retain the above copyright

-// notice, this list of conditions and the following disclaimer.

-//     * Redistributions in binary form must reproduce the above

-// copyright notice, this list of conditions and the following disclaimer

-// in the documentation and/or other materials provided with the

-// distribution.

-//     * Neither the name of Google Inc. nor the names of its

-// contributors may be used to endorse or promote products derived from

-// this software without specific prior written permission.

-//

-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-#endregion

-

-namespace Google.Protobuf

-{

-    /// <summary>

-    /// This class is used internally by the Protocol Buffer Library and generated

-    /// message implementations. It is public only for the sake of those generated

-    /// messages. Others should not use this class directly.

-    /// <para>

-    /// This class contains constants and helper functions useful for dealing with

-    /// the Protocol Buffer wire format.

-    /// </para>

-    /// </summary>

-    public static class WireFormat

-    {

-        /// <summary>

-        /// Wire types within protobuf encoding.

-        /// </summary>

-        public enum WireType : uint

-        {

-            /// <summary>

-            /// Variable-length integer.

-            /// </summary>

-            Varint = 0,

-            /// <summary>

-            /// A fixed-length 64-bit value.

-            /// </summary>

-            Fixed64 = 1,

-            /// <summary>

-            /// A length-delimited value, i.e. a length followed by that many bytes of data.

-            /// </summary>

-            LengthDelimited = 2,

-            /// <summary>

-            /// A "start group" value

-            /// </summary>

-            StartGroup = 3,

-            /// <summary>

-            /// An "end group" value

-            /// </summary>

-            EndGroup = 4,

-            /// <summary>

-            /// A fixed-length 32-bit value.

-            /// </summary>

-            Fixed32 = 5

-        }

-

-        private const int TagTypeBits = 3;

-        private const uint TagTypeMask = (1 << TagTypeBits) - 1;

-

-        /// <summary>

-        /// Given a tag value, determines the wire type (lower 3 bits).

-        /// </summary>

-        public static WireType GetTagWireType(uint tag)

-        {

-            return (WireType) (tag & TagTypeMask);

-        }

-

-        /// <summary>

-        /// Given a tag value, determines the field number (the upper 29 bits).

-        /// </summary>

-        public static int GetTagFieldNumber(uint tag)

-        {

-            return (int) (tag >> TagTypeBits);

-        }

-

-        /// <summary>

-        /// Makes a tag value given a field number and wire type.

-        /// </summary>

-        public static uint MakeTag(int fieldNumber, WireType wireType)

-        {

-            return (uint) (fieldNumber << TagTypeBits) | (uint) wireType;

-        }

-    }

+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// This class is used internally by the Protocol Buffer Library and generated
+    /// message implementations. It is public only for the sake of those generated
+    /// messages. Others should not use this class directly.
+    /// <para>
+    /// This class contains constants and helper functions useful for dealing with
+    /// the Protocol Buffer wire format.
+    /// </para>
+    /// </summary>
+    public static class WireFormat
+    {
+        /// <summary>
+        /// Wire types within protobuf encoding.
+        /// </summary>
+        public enum WireType : uint
+        {
+            /// <summary>
+            /// Variable-length integer.
+            /// </summary>
+            Varint = 0,
+            /// <summary>
+            /// A fixed-length 64-bit value.
+            /// </summary>
+            Fixed64 = 1,
+            /// <summary>
+            /// A length-delimited value, i.e. a length followed by that many bytes of data.
+            /// </summary>
+            LengthDelimited = 2,
+            /// <summary>
+            /// A "start group" value
+            /// </summary>
+            StartGroup = 3,
+            /// <summary>
+            /// An "end group" value
+            /// </summary>
+            EndGroup = 4,
+            /// <summary>
+            /// A fixed-length 32-bit value.
+            /// </summary>
+            Fixed32 = 5
+        }
+
+        private const int TagTypeBits = 3;
+        private const uint TagTypeMask = (1 << TagTypeBits) - 1;
+
+        /// <summary>
+        /// Given a tag value, determines the wire type (lower 3 bits).
+        /// </summary>
+        public static WireType GetTagWireType(uint tag)
+        {
+            return (WireType) (tag & TagTypeMask);
+        }
+
+        /// <summary>
+        /// Given a tag value, determines the field number (the upper 29 bits).
+        /// </summary>
+        public static int GetTagFieldNumber(uint tag)
+        {
+            return (int) (tag >> TagTypeBits);
+        }
+
+        /// <summary>
+        /// Makes a tag value given a field number and wire type.
+        /// </summary>
+        public static uint MakeTag(int fieldNumber, WireType wireType)
+        {
+            return (uint) (fieldNumber << TagTypeBits) | (uint) wireType;
+        }
+    }
 }
\ No newline at end of file
diff --git a/docs/third_party.md b/docs/third_party.md
index ca1415a..d8478e9 100644
--- a/docs/third_party.md
+++ b/docs/third_party.md
@@ -71,6 +71,7 @@
 * Kotlin: https://github.com/Kotlin/kotlinx.serialization
 * Kotlin: https://github.com/ButterCam/sisyphus
 * Kotlin: https://github.com/open-toast/protokt
+* Kotlin Multiplatform: https://github.com/streem/pbandk
 * Lua: https://code.google.com/p/protoc-gen-lua/
 * Lua: http://github.com/indygreg/lua-protobuf
 * Lua: https://github.com/Neopallium/lua-pb
diff --git a/java/README.md b/java/README.md
index ecbb9a6..df68050 100644
--- a/java/README.md
+++ b/java/README.md
@@ -23,7 +23,7 @@
 <dependency>
   <groupId>com.google.protobuf</groupId>
   <artifactId>protobuf-java</artifactId>
-  <version>3.20.0-rc-1</version>
+  <version>3.20.0</version>
 </dependency>
 ```
 
@@ -37,7 +37,7 @@
 <dependency>
   <groupId>com.google.protobuf</groupId>
   <artifactId>protobuf-java-util</artifactId>
-  <version>3.20.0-rc-1</version>
+  <version>3.20.0</version>
 </dependency>
 ```
 
@@ -45,7 +45,7 @@
 
 If you are using Gradle, add the following to your `build.gradle` file's dependencies:
 ```
-    implementation 'com.google.protobuf:protobuf-java:3.20.0-rc-1'
+    implementation 'com.google.protobuf:protobuf-java:3.20.0'
 ```
 Again, be sure to check that the version number matches (or is newer than) the version number of protoc that you are using.
 
diff --git a/java/bom/pom.xml b/java/bom/pom.xml
index c5bcb0a..5ebf16a 100644
--- a/java/bom/pom.xml
+++ b/java/bom/pom.xml
@@ -4,7 +4,7 @@
 
   <groupId>com.google.protobuf</groupId>
   <artifactId>protobuf-bom</artifactId>
-  <version>3.20.0-rc-1</version>
+  <version>3.20.0</version>
   <packaging>pom</packaging>
 
   <name>Protocol Buffers [BOM]</name>
diff --git a/java/core/pom.xml b/java/core/pom.xml
index bce2b11..9dadd70 100644
--- a/java/core/pom.xml
+++ b/java/core/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>com.google.protobuf</groupId>
     <artifactId>protobuf-parent</artifactId>
-    <version>3.20.0-rc-1</version>
+    <version>3.20.0</version>
   </parent>
 
   <artifactId>protobuf-java</artifactId>
diff --git a/java/core/src/main/java/com/google/protobuf/Descriptors.java b/java/core/src/main/java/com/google/protobuf/Descriptors.java
index 5277d1e..a36195b 100644
--- a/java/core/src/main/java/com/google/protobuf/Descriptors.java
+++ b/java/core/src/main/java/com/google/protobuf/Descriptors.java
@@ -1295,13 +1295,13 @@
      *   }
      *   message Bar {
      *     extend Foo {
-     *       optional int32 qux = 4321;
+     *       optional int32 moo = 4321;
      *     }
      *   }
      * </pre>
      *
-     * Both {@code baz}'s and {@code qux}'s containing type is {@code Foo}. However, {@code baz}'s
-     * extension scope is {@code null} while {@code qux}'s extension scope is {@code Bar}.
+     * Both {@code baz}'s and {@code moo}'s containing type is {@code Foo}. However, {@code baz}'s
+     * extension scope is {@code null} while {@code moo}'s extension scope is {@code Bar}.
      */
     public Descriptor getExtensionScope() {
       if (!isExtension()) {
diff --git a/java/core/src/main/java/com/google/protobuf/TextFormat.java b/java/core/src/main/java/com/google/protobuf/TextFormat.java
index 7337bc2..b401cdf 100644
--- a/java/core/src/main/java/com/google/protobuf/TextFormat.java
+++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java
@@ -472,7 +472,7 @@
 
       private final FieldDescriptor.JavaType fieldType;
 
-      public MapEntryAdapter(Object entry, FieldDescriptor fieldDescriptor) {
+      MapEntryAdapter(Object entry, FieldDescriptor fieldDescriptor) {
         if (entry instanceof MapEntry) {
           this.mapEntry = (MapEntry) entry;
         } else {
@@ -485,14 +485,14 @@
         return fieldDescriptor.getMessageType().getFields().get(0).getJavaType();
       }
 
-      public Object getKey() {
+      Object getKey() {
         if (mapEntry != null) {
           return mapEntry.getKey();
         }
         return null;
       }
 
-      public Object getEntry() {
+      Object getEntry() {
         if (mapEntry != null) {
           return mapEntry;
         }
@@ -1557,13 +1557,13 @@
 
     /**
      * Determines if repeated values for non-repeated fields and oneofs are permitted. For example,
-     * given required/optional field "foo" and a oneof containing "baz" and "qux":
+     * given required/optional field "foo" and a oneof containing "baz" and "moo":
      *
      * <ul>
      *   <li>"foo: 1 foo: 2"
-     *   <li>"baz: 1 qux: 2"
+     *   <li>"baz: 1 moo: 2"
      *   <li>merging "foo: 2" into a proto in which foo is already set, or
-     *   <li>merging "qux: 2" into a proto in which baz is already set.
+     *   <li>merging "moo: 2" into a proto in which baz is already set.
      * </ul>
      */
     public enum SingularOverwritePolicy {
diff --git a/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
index 26d0e4f..55864af 100644
--- a/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
+++ b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
@@ -1498,7 +1498,7 @@
     assertThat(message.hasFooStringPiece()).isTrue();
     TestUtil.assertAtMostOneFieldSetOneof(message);
 
-    message = builder.setFooBytes(TestUtil.toBytes("qux")).buildPartial();
+    message = builder.setFooBytes(TestUtil.toBytes("moo")).buildPartial();
     assertThat(message.hasFooBytes()).isTrue();
     TestUtil.assertAtMostOneFieldSetOneof(message);
 
@@ -1508,7 +1508,7 @@
 
     message =
         builder
-            .setFooMessage(TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build())
+            .setFooMessage(TestOneof2.NestedMessage.newBuilder().setMooInt(234).build())
             .buildPartial();
     assertThat(message.hasFooMessage()).isTrue();
     TestUtil.assertAtMostOneFieldSetOneof(message);
@@ -1625,31 +1625,31 @@
     {
       // set
       TestOneof2.Builder builder = TestOneof2.newBuilder();
-      assertThat(builder.getFooMessage().getQuxInt()).isEqualTo(0);
-      builder.setFooMessage(TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build());
+      assertThat(builder.getFooMessage().getMooInt()).isEqualTo(0);
+      builder.setFooMessage(TestOneof2.NestedMessage.newBuilder().setMooInt(234).build());
       assertThat(builder.hasFooMessage()).isTrue();
-      assertThat(builder.getFooMessage().getQuxInt()).isEqualTo(234);
+      assertThat(builder.getFooMessage().getMooInt()).isEqualTo(234);
       TestOneof2 message = builder.buildPartial();
       assertThat(message.hasFooMessage()).isTrue();
-      assertThat(message.getFooMessage().getQuxInt()).isEqualTo(234);
+      assertThat(message.getFooMessage().getMooInt()).isEqualTo(234);
 
       // clear
       assertThat(builder.clearFooMessage().hasFooString()).isFalse();
       message = builder.build();
       assertThat(message.hasFooMessage()).isFalse();
-      assertThat(message.getFooMessage().getQuxInt()).isEqualTo(0);
+      assertThat(message.getFooMessage().getMooInt()).isEqualTo(0);
 
       // nested builder
       builder = TestOneof2.newBuilder();
       assertThat(builder.getFooMessageOrBuilder())
           .isSameInstanceAs(TestOneof2.NestedMessage.getDefaultInstance());
       assertThat(builder.hasFooMessage()).isFalse();
-      builder.getFooMessageBuilder().setQuxInt(123);
+      builder.getFooMessageBuilder().setMooInt(123);
       assertThat(builder.hasFooMessage()).isTrue();
-      assertThat(builder.getFooMessage().getQuxInt()).isEqualTo(123);
+      assertThat(builder.getFooMessage().getMooInt()).isEqualTo(123);
       message = builder.build();
       assertThat(message.hasFooMessage()).isTrue();
-      assertThat(message.getFooMessage().getQuxInt()).isEqualTo(123);
+      assertThat(message.getFooMessage().getMooInt()).isEqualTo(123);
     }
 
     // LazyMessage is tested in LazyMessageLiteTest.java
@@ -1689,10 +1689,10 @@
   public void testOneofMergeMessage_mergeIntoNewBuilder() {
     TestOneof2.Builder builder = TestOneof2.newBuilder();
     TestOneof2 message =
-        builder.setFooMessage(TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build()).build();
+        builder.setFooMessage(TestOneof2.NestedMessage.newBuilder().setMooInt(234).build()).build();
     TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
     assertThat(message2.hasFooMessage()).isTrue();
-    assertThat(message2.getFooMessage().getQuxInt()).isEqualTo(234);
+    assertThat(message2.getFooMessage().getMooInt()).isEqualTo(234);
   }
 
   @Test
@@ -1755,12 +1755,12 @@
       TestOneof2.Builder builder = TestOneof2.newBuilder();
       TestOneof2 message =
           builder
-              .setFooMessage(TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build())
+              .setFooMessage(TestOneof2.NestedMessage.newBuilder().setMooInt(234).build())
               .build();
       ByteString serialized = message.toByteString();
       TestOneof2 message2 = TestOneof2.parseFrom(serialized);
       assertThat(message2.hasFooMessage()).isTrue();
-      assertThat(message2.getFooMessage().getQuxInt()).isEqualTo(234);
+      assertThat(message2.getFooMessage().getMooInt()).isEqualTo(234);
     }
   }
 
diff --git a/java/core/src/test/java/com/google/protobuf/TestUtil.java b/java/core/src/test/java/com/google/protobuf/TestUtil.java
index 377f34c..0b1852e 100644
--- a/java/core/src/test/java/com/google/protobuf/TestUtil.java
+++ b/java/core/src/test/java/com/google/protobuf/TestUtil.java
@@ -2537,7 +2537,7 @@
   // ===================================================================
   // oneof
   public static void setOneof(TestOneof2.Builder message) {
-    message.setFooLazyMessage(TestOneof2.NestedMessage.newBuilder().setQuxInt(100).build());
+    message.setFooLazyMessage(TestOneof2.NestedMessage.newBuilder().setMooInt(100).build());
     message.setBarString("101");
     message.setBazInt(102);
     message.setBazString("103");
@@ -2545,13 +2545,13 @@
 
   public static void assertOneofSet(TestOneof2 message) {
     Assert.assertTrue(message.hasFooLazyMessage());
-    Assert.assertTrue(message.getFooLazyMessage().hasQuxInt());
+    Assert.assertTrue(message.getFooLazyMessage().hasMooInt());
 
     Assert.assertTrue(message.hasBarString());
     Assert.assertTrue(message.hasBazInt());
     Assert.assertTrue(message.hasBazString());
 
-    Assert.assertEquals(100, message.getFooLazyMessage().getQuxInt());
+    Assert.assertEquals(100, message.getFooLazyMessage().getMooInt());
     Assert.assertEquals("101", message.getBarString());
     Assert.assertEquals(102, message.getBazInt());
     Assert.assertEquals("103", message.getBazString());
diff --git a/java/kotlin-lite/pom.xml b/java/kotlin-lite/pom.xml
index 6ccb100..11a0102 100644
--- a/java/kotlin-lite/pom.xml
+++ b/java/kotlin-lite/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>com.google.protobuf</groupId>
     <artifactId>protobuf-parent</artifactId>
-    <version>3.20.0-rc-1</version>
+    <version>3.20.0</version>
   </parent>
 
   <artifactId>protobuf-kotlin-lite</artifactId>
diff --git a/java/kotlin/pom.xml b/java/kotlin/pom.xml
index 2ae5b68..fe9e0e5 100644
--- a/java/kotlin/pom.xml
+++ b/java/kotlin/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>com.google.protobuf</groupId>
     <artifactId>protobuf-parent</artifactId>
-    <version>3.20.0-rc-1</version>
+    <version>3.20.0</version>
   </parent>
 
   <artifactId>protobuf-kotlin</artifactId>
diff --git a/java/lite.md b/java/lite.md
index a71d48e..6fab856 100644
--- a/java/lite.md
+++ b/java/lite.md
@@ -29,7 +29,7 @@
 <dependency>
   <groupId>com.google.protobuf</groupId>
   <artifactId>protobuf-javalite</artifactId>
-  <version>3.20.0-rc-1</version>
+  <version>3.20.0</version>
 </dependency>
 ```
 
diff --git a/java/lite/pom.xml b/java/lite/pom.xml
index 35d8ac2..c2f7e37 100644
--- a/java/lite/pom.xml
+++ b/java/lite/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>com.google.protobuf</groupId>
     <artifactId>protobuf-parent</artifactId>
-    <version>3.20.0-rc-1</version>
+    <version>3.20.0</version>
   </parent>
 
   <artifactId>protobuf-javalite</artifactId>
diff --git a/java/pom.xml b/java/pom.xml
index 4869c94..82f31fb 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -4,7 +4,7 @@
 
   <groupId>com.google.protobuf</groupId>
   <artifactId>protobuf-parent</artifactId>
-  <version>3.20.0-rc-1</version>
+  <version>3.20.0</version>
   <packaging>pom</packaging>
 
   <name>Protocol Buffers [Parent]</name>
diff --git a/java/util/pom.xml b/java/util/pom.xml
index 34fcbb3..d9eadd6 100644
--- a/java/util/pom.xml
+++ b/java/util/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>com.google.protobuf</groupId>
     <artifactId>protobuf-parent</artifactId>
-    <version>3.20.0-rc-1</version>
+    <version>3.20.0</version>
   </parent>
 
   <artifactId>protobuf-java-util</artifactId>
diff --git a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
index 854c826..096acee 100644
--- a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
+++ b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
@@ -142,7 +142,7 @@
    *   <li>If all children of a node have been removed, the node itself will be removed as well.
    *       That is, if "foo" only has one child "bar" and "foo.bar" only has one child "baz",
    *       removing "foo.bar.barz" would remove both "foo" and "foo.bar". If "foo" has both "bar"
-   *       and "qux" as children, removing "foo.bar" would leave the path "foo.qux" intact.
+   *       and "moo" as children, removing "foo.bar" would leave the path "foo.moo" intact.
    *   <li>If the field path to remove is a non-exist sub-path, nothing will be changed.
    * </ul>
    */
diff --git a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
index 7ce6f55..6bed2e3 100644
--- a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
+++ b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
@@ -1718,7 +1718,8 @@
         FieldDescriptor field, JsonElement json, Message.Builder builder)
         throws InvalidProtocolBufferException {
       if (!(json instanceof JsonArray)) {
-        throw new InvalidProtocolBufferException("Expect an array but found: " + json);
+        throw new InvalidProtocolBufferException(
+            "Expected an array for " + field.getName() + " but found " + json);
       }
       JsonArray array = (JsonArray) json;
       for (int i = 0; i < array.size(); ++i) {
diff --git a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
index 2169690..c31031e 100644
--- a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
@@ -821,6 +821,23 @@
   }
 
   @Test
+  // https://github.com/protocolbuffers/protobuf/issues/7456
+  public void testArrayTypeMismatch() throws IOException {
+    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+    try {
+      mergeFromJson(
+          "{\n"
+              + "  \"repeated_int32\": 5\n"
+              + "}",
+          builder);
+      assertWithMessage("should have thrown exception for incorrect type").fail();
+    } catch (InvalidProtocolBufferException expected) {
+      assertThat(expected).hasMessageThat()
+          .isEqualTo("Expected an array for repeated_int32 but found 5");
+    }
+  }
+
+  @Test
   public void testParserAcceptNonQuotedObjectKey() throws Exception {
     TestMap.Builder builder = TestMap.newBuilder();
     mergeFromJson(
diff --git a/js/binary/reader_test.js b/js/binary/reader_test.js
index 7e46828..6963f77 100644
--- a/js/binary/reader_test.js
+++ b/js/binary/reader_test.js
@@ -57,8 +57,8 @@
   it('testInstanceCaches', /** @suppress {visibility} */ function() {
     var writer = new jspb.BinaryWriter();
     var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
-    writer.writeMessage(1, dummyMessage, goog.nullFunction);
-    writer.writeMessage(2, dummyMessage, goog.nullFunction);
+    writer.writeMessage(1, dummyMessage, () => {});
+    writer.writeMessage(2, dummyMessage, () => {});
 
     var buffer = writer.getResultBuffer();
 
@@ -139,7 +139,7 @@
     var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
     reader.nextField();
     assertThrows(function() {
-      reader.readMessage(dummyMessage, goog.nullFunction);
+      reader.readMessage(dummyMessage, () => {});
     });
 
     // Reading past the end of the stream should trigger an assertion.
@@ -651,7 +651,7 @@
     });
 
     // Add one empty message.
-    writer.writeMessage(6, dummyMessage, goog.nullFunction);
+    writer.writeMessage(6, dummyMessage, () => {});
 
     writer.writeInt32(7, 700);
 
diff --git a/js/binary/writer_test.js b/js/binary/writer_test.js
index 6c5f36e..543cff6 100644
--- a/js/binary/writer_test.js
+++ b/js/binary/writer_test.js
@@ -65,7 +65,7 @@
     var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
 
     assertFails(function() {
-      writer.writeMessage(-1, dummyMessage, goog.nullFunction);
+      writer.writeMessage(-1, dummyMessage, () => {});
     });
 
     // Writing invalid field indices should assert.
diff --git a/js/compatibility_tests/v3.0.0/binary/reader_test.js b/js/compatibility_tests/v3.0.0/binary/reader_test.js
index 9571138..285b0e5 100644
--- a/js/compatibility_tests/v3.0.0/binary/reader_test.js
+++ b/js/compatibility_tests/v3.0.0/binary/reader_test.js
@@ -56,8 +56,8 @@
   it('testInstanceCaches', /** @suppress {visibility} */ function() {
     var writer = new jspb.BinaryWriter();
     var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
-    writer.writeMessage(1, dummyMessage, goog.nullFunction);
-    writer.writeMessage(2, dummyMessage, goog.nullFunction);
+    writer.writeMessage(1, dummyMessage, () => {});
+    writer.writeMessage(2, dummyMessage, () => {});
 
     var buffer = writer.getResultBuffer();
 
@@ -138,7 +138,7 @@
     var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
     reader.nextField();
     assertThrows(function() {
-      reader.readMessage(dummyMessage, goog.nullFunction);
+      reader.readMessage(dummyMessage, () => {});
     });
 
     // Reading past the end of the stream should trigger an assertion.
@@ -596,7 +596,7 @@
     });
 
     // Add one empty message.
-    writer.writeMessage(6, dummyMessage, goog.nullFunction);
+    writer.writeMessage(6, dummyMessage, () => {});
 
     writer.writeInt32(7, 700);
 
diff --git a/js/compatibility_tests/v3.0.0/binary/writer_test.js b/js/compatibility_tests/v3.0.0/binary/writer_test.js
index d5dadb4..f896c3b 100644
--- a/js/compatibility_tests/v3.0.0/binary/writer_test.js
+++ b/js/compatibility_tests/v3.0.0/binary/writer_test.js
@@ -62,7 +62,7 @@
     var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
 
     assertFails(function() {
-      writer.writeMessage(-1, dummyMessage, goog.nullFunction);
+      writer.writeMessage(-1, dummyMessage, () => {});
     });
 
     // Writing invalid field indices should assert.
diff --git a/js/compatibility_tests/v3.1.0/binary/reader_test.js b/js/compatibility_tests/v3.1.0/binary/reader_test.js
index d0062de..3b05176 100644
--- a/js/compatibility_tests/v3.1.0/binary/reader_test.js
+++ b/js/compatibility_tests/v3.1.0/binary/reader_test.js
@@ -56,8 +56,8 @@
   it('testInstanceCaches', /** @suppress {visibility} */ function() {
     var writer = new jspb.BinaryWriter();
     var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
-    writer.writeMessage(1, dummyMessage, goog.nullFunction);
-    writer.writeMessage(2, dummyMessage, goog.nullFunction);
+    writer.writeMessage(1, dummyMessage, () => {});
+    writer.writeMessage(2, dummyMessage, () => {});
 
     var buffer = writer.getResultBuffer();
 
@@ -138,7 +138,7 @@
     var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
     reader.nextField();
     assertThrows(function() {
-      reader.readMessage(dummyMessage, goog.nullFunction);
+      reader.readMessage(dummyMessage, () => {});
     });
 
     // Reading past the end of the stream should trigger an assertion.
@@ -620,7 +620,7 @@
     });
 
     // Add one empty message.
-    writer.writeMessage(6, dummyMessage, goog.nullFunction);
+    writer.writeMessage(6, dummyMessage, () => {});
 
     writer.writeInt32(7, 700);
 
diff --git a/js/compatibility_tests/v3.1.0/binary/writer_test.js b/js/compatibility_tests/v3.1.0/binary/writer_test.js
index 55bc6be..5779cc5 100644
--- a/js/compatibility_tests/v3.1.0/binary/writer_test.js
+++ b/js/compatibility_tests/v3.1.0/binary/writer_test.js
@@ -62,7 +62,7 @@
     var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({});
 
     assertFails(function() {
-      writer.writeMessage(-1, dummyMessage, goog.nullFunction);
+      writer.writeMessage(-1, dummyMessage, () => {});
     });
 
     // Writing invalid field indices should assert.
diff --git a/kokoro/caplog.sh b/kokoro/caplog.sh
new file mode 100644
index 0000000..fe81949
--- /dev/null
+++ b/kokoro/caplog.sh
@@ -0,0 +1,92 @@
+# Log capturing for the Kokoro runtime environment.
+#
+# This script should be `source`d from Kokoro build scripts to configure log
+# capturing when running under Kokoro.
+#
+# When not running under Kokoro, no logs will be collected. If you want to run
+# locally and collect logs anyway, set the KOKORO_ARTIFACTS_DIR environment
+# variable to a directory where the logs should go.
+#
+# The job `.cfg` file needs the following stanzas to declare the captured logs
+# as outputs (yes, these are globs, not regexes):
+#
+#   action: {
+#     define_artifacts: {
+#       regex: "**/*sponge_log.log"
+#       regex: "**/*sponge_log.xml"
+#     }
+#   }
+#
+# Use the provided functions below as build/test fixtures, e.g.:
+#
+#   source kokoro/capture_logs.sh
+#   caplog build/step1 <build command>
+#   caplog tests/step2 <test command>
+#
+# If log capturing is enabled, this script will set some variables that can be
+# used if necessary:
+#
+#   CAPLOG_DIR         is used for logs
+#   CAPLOG_CMAKE_ARGS  contains extra cmake args to enable test XML output
+#   CAPLOG_CTEST_ARGS  contains extra ctest args to capture combined test logs
+#
+# For example:
+#
+#   if [[ -v CAPLOG_DIR_BUILD ]]; then
+#     cp extra_diagnostics.log "${CAPLOG_DIR_BUILD}/diagnostics.log"
+#   fi
+#
+#   # Use ${...:-} form under `set -u`:
+#   caplog build/01_configure cmake -G Ninja ${CAPLOG_CMAKE_ARGS:-}
+#   caplog build/02_build     cmake --build
+#   caplog test/03_test       ctest ${CAPLOG_CTEST_ARGS:-}
+
+if [[ -z ${KOKORO_ARTIFACTS_DIR:-} ]]; then
+  function caplog() { shift; "$@"; }
+else
+
+  CAPLOG_DIR="$(mktemp -d)"
+  CAPLOG_CMAKE_ARGS="-Dprotobuf_TEST_XML_OUTDIR=${CAPLOG_DIR}/tests/"
+  CAPLOG_CTEST_ARGS="--verbose"
+
+  # Captures the stdout/stderr of a command to a named log file.
+  # Usage: caplog NAME COMMAND [ARGS...]
+  function caplog() {
+    _name="${CAPLOG_DIR}/${1%.log}.log"; shift
+    mkdir -p "${_name%/*}"
+    date
+    time ( "$@" 2>&1 | tee "${_name}" )
+    if [[ $? != 0 ]] ; then
+      cat "${_name}"
+      return 1
+    fi
+  }
+
+  # Trap handler: renames logs on script exit so they will be found by Kokoro.
+  function _caplog_onexit() {
+    _rc=$?
+    set +x
+    echo "Collecting logs [${BASH_SOURCE}]"
+
+    find "${CAPLOG_DIR}" -type f -name '*.log' \
+      | while read _textlog; do
+      # Ensure an XML file exists for each .log file.
+      touch ${_textlog%.log}.xml
+    done
+
+    find "${CAPLOG_DIR}" -type f \( -name '*.xml' -or -name '*.log' \) \
+      | while read _src; do
+      # Move to artifacts dir, preserving the path relative to CAPLOG_DIR.
+      # The filename changes from foo/bar.log to foo/bar/sponge_log.log.
+      _logfile=${_src/${CAPLOG_DIR}\//}
+      _stem=${KOKORO_ARTIFACTS_DIR}/${_logfile%.*}
+      _ext=${_logfile##*.}
+      mkdir -p ${_stem}
+      mv -v "${_src}" "${_stem}/sponge_log.${_ext}"
+    done
+    rm -rv "${CAPLOG_DIR}"
+    exit ${_rc}
+  }
+  trap _caplog_onexit EXIT
+
+fi
diff --git a/kokoro/linux/dockerfile/test/ruby/Dockerfile b/kokoro/linux/dockerfile/test/ruby/Dockerfile
index 914cd4b..0e34fe5 100644
--- a/kokoro/linux/dockerfile/test/ruby/Dockerfile
+++ b/kokoro/linux/dockerfile/test/ruby/Dockerfile
@@ -38,6 +38,7 @@
 RUN /bin/bash -l -c "rvm install 3.1.0"
 RUN /bin/bash -l -c "rvm install jruby-9.2.20.1"
 RUN /bin/bash -l -c "rvm install jruby-9.3.3.0"
+RUN /bin/bash -l -c "rvm install jruby-9.3.4.0"
 
 RUN /bin/bash -l -c "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
 RUN /bin/bash -l -c "echo 'export PATH=/usr/local/rvm/bin:$PATH' >> ~/.bashrc"
diff --git a/kokoro/macos-next/README.md b/kokoro/macos-next/README.md
new file mode 100644
index 0000000..4657855
--- /dev/null
+++ b/kokoro/macos-next/README.md
@@ -0,0 +1,4 @@
+# macOS-next
+
+This builder is temporary for developing and testing builds using the "next"
+macOS version without affecting the ordinary builds.
diff --git a/kokoro/macos-next/cpp/build.sh b/kokoro/macos-next/cpp/build.sh
new file mode 100755
index 0000000..490e990
--- /dev/null
+++ b/kokoro/macos-next/cpp/build.sh
@@ -0,0 +1,55 @@
+#!/bin/bash -eux
+#
+# Build file to set up and run tests
+
+set -o pipefail
+
+if [[ -h /tmpfs ]] && [[ ${PWD} == /tmpfs/src ]]; then
+  # Workaround for internal Kokoro bug: b/227401944
+  cd /Volumes/BuildData/tmpfs/src
+fi
+
+# These vars can be changed when running manually, e.g.:
+#
+#   % BUILD_CONFIG=RelWithDebInfo path/to/build.sh
+
+# By default, build using Debug config.
+: ${BUILD_CONFIG:=Debug}
+
+# By default, find the sources based on this script path.
+: ${SOURCE_DIR:=$(cd $(dirname $0)/../../..; pwd)}
+
+# By default, put outputs under <git root>/cmake/build.
+: ${BUILD_DIR:=${SOURCE_DIR}/cmake/build}
+
+source ${SOURCE_DIR}/kokoro/caplog.sh
+
+#
+# Update submodules
+#
+git -C "${SOURCE_DIR}" submodule update --init --recursive
+
+#
+# Configure and build in a separate directory
+#
+mkdir -p "${BUILD_DIR}"
+
+caplog 01_configure \
+  cmake -S "${SOURCE_DIR}" -B "${BUILD_DIR}" ${CAPLOG_CMAKE_ARGS:-}
+
+if [[ -n ${CAPLOG_DIR:-} ]]; then
+  mkdir -p "${CAPLOG_DIR}/CMakeFiles"
+  cp "${BUILD_DIR}"/CMakeFiles/CMake*.log "${CAPLOG_DIR}/CMakeFiles"
+fi
+
+caplog 02_build \
+  cmake --build "${BUILD_DIR}" --config "${BUILD_CONFIG}"
+
+#
+# Run tests
+#
+(
+  cd "${BUILD_DIR}"
+  caplog 03_combined_testlog \
+    ctest -C "${BUILD_CONFIG}" -j4 ${CAPLOG_CTEST_ARGS:-}
+)
diff --git a/kokoro/macos-next/cpp/continuous.cfg b/kokoro/macos-next/cpp/continuous.cfg
new file mode 100644
index 0000000..166caa5
--- /dev/null
+++ b/kokoro/macos-next/cpp/continuous.cfg
@@ -0,0 +1,13 @@
+# Config file for running tests in Kokoro
+
+# Location of the build script in repository
+build_file: "protobuf/kokoro/macos-next/cpp/build.sh"
+timeout_mins: 1440
+
+# Upload logs
+action: {
+  define_artifacts: {
+    regex: "**/*sponge_log.log"
+    regex: "**/*sponge_log.xml"
+  }
+}
diff --git a/kokoro/macos-next/cpp/presubmit.cfg b/kokoro/macos-next/cpp/presubmit.cfg
new file mode 100644
index 0000000..166caa5
--- /dev/null
+++ b/kokoro/macos-next/cpp/presubmit.cfg
@@ -0,0 +1,13 @@
+# Config file for running tests in Kokoro
+
+# Location of the build script in repository
+build_file: "protobuf/kokoro/macos-next/cpp/build.sh"
+timeout_mins: 1440
+
+# Upload logs
+action: {
+  define_artifacts: {
+    regex: "**/*sponge_log.log"
+    regex: "**/*sponge_log.xml"
+  }
+}
diff --git a/maven_install.json b/maven_install.json
index 808e013..9c5860a 100644
--- a/maven_install.json
+++ b/maven_install.json
@@ -1,10 +1,10 @@
 {
     "dependency_tree": {
         "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL",
-        "__INPUT_ARTIFACTS_HASH": -1867950668,
-        "__RESOLVED_ARTIFACTS_HASH": 1254982283,
+        "__INPUT_ARTIFACTS_HASH": -228414992,
+        "__RESOLVED_ARTIFACTS_HASH": -722345565,
         "conflict_resolution": {
-            "com.google.errorprone:error_prone_annotations:2.3.2": "com.google.errorprone:error_prone_annotations:2.5.1",
+            "com.google.errorprone:error_prone_annotations:2.3.2": "com.google.errorprone:error_prone_annotations:2.11.0",
             "junit:junit:4.12": "junit:junit:4.13.2"
         },
         "dependencies": [
@@ -45,16 +45,16 @@
                 "url": "https://repo1.maven.org/maven2/com/google/code/gson/gson/2.8.9/gson-2.8.9.jar"
             },
             {
-                "coord": "com.google.errorprone:error_prone_annotations:2.5.1",
+                "coord": "com.google.errorprone:error_prone_annotations:2.11.0",
                 "dependencies": [],
                 "directDependencies": [],
-                "file": "v1/https/repo1.maven.org/maven2/com/google/errorprone/error_prone_annotations/2.5.1/error_prone_annotations-2.5.1.jar",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/errorprone/error_prone_annotations/2.11.0/error_prone_annotations-2.11.0.jar",
                 "mirror_urls": [
-                    "https://repo1.maven.org/maven2/com/google/errorprone/error_prone_annotations/2.5.1/error_prone_annotations-2.5.1.jar",
-                    "https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.5.1/error_prone_annotations-2.5.1.jar"
+                    "https://repo1.maven.org/maven2/com/google/errorprone/error_prone_annotations/2.11.0/error_prone_annotations-2.11.0.jar",
+                    "https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.11.0/error_prone_annotations-2.11.0.jar"
                 ],
-                "sha256": "ff80626baaf12a09342befd4e84cba9d50662f5fcd7f7a9b3490a6b7cf87e66c",
-                "url": "https://repo1.maven.org/maven2/com/google/errorprone/error_prone_annotations/2.5.1/error_prone_annotations-2.5.1.jar"
+                "sha256": "721cb91842b46fa056847d104d5225c8b8e1e8b62263b993051e1e5a0137b7ec",
+                "url": "https://repo1.maven.org/maven2/com/google/errorprone/error_prone_annotations/2.11.0/error_prone_annotations-2.11.0.jar"
             },
             {
                 "coord": "com.google.guava:failureaccess:1.0.1",
@@ -69,59 +69,59 @@
                 "url": "https://repo1.maven.org/maven2/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar"
             },
             {
-                "coord": "com.google.guava:guava-testlib:30.1.1-jre",
+                "coord": "com.google.guava:guava-testlib:31.1-jre",
                 "dependencies": [
                     "com.google.code.findbugs:jsr305:3.0.2",
-                    "com.google.errorprone:error_prone_annotations:2.5.1",
+                    "com.google.errorprone:error_prone_annotations:2.11.0",
                     "com.google.guava:failureaccess:1.0.1",
-                    "com.google.guava:guava:30.1.1-jre",
+                    "com.google.guava:guava:31.1-jre",
                     "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
                     "com.google.j2objc:j2objc-annotations:1.3",
                     "junit:junit:4.13.2",
-                    "org.checkerframework:checker-qual:3.9.1",
+                    "org.checkerframework:checker-qual:3.12.0",
                     "org.hamcrest:hamcrest-core:1.3"
                 ],
                 "directDependencies": [
                     "com.google.code.findbugs:jsr305:3.0.2",
-                    "com.google.errorprone:error_prone_annotations:2.5.1",
-                    "com.google.guava:guava:30.1.1-jre",
+                    "com.google.errorprone:error_prone_annotations:2.11.0",
+                    "com.google.guava:guava:31.1-jre",
                     "com.google.j2objc:j2objc-annotations:1.3",
                     "junit:junit:4.13.2",
-                    "org.checkerframework:checker-qual:3.9.1"
+                    "org.checkerframework:checker-qual:3.12.0"
                 ],
-                "file": "v1/https/repo1.maven.org/maven2/com/google/guava/guava-testlib/30.1.1-jre/guava-testlib-30.1.1-jre.jar",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/guava/guava-testlib/31.1-jre/guava-testlib-31.1-jre.jar",
                 "mirror_urls": [
-                    "https://repo1.maven.org/maven2/com/google/guava/guava-testlib/30.1.1-jre/guava-testlib-30.1.1-jre.jar",
-                    "https://repo.maven.apache.org/maven2/com/google/guava/guava-testlib/30.1.1-jre/guava-testlib-30.1.1-jre.jar"
+                    "https://repo1.maven.org/maven2/com/google/guava/guava-testlib/31.1-jre/guava-testlib-31.1-jre.jar",
+                    "https://repo.maven.apache.org/maven2/com/google/guava/guava-testlib/31.1-jre/guava-testlib-31.1-jre.jar"
                 ],
-                "sha256": "8a7fc9adfa1e7441d1d30ca288c593ebc7c4a24c601d01169b781c398f24099b",
-                "url": "https://repo1.maven.org/maven2/com/google/guava/guava-testlib/30.1.1-jre/guava-testlib-30.1.1-jre.jar"
+                "sha256": "aadc71b10d5c3ac474dd16be84cfb18d257e584d1e0a59f8cab64ef4376226ce",
+                "url": "https://repo1.maven.org/maven2/com/google/guava/guava-testlib/31.1-jre/guava-testlib-31.1-jre.jar"
             },
             {
-                "coord": "com.google.guava:guava:30.1.1-jre",
+                "coord": "com.google.guava:guava:31.1-jre",
                 "dependencies": [
                     "com.google.code.findbugs:jsr305:3.0.2",
-                    "com.google.errorprone:error_prone_annotations:2.5.1",
+                    "com.google.errorprone:error_prone_annotations:2.11.0",
                     "com.google.guava:failureaccess:1.0.1",
                     "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
                     "com.google.j2objc:j2objc-annotations:1.3",
-                    "org.checkerframework:checker-qual:3.9.1"
+                    "org.checkerframework:checker-qual:3.12.0"
                 ],
                 "directDependencies": [
                     "com.google.code.findbugs:jsr305:3.0.2",
-                    "com.google.errorprone:error_prone_annotations:2.5.1",
+                    "com.google.errorprone:error_prone_annotations:2.11.0",
                     "com.google.guava:failureaccess:1.0.1",
                     "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
                     "com.google.j2objc:j2objc-annotations:1.3",
-                    "org.checkerframework:checker-qual:3.9.1"
+                    "org.checkerframework:checker-qual:3.12.0"
                 ],
-                "file": "v1/https/repo1.maven.org/maven2/com/google/guava/guava/30.1.1-jre/guava-30.1.1-jre.jar",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/guava/guava/31.1-jre/guava-31.1-jre.jar",
                 "mirror_urls": [
-                    "https://repo1.maven.org/maven2/com/google/guava/guava/30.1.1-jre/guava-30.1.1-jre.jar",
-                    "https://repo.maven.apache.org/maven2/com/google/guava/guava/30.1.1-jre/guava-30.1.1-jre.jar"
+                    "https://repo1.maven.org/maven2/com/google/guava/guava/31.1-jre/guava-31.1-jre.jar",
+                    "https://repo.maven.apache.org/maven2/com/google/guava/guava/31.1-jre/guava-31.1-jre.jar"
                 ],
-                "sha256": "44ce229ce26d880bf3afc362bbfcec34d7e6903d195bbb1db9f3b6e0d9834f06",
-                "url": "https://repo1.maven.org/maven2/com/google/guava/guava/30.1.1-jre/guava-30.1.1-jre.jar"
+                "sha256": "a42edc9cab792e39fe39bb94f3fca655ed157ff87a8af78e1d6ba5b07c4a00ab",
+                "url": "https://repo1.maven.org/maven2/com/google/guava/guava/31.1-jre/guava-31.1-jre.jar"
             },
             {
                 "coord": "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
@@ -151,18 +151,18 @@
                 "coord": "com.google.truth:truth:1.1.2",
                 "dependencies": [
                     "com.google.auto.value:auto-value-annotations:1.7.4",
-                    "com.google.errorprone:error_prone_annotations:2.5.1",
-                    "com.google.guava:guava:30.1.1-jre",
+                    "com.google.errorprone:error_prone_annotations:2.11.0",
+                    "com.google.guava:guava:31.1-jre",
                     "junit:junit:4.13.2",
-                    "org.checkerframework:checker-qual:3.9.1",
+                    "org.checkerframework:checker-qual:3.12.0",
                     "org.ow2.asm:asm:9.0"
                 ],
                 "directDependencies": [
                     "com.google.auto.value:auto-value-annotations:1.7.4",
-                    "com.google.errorprone:error_prone_annotations:2.5.1",
-                    "com.google.guava:guava:30.1.1-jre",
+                    "com.google.errorprone:error_prone_annotations:2.11.0",
+                    "com.google.guava:guava:31.1-jre",
                     "junit:junit:4.13.2",
-                    "org.checkerframework:checker-qual:3.9.1",
+                    "org.checkerframework:checker-qual:3.12.0",
                     "org.ow2.asm:asm:9.0"
                 ],
                 "file": "v1/https/repo1.maven.org/maven2/com/google/truth/truth/1.1.2/truth-1.1.2.jar",
@@ -214,16 +214,16 @@
                 "url": "https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.12.7/byte-buddy-1.12.7.jar"
             },
             {
-                "coord": "org.checkerframework:checker-qual:3.9.1",
+                "coord": "org.checkerframework:checker-qual:3.12.0",
                 "dependencies": [],
                 "directDependencies": [],
-                "file": "v1/https/repo1.maven.org/maven2/org/checkerframework/checker-qual/3.9.1/checker-qual-3.9.1.jar",
+                "file": "v1/https/repo1.maven.org/maven2/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.jar",
                 "mirror_urls": [
-                    "https://repo1.maven.org/maven2/org/checkerframework/checker-qual/3.9.1/checker-qual-3.9.1.jar",
-                    "https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/3.9.1/checker-qual-3.9.1.jar"
+                    "https://repo1.maven.org/maven2/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.jar",
+                    "https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.jar"
                 ],
-                "sha256": "ab0468b1ba35bb2ae45f61a60dc4960bd887660ab8f05113a662a7e675eae776",
-                "url": "https://repo1.maven.org/maven2/org/checkerframework/checker-qual/3.9.1/checker-qual-3.9.1.jar"
+                "sha256": "ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb",
+                "url": "https://repo1.maven.org/maven2/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.jar"
             },
             {
                 "coord": "org.hamcrest:hamcrest-core:1.3",
diff --git a/php/ext/google/protobuf/package.xml b/php/ext/google/protobuf/package.xml
index 44c4815..b529dc9 100644
--- a/php/ext/google/protobuf/package.xml
+++ b/php/ext/google/protobuf/package.xml
@@ -10,15 +10,15 @@
   <email>protobuf-opensource@google.com</email>
   <active>yes</active>
  </lead>
- <date>2022-03-04</date>
- <time>16:28:14</time>
+ <date>2022-03-25</date>
+ <time>19:17:44</time>
  <version>
-  <release>3.20.0RC1</release>
+  <release>3.20.0</release>
   <api>3.20.0</api>
  </version>
  <stability>
-  <release>beta</release>
-  <api>beta</api>
+  <release>stable</release>
+  <api>stable</api>
  </stability>
  <license uri="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</license>
  <notes>
@@ -46,10 +46,12 @@
     <file baseinstalldir="/" name="protobuf.c" role="src"/>
     <file baseinstalldir="/" name="protobuf.h" role="src"/>
     <file baseinstalldir="/" name="wkt.inc" role="src"/>
-    <file baseinstalldir="/" name="third_party/utf8_range/naive.c" role="doc"/>
-    <file baseinstalldir="/" name="third_party/utf8_range/range2-neon.c" role="doc"/>
-    <file baseinstalldir="/" name="third_party/utf8_range/range2-sse.c" role="doc"/>
     <file baseinstalldir="/" name="LICENSE" role="doc"/>
+    <file baseinstalldir="/" name="third_party/utf8_range/naive.c" role="src"/>
+    <file baseinstalldir="/" name="third_party/utf8_range/range2-neon.c" role="src"/>
+    <file baseinstalldir="/" name="third_party/utf8_range/range2-sse.c" role="src"/>
+    <file baseinstalldir="/" name="third_party/utf8_range/utf8_range.h" role="src"/>
+    <file baseinstalldir="/" name="third_party/utf8_range/LICENSE" role="doc"/>
   </dir>
  </contents>
  <dependencies>
@@ -1221,5 +1223,35 @@
    <notes>
    </notes>
   </release>
+  <release>
+   <version>
+    <release>3.20.0RC2</release>
+    <api>3.20.0</api>
+   </version>
+   <stability>
+    <release>beta</release>
+    <api>beta</api>
+   </stability>
+   <date>2022-03-15</date>
+   <time>21:10:15</time>
+   <license uri="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</license>
+   <notes>
+   </notes>
+  </release>
+  <release>
+   <version>
+    <release>3.20.0</release>
+    <api>3.20.0</api>
+   </version>
+   <stability>
+    <release>stable</release>
+    <api>stable</api>
+   </stability>
+   <date>2022-03-25</date>
+   <time>19:17:44</time>
+   <license uri="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</license>
+   <notes>
+   </notes>
+  </release>
  </changelog>
 </package>
diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h
index 47e2494..01be62a 100644
--- a/php/ext/google/protobuf/protobuf.h
+++ b/php/ext/google/protobuf/protobuf.h
@@ -127,7 +127,7 @@
   ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-#define PHP_PROTOBUF_VERSION "3.20.0RC1"
+#define PHP_PROTOBUF_VERSION "3.20.0"
 
 // ptr -> PHP object cache. This is a weak map that caches lazily-created
 // wrapper objects around upb types:
diff --git a/php/release.sh b/php/release.sh
index 6b0baac..9a4b139 100755
--- a/php/release.sh
+++ b/php/release.sh
@@ -30,7 +30,11 @@
 sed -i 's|php/src|src|g' composer.json
 git add .
 git commit -m "$VERSION"
-git tag "$VERSION"
+if [ $(git tag -l "$VERSION") ]; then
+  echo "tag $VERSION already exists"
+else
+  git tag "$VERSION"
+fi
 popd
 
 # Clean up
diff --git a/php/src/Google/Protobuf/Internal/SourceCodeInfo/Location.php b/php/src/Google/Protobuf/Internal/SourceCodeInfo/Location.php
index a3e8e72..479c8b2 100644
--- a/php/src/Google/Protobuf/Internal/SourceCodeInfo/Location.php
+++ b/php/src/Google/Protobuf/Internal/SourceCodeInfo/Location.php
@@ -72,12 +72,12 @@
      *   optional string baz = 3;
      *   // Comment attached to baz.
      *   // Another line attached to baz.
-     *   // Comment attached to qux.
+     *   // Comment attached to moo.
      *   //
-     *   // Another line attached to qux.
-     *   optional double qux = 4;
+     *   // Another line attached to moo.
+     *   optional double moo = 4;
      *   // Detached comment for corge. This is not leading or trailing comments
-     *   // to qux or corge because there are blank lines separating it from
+     *   // to moo or corge because there are blank lines separating it from
      *   // both.
      *   // Detached comment for corge paragraph 2.
      *   optional string corge = 5;
@@ -156,12 +156,12 @@
      *             optional string baz = 3;
      *             // Comment attached to baz.
      *             // Another line attached to baz.
-     *             // Comment attached to qux.
+     *             // Comment attached to moo.
      *             //
-     *             // Another line attached to qux.
-     *             optional double qux = 4;
+     *             // Another line attached to moo.
+     *             optional double moo = 4;
      *             // Detached comment for corge. This is not leading or trailing comments
-     *             // to qux or corge because there are blank lines separating it from
+     *             // to moo or corge because there are blank lines separating it from
      *             // both.
      *             // Detached comment for corge paragraph 2.
      *             optional string corge = 5;
@@ -302,12 +302,12 @@
      *   optional string baz = 3;
      *   // Comment attached to baz.
      *   // Another line attached to baz.
-     *   // Comment attached to qux.
+     *   // Comment attached to moo.
      *   //
-     *   // Another line attached to qux.
-     *   optional double qux = 4;
+     *   // Another line attached to moo.
+     *   optional double moo = 4;
      *   // Detached comment for corge. This is not leading or trailing comments
-     *   // to qux or corge because there are blank lines separating it from
+     *   // to moo or corge because there are blank lines separating it from
      *   // both.
      *   // Detached comment for corge paragraph 2.
      *   optional string corge = 5;
@@ -358,12 +358,12 @@
      *   optional string baz = 3;
      *   // Comment attached to baz.
      *   // Another line attached to baz.
-     *   // Comment attached to qux.
+     *   // Comment attached to moo.
      *   //
-     *   // Another line attached to qux.
-     *   optional double qux = 4;
+     *   // Another line attached to moo.
+     *   optional double moo = 4;
      *   // Detached comment for corge. This is not leading or trailing comments
-     *   // to qux or corge because there are blank lines separating it from
+     *   // to moo or corge because there are blank lines separating it from
      *   // both.
      *   // Detached comment for corge paragraph 2.
      *   optional string corge = 5;
diff --git a/php/src/Google/Protobuf/Internal/UninterpretedOption/NamePart.php b/php/src/Google/Protobuf/Internal/UninterpretedOption/NamePart.php
index 6212d1e..2debf83 100644
--- a/php/src/Google/Protobuf/Internal/UninterpretedOption/NamePart.php
+++ b/php/src/Google/Protobuf/Internal/UninterpretedOption/NamePart.php
@@ -14,8 +14,8 @@
  * The name of the uninterpreted option.  Each string represents a segment in
  * a dot-separated name.  is_extension is true iff a segment represents an
  * extension (denoted with parentheses in options specs in .proto files).
- * E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
- * "foo.(bar.baz).qux".
+ * E.g.,{ ["foo", false], ["bar.baz", true], ["moo", false] } represents
+ * "foo.(bar.baz).moo".
  *
  * Generated from protobuf message <code>google.protobuf.UninterpretedOption.NamePart</code>
  */
diff --git a/protobuf.bzl b/protobuf.bzl
index ffd7c8d..8e173fd 100644
--- a/protobuf.bzl
+++ b/protobuf.bzl
@@ -266,9 +266,9 @@
         deps = [],
         cc_libs = [],
         include = None,
-        protoc = Label("//:protoc"),
+        protoc = "@com_google_protobuf//:protoc",
         use_grpc_plugin = False,
-        default_runtime = Label("//:protobuf"),
+        default_runtime = "@com_google_protobuf//:protobuf",
         **kargs):
     """Bazel rule to create a C++ protobuf library from proto source files
 
@@ -386,7 +386,7 @@
         "_protoc": attr.label(
             executable = True,
             cfg = "exec",
-            default = "//:protoc",
+            default = "@com_google_protobuf//:protoc",
         ),
     },
 )
@@ -493,8 +493,8 @@
         py_libs = [],
         py_extra_srcs = [],
         include = None,
-        default_runtime = Label("//:protobuf_python"),
-        protoc = Label("//:protoc"),
+        default_runtime = "@com_google_protobuf//:protobuf_python",
+        protoc = "@com_google_protobuf//:protoc",
         use_grpc_plugin = False,
         **kargs):
     """Bazel rule to create a Python protobuf library from proto source files
diff --git a/protobuf_deps.bzl b/protobuf_deps.bzl
index de81ae8..601e6c0 100644
--- a/protobuf_deps.bzl
+++ b/protobuf_deps.bzl
@@ -7,8 +7,8 @@
     "com.google.code.gson:gson:2.8.9",
     "com.google.errorprone:error_prone_annotations:2.3.2",
     "com.google.j2objc:j2objc-annotations:1.3",
-    "com.google.guava:guava:30.1.1-jre",
-    "com.google.guava:guava-testlib:30.1.1-jre",
+    "com.google.guava:guava:31.1-jre",
+    "com.google.guava:guava-testlib:31.1-jre",
     "com.google.truth:truth:1.1.2",
     "junit:junit:4.13.2",
     "org.mockito:mockito-core:4.3.1",
@@ -30,7 +30,7 @@
     if not native.existing_rule("zlib"):
         http_archive(
             name = "zlib",
-            build_file = Label("//:third_party/zlib.BUILD"),
+            build_file = "@com_google_protobuf//:third_party/zlib.BUILD",
             sha256 = "629380c90a77b964d896ed37163f5c3a34f6e6d897311f1df2a7016355c45eff",
             strip_prefix = "zlib-1.2.11",
             urls = ["https://github.com/madler/zlib/archive/v1.2.11.tar.gz"],
diff --git a/protobuf_version.bzl b/protobuf_version.bzl
index b2e158d..a62513f 100644
--- a/protobuf_version.bzl
+++ b/protobuf_version.bzl
@@ -1 +1 @@
-PROTOBUF_VERSION = '3.20.0-rc-1'
+PROTOBUF_VERSION = '3.20.0'
diff --git a/protoc-artifacts/pom.xml b/protoc-artifacts/pom.xml
index 5df1af0..a667c52 100644
--- a/protoc-artifacts/pom.xml
+++ b/protoc-artifacts/pom.xml
@@ -8,7 +8,7 @@
   </parent>
   <groupId>com.google.protobuf</groupId>
   <artifactId>protoc</artifactId>
-  <version>3.20.0-rc-1</version>
+  <version>3.20.0</version>
   <packaging>pom</packaging>
   <name>Protobuf Compiler</name>
   <description>
diff --git a/python/google/protobuf/internal/descriptor_test.py b/python/google/protobuf/internal/descriptor_test.py
index d026a74..1d8286a 100644
--- a/python/google/protobuf/internal/descriptor_test.py
+++ b/python/google/protobuf/internal/descriptor_test.py
@@ -328,10 +328,10 @@
         unittest_custom_options_pb2.complex_opt1].foo)
     self.assertEqual(324, options.Extensions[
         unittest_custom_options_pb2.complex_opt1].Extensions[
-            unittest_custom_options_pb2.quux])
+            unittest_custom_options_pb2.mooo])
     self.assertEqual(876, options.Extensions[
         unittest_custom_options_pb2.complex_opt1].Extensions[
-            unittest_custom_options_pb2.corge].qux)
+            unittest_custom_options_pb2.corge].moo)
     self.assertEqual(987, options.Extensions[
         unittest_custom_options_pb2.complex_opt2].baz)
     self.assertEqual(654, options.Extensions[
@@ -341,28 +341,28 @@
         unittest_custom_options_pb2.complex_opt2].bar.foo)
     self.assertEqual(1999, options.Extensions[
         unittest_custom_options_pb2.complex_opt2].bar.Extensions[
-            unittest_custom_options_pb2.quux])
+            unittest_custom_options_pb2.mooo])
     self.assertEqual(2008, options.Extensions[
         unittest_custom_options_pb2.complex_opt2].bar.Extensions[
-            unittest_custom_options_pb2.corge].qux)
+            unittest_custom_options_pb2.corge].moo)
     self.assertEqual(741, options.Extensions[
         unittest_custom_options_pb2.complex_opt2].Extensions[
             unittest_custom_options_pb2.garply].foo)
     self.assertEqual(1998, options.Extensions[
         unittest_custom_options_pb2.complex_opt2].Extensions[
             unittest_custom_options_pb2.garply].Extensions[
-                unittest_custom_options_pb2.quux])
+                unittest_custom_options_pb2.mooo])
     self.assertEqual(2121, options.Extensions[
         unittest_custom_options_pb2.complex_opt2].Extensions[
             unittest_custom_options_pb2.garply].Extensions[
-                unittest_custom_options_pb2.corge].qux)
+                unittest_custom_options_pb2.corge].moo)
     self.assertEqual(1971, options.Extensions[
         unittest_custom_options_pb2.ComplexOptionType2
         .ComplexOptionType4.complex_opt4].waldo)
     self.assertEqual(321, options.Extensions[
         unittest_custom_options_pb2.complex_opt2].fred.waldo)
     self.assertEqual(9, options.Extensions[
-        unittest_custom_options_pb2.complex_opt3].qux)
+        unittest_custom_options_pb2.complex_opt3].moo)
     self.assertEqual(22, options.Extensions[
         unittest_custom_options_pb2.complex_opt3].complexoptiontype5.plugh)
     self.assertEqual(24, options.Extensions[
diff --git a/python/google/protobuf/internal/message_factory_test.py b/python/google/protobuf/internal/message_factory_test.py
index 97ef3aa..efba619 100644
--- a/python/google/protobuf/internal/message_factory_test.py
+++ b/python/google/protobuf/internal/message_factory_test.py
@@ -174,48 +174,42 @@
     factory = message_factory.MessageFactory(pool=pool)
 
     # Add Container message.
-    f = descriptor_pb2.FileDescriptorProto()
-    f.name = 'google/protobuf/internal/container.proto'
-    f.package = 'google.protobuf.python.internal'
-    msg = f.message_type.add()
-    msg.name = 'Container'
-    rng = msg.extension_range.add()
-    rng.start = 1
-    rng.end = 10
+    f = descriptor_pb2.FileDescriptorProto(
+        name='google/protobuf/internal/container.proto',
+        package='google.protobuf.python.internal')
+    f.message_type.add(name='Container').extension_range.add(start=1, end=10)
     pool.Add(f)
     msgs = factory.GetMessages([f.name])
     self.assertIn('google.protobuf.python.internal.Container', msgs)
 
     # Extend container.
-    f = descriptor_pb2.FileDescriptorProto()
-    f.name = 'google/protobuf/internal/extension.proto'
-    f.package = 'google.protobuf.python.internal'
-    f.dependency.append('google/protobuf/internal/container.proto')
-    msg = f.message_type.add()
-    msg.name = 'Extension'
-    ext = msg.extension.add()
-    ext.name = 'extension_field'
-    ext.number = 2
-    ext.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL
-    ext.type_name = 'Extension'
-    ext.extendee = 'Container'
+    f = descriptor_pb2.FileDescriptorProto(
+        name='google/protobuf/internal/extension.proto',
+        package='google.protobuf.python.internal',
+        dependency=['google/protobuf/internal/container.proto'])
+    msg = f.message_type.add(name='Extension')
+    msg.extension.add(
+        name='extension_field',
+        number=2,
+        label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL,
+        type_name='Extension',
+        extendee='Container')
     pool.Add(f)
     msgs = factory.GetMessages([f.name])
     self.assertIn('google.protobuf.python.internal.Extension', msgs)
 
     # Add Duplicate extending the same field number.
-    f = descriptor_pb2.FileDescriptorProto()
-    f.name = 'google/protobuf/internal/duplicate.proto'
-    f.package = 'google.protobuf.python.internal'
-    f.dependency.append('google/protobuf/internal/container.proto')
-    msg = f.message_type.add()
-    msg.name = 'Duplicate'
-    ext = msg.extension.add()
-    ext.name = 'extension_field'
-    ext.number = 2
-    ext.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL
-    ext.type_name = 'Duplicate'
-    ext.extendee = 'Container'
+    f = descriptor_pb2.FileDescriptorProto(
+        name='google/protobuf/internal/duplicate.proto',
+        package='google.protobuf.python.internal',
+        dependency=['google/protobuf/internal/container.proto'])
+    msg = f.message_type.add(name='Duplicate')
+    msg.extension.add(
+        name='extension_field',
+        number=2,
+        label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL,
+        type_name='Duplicate',
+        extendee='Container')
     pool.Add(f)
 
     with self.assertRaises(Exception) as cm:
@@ -230,6 +224,76 @@
                    ' with field number 2.',
                    'Double registration of Extensions'])
 
+  def testExtensionValueInDifferentFile(self):
+    # Add Container message.
+    f1 = descriptor_pb2.FileDescriptorProto(
+        name='google/protobuf/internal/container.proto',
+        package='google.protobuf.python.internal')
+    f1.message_type.add(name='Container').extension_range.add(start=1, end=10)
+
+    # Add ValueType message.
+    f2 = descriptor_pb2.FileDescriptorProto(
+        name='google/protobuf/internal/value_type.proto',
+        package='google.protobuf.python.internal')
+    f2.message_type.add(name='ValueType').field.add(
+        name='setting',
+        number=1,
+        label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL,
+        type=descriptor_pb2.FieldDescriptorProto.TYPE_INT32,
+        default_value='123')
+
+    # Extend container with field of ValueType.
+    f3 = descriptor_pb2.FileDescriptorProto(
+        name='google/protobuf/internal/extension.proto',
+        package='google.protobuf.python.internal',
+        dependency=[f1.name, f2.name])
+    f3.extension.add(
+        name='top_level_extension_field',
+        number=2,
+        label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL,
+        type_name='ValueType',
+        extendee='Container')
+    f3.message_type.add(name='Extension').extension.add(
+        name='nested_extension_field',
+        number=3,
+        label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL,
+        type_name='ValueType',
+        extendee='Container')
+
+    class SimpleDescriptorDB:
+
+      def __init__(self, files):
+        self._files = files
+
+      def FindFileByName(self, name):
+        return self._files[name]
+
+    db = SimpleDescriptorDB({f1.name: f1, f2.name: f2, f3.name: f3})
+
+    pool = descriptor_pool.DescriptorPool(db)
+    factory = message_factory.MessageFactory(pool=pool)
+    msgs = factory.GetMessages([f1.name, f3.name])  # Deliberately not f2.
+    msg = msgs['google.protobuf.python.internal.Container']
+    desc = msgs['google.protobuf.python.internal.Extension'].DESCRIPTOR
+    ext1 = desc.file.extensions_by_name['top_level_extension_field']
+    ext2 = desc.extensions_by_name['nested_extension_field']
+    m = msg()
+    m.Extensions[ext1].setting = 234
+    m.Extensions[ext2].setting = 345
+    serialized = m.SerializeToString()
+
+    pool = descriptor_pool.DescriptorPool(db)
+    factory = message_factory.MessageFactory(pool=pool)
+    msgs = factory.GetMessages([f1.name, f3.name])  # Deliberately not f2.
+    msg = msgs['google.protobuf.python.internal.Container']
+    desc = msgs['google.protobuf.python.internal.Extension'].DESCRIPTOR
+    ext1 = desc.file.extensions_by_name['top_level_extension_field']
+    ext2 = desc.extensions_by_name['nested_extension_field']
+    m = msg.FromString(serialized)
+    self.assertEqual(2, len(m.ListFields()))
+    self.assertEqual(234, m.Extensions[ext1].setting)
+    self.assertEqual(345, m.Extensions[ext2].setting)
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/python/google/protobuf/internal/python_message.py b/python/google/protobuf/internal/python_message.py
index 2921d5c..5550b42 100644
--- a/python/google/protobuf/internal/python_message.py
+++ b/python/google/protobuf/internal/python_message.py
@@ -1479,7 +1479,7 @@
 
   In order to support semantics like:
 
-    foo.bar.baz.qux = 23
+    foo.bar.baz.moo = 23
     assert foo.HasField('bar')
 
   ...child objects must have back references to their parents.
diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py
index 66dd0c7..a2872a2 100644
--- a/python/google/protobuf/internal/reflection_test.py
+++ b/python/google/protobuf/internal/reflection_test.py
@@ -1933,17 +1933,17 @@
 
   def testDisconnectingInOneof(self):
     m = unittest_pb2.TestOneof2()  # This message has two messages in a oneof.
-    m.foo_message.qux_int = 5
+    m.foo_message.moo_int = 5
     sub_message = m.foo_message
     # Accessing another message's field does not clear the first one
-    self.assertEqual(m.foo_lazy_message.qux_int, 0)
-    self.assertEqual(m.foo_message.qux_int, 5)
+    self.assertEqual(m.foo_lazy_message.moo_int, 0)
+    self.assertEqual(m.foo_message.moo_int, 5)
     # But mutating another message in the oneof detaches the first one.
-    m.foo_lazy_message.qux_int = 6
-    self.assertEqual(m.foo_message.qux_int, 0)
+    m.foo_lazy_message.moo_int = 6
+    self.assertEqual(m.foo_message.moo_int, 0)
     # The reference we got above was detached and is still valid.
-    self.assertEqual(sub_message.qux_int, 5)
-    sub_message.qux_int = 7
+    self.assertEqual(sub_message.moo_int, 5)
+    sub_message.moo_int = 7
 
   def assertInitialized(self, proto):
     self.assertTrue(proto.IsInitialized())
diff --git a/python/google/protobuf/internal/well_known_types_test.py b/python/google/protobuf/internal/well_known_types_test.py
index 3912901..3618fff 100644
--- a/python/google/protobuf/internal/well_known_types_test.py
+++ b/python/google/protobuf/internal/well_known_types_test.py
@@ -657,9 +657,9 @@
     # Test Merge oneof field.
     new_msg = unittest_pb2.TestOneof2()
     dst = unittest_pb2.TestOneof2()
-    dst.foo_message.qux_int = 1
+    dst.foo_message.moo_int = 1
     mask = field_mask_pb2.FieldMask()
-    mask.FromJsonString('fooMessage,fooLazyMessage.quxInt')
+    mask.FromJsonString('fooMessage,fooLazyMessage.mooInt')
     mask.MergeMessage(new_msg, dst)
     self.assertTrue(dst.HasField('foo_message'))
     self.assertFalse(dst.HasField('foo_lazy_message'))
diff --git a/python/google/protobuf/message_factory.py b/python/google/protobuf/message_factory.py
index 3656fa6..5539410 100644
--- a/python/google/protobuf/message_factory.py
+++ b/python/google/protobuf/message_factory.py
@@ -118,6 +118,8 @@
         self.GetPrototype(extension.containing_type)
       extended_class = self._classes[extension.containing_type]
       extended_class.RegisterExtension(extension)
+      if extension.message_type:
+        self.GetPrototype(extension.message_type)
     return result_class
 
   def GetMessages(self, files):
@@ -154,6 +156,8 @@
           self.GetPrototype(extension.containing_type)
         extended_class = self._classes[extension.containing_type]
         extended_class.RegisterExtension(extension)
+        if extension.message_type:
+          self.GetPrototype(extension.message_type)
     return result
 
 
diff --git a/python/google/protobuf/pyext/extension_dict.cc b/python/google/protobuf/pyext/extension_dict.cc
index 692029f..66703da 100644
--- a/python/google/protobuf/pyext/extension_dict.cc
+++ b/python/google/protobuf/pyext/extension_dict.cc
@@ -331,7 +331,7 @@
   }
   bool equals = false;
   if (PyObject_TypeCheck(other, &ExtensionDict_Type)) {
-    equals = self->parent == reinterpret_cast<ExtensionDict*>(other)->parent;;
+    equals = self->parent == reinterpret_cast<ExtensionDict*>(other)->parent;
   }
   if (equals ^ (opid == Py_EQ)) {
     Py_RETURN_FALSE;
diff --git a/python/google/protobuf/pyext/map_container.cc b/python/google/protobuf/pyext/map_container.cc
index 4d516d2..e8a6888 100644
--- a/python/google/protobuf/pyext/map_container.cc
+++ b/python/google/protobuf/pyext/map_container.cc
@@ -264,7 +264,7 @@
     case FieldDescriptor::CPPTYPE_BOOL: {
       GOOGLE_CHECK_GET_BOOL(obj, value, false);
       value_ref->SetBoolValue(value);
-      return true;;
+      return true;
     }
     case FieldDescriptor::CPPTYPE_STRING: {
       std::string str;
diff --git a/python/google/protobuf/pyext/unknown_field_set.cc b/python/google/protobuf/pyext/unknown_field_set.cc
index 76be8fd..42f9bbc 100644
--- a/python/google/protobuf/pyext/unknown_field_set.cc
+++ b/python/google/protobuf/pyext/unknown_field_set.cc
@@ -133,6 +133,8 @@
   PyUnknownFieldSet* self = reinterpret_cast<PyUnknownFieldSet*>(pself);
   if (self->parent == nullptr) {
     delete self->fields;
+  } else {
+    Py_CLEAR(self->parent);
   }
   auto* py_type = Py_TYPE(pself);
   self->~PyUnknownFieldSet();
diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec
index 0904700..fa99970 100644
--- a/ruby/google-protobuf.gemspec
+++ b/ruby/google-protobuf.gemspec
@@ -1,6 +1,6 @@
 Gem::Specification.new do |s|
   s.name        = "google-protobuf"
-  s.version     = "3.20.0.rc.1"
+  s.version     = "3.20.0"
   git_tag       = "v#{s.version.to_s.sub('.rc.', '-rc')}" # Converts X.Y.Z.rc.N to vX.Y.Z-rcN, used for the git tag
   s.licenses    = ["BSD-3-Clause"]
   s.summary     = "Protocol Buffers"
diff --git a/ruby/pom.xml b/ruby/pom.xml
index bf6a8c9..5482c6f 100644
--- a/ruby/pom.xml
+++ b/ruby/pom.xml
@@ -9,7 +9,7 @@
 
     <groupId>com.google.protobuf.jruby</groupId>
     <artifactId>protobuf-jruby</artifactId>
-    <version>3.20.0-rc-1</version>
+    <version>3.20.0</version>
     <name>Protocol Buffer JRuby native extension</name>
     <description>
       Protocol Buffers are a way of encoding structured data in an efficient yet
@@ -76,7 +76,7 @@
         <dependency>
           <groupId>com.google.protobuf</groupId>
           <artifactId>protobuf-java-util</artifactId>
-          <version>3.20.0-rc-1</version>
+          <version>3.20.0</version>
         </dependency>
         <dependency>
             <groupId>org.jruby</groupId>
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java
index a59596a..b809253 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java
@@ -35,6 +35,8 @@
 import com.google.protobuf.Descriptors.Descriptor;
 import com.google.protobuf.Descriptors.FieldDescriptor;
 import com.google.protobuf.Descriptors.OneofDescriptor;
+import java.util.HashMap;
+import java.util.Map;
 import org.jruby.*;
 import org.jruby.anno.JRubyClass;
 import org.jruby.anno.JRubyMethod;
@@ -44,180 +46,186 @@
 import org.jruby.runtime.ThreadContext;
 import org.jruby.runtime.builtin.IRubyObject;
 
-import java.util.HashMap;
-import java.util.Map;
-
-
 @JRubyClass(name = "Descriptor", include = "Enumerable")
 public class RubyDescriptor extends RubyObject {
-    public static void createRubyDescriptor(Ruby runtime) {
-        RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
-        RubyClass cDescriptor = protobuf.defineClassUnder("Descriptor", runtime.getObject(), new ObjectAllocator() {
-            @Override
-            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+  public static void createRubyDescriptor(Ruby runtime) {
+    RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
+    RubyClass cDescriptor =
+        protobuf.defineClassUnder(
+            "Descriptor",
+            runtime.getObject(),
+            new ObjectAllocator() {
+              @Override
+              public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
                 return new RubyDescriptor(runtime, klazz);
-            }
-        });
-        cDescriptor.includeModule(runtime.getEnumerable());
-        cDescriptor.defineAnnotatedMethods(RubyDescriptor.class);
-        cFieldDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::FieldDescriptor");
-        cOneofDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::OneofDescriptor");
+              }
+            });
+    cDescriptor.includeModule(runtime.getEnumerable());
+    cDescriptor.defineAnnotatedMethods(RubyDescriptor.class);
+    cFieldDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::FieldDescriptor");
+    cOneofDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::OneofDescriptor");
+  }
+
+  public RubyDescriptor(Ruby runtime, RubyClass klazz) {
+    super(runtime, klazz);
+  }
+
+  /*
+   * call-seq:
+   *     Descriptor.name => name
+   *
+   * Returns the name of this message type as a fully-qualified string (e.g.,
+   * My.Package.MessageType).
+   */
+  @JRubyMethod(name = "name")
+  public IRubyObject getName(ThreadContext context) {
+    return name;
+  }
+
+  /*
+   * call-seq:
+   *     Descriptor.lookup(name) => FieldDescriptor
+   *
+   * Returns the field descriptor for the field with the given name, if present,
+   * or nil if none.
+   */
+  @JRubyMethod
+  public IRubyObject lookup(ThreadContext context, IRubyObject fieldName) {
+    return Helpers.nullToNil(fieldDescriptors.get(fieldName), context.nil);
+  }
+
+  /*
+   * call-seq:
+   *     Descriptor.msgclass => message_klass
+   *
+   * Returns the Ruby class created for this message type. Valid only once the
+   * message type has been added to a pool.
+   */
+  @JRubyMethod
+  public IRubyObject msgclass(ThreadContext context) {
+    return klazz;
+  }
+
+  /*
+   * call-seq:
+   *     Descriptor.each(&block)
+   *
+   * Iterates over fields in this message type, yielding to the block on each one.
+   */
+  @JRubyMethod
+  public IRubyObject each(ThreadContext context, Block block) {
+    for (Map.Entry<IRubyObject, RubyFieldDescriptor> entry : fieldDescriptors.entrySet()) {
+      block.yield(context, entry.getValue());
+    }
+    return context.nil;
+  }
+
+  /*
+   * call-seq:
+   *    Descriptor.file_descriptor
+   *
+   * Returns the FileDescriptor object this message belongs to.
+   */
+  @JRubyMethod(name = "file_descriptor")
+  public IRubyObject getFileDescriptor(ThreadContext context) {
+    return RubyFileDescriptor.getRubyFileDescriptor(context, descriptor);
+  }
+
+  /*
+   * call-seq:
+   *     Descriptor.each_oneof(&block) => nil
+   *
+   * Invokes the given block for each oneof in this message type, passing the
+   * corresponding OneofDescriptor.
+   */
+  @JRubyMethod(name = "each_oneof")
+  public IRubyObject eachOneof(ThreadContext context, Block block) {
+    for (RubyOneofDescriptor oneofDescriptor : oneofDescriptors.values()) {
+      block.yieldSpecific(context, oneofDescriptor);
+    }
+    return context.nil;
+  }
+
+  /*
+   * call-seq:
+   *     Descriptor.lookup_oneof(name) => OneofDescriptor
+   *
+   * Returns the oneof descriptor for the oneof with the given name, if present,
+   * or nil if none.
+   */
+  @JRubyMethod(name = "lookup_oneof")
+  public IRubyObject lookupOneof(ThreadContext context, IRubyObject name) {
+    return Helpers.nullToNil(oneofDescriptors.get(Utils.symToString(name)), context.nil);
+  }
+
+  protected FieldDescriptor getField(String name) {
+    return descriptor.findFieldByName(name);
+  }
+
+  protected void setDescriptor(
+      ThreadContext context, Descriptor descriptor, RubyDescriptorPool pool) {
+    Ruby runtime = context.runtime;
+    Map<FieldDescriptor, RubyFieldDescriptor> cache = new HashMap();
+    this.descriptor = descriptor;
+
+    // Populate the field caches
+    fieldDescriptors = new HashMap<IRubyObject, RubyFieldDescriptor>();
+    oneofDescriptors = new HashMap<IRubyObject, RubyOneofDescriptor>();
+
+    for (FieldDescriptor fieldDescriptor : descriptor.getFields()) {
+      RubyFieldDescriptor fd =
+          (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK);
+      fd.setDescriptor(context, fieldDescriptor, pool);
+      fieldDescriptors.put(runtime.newString(fieldDescriptor.getName()), fd);
+      cache.put(fieldDescriptor, fd);
     }
 
-    public RubyDescriptor(Ruby runtime, RubyClass klazz) {
-        super(runtime, klazz);
+    for (OneofDescriptor oneofDescriptor : descriptor.getRealOneofs()) {
+      RubyOneofDescriptor ood =
+          (RubyOneofDescriptor) cOneofDescriptor.newInstance(context, Block.NULL_BLOCK);
+      ood.setDescriptor(context, oneofDescriptor, cache);
+      oneofDescriptors.put(runtime.newString(oneofDescriptor.getName()), ood);
     }
 
-    /*
-     * call-seq:
-     *     Descriptor.name => name
-     *
-     * Returns the name of this message type as a fully-qualified string (e.g.,
-     * My.Package.MessageType).
-     */
-    @JRubyMethod(name = "name")
-    public IRubyObject getName(ThreadContext context) {
-        return name;
-    }
+    // Make sure our class is built
+    this.klazz = buildClassFromDescriptor(context);
+  }
 
-    /*
-     * call-seq:
-     *     Descriptor.lookup(name) => FieldDescriptor
-     *
-     * Returns the field descriptor for the field with the given name, if present,
-     * or nil if none.
-     */
-    @JRubyMethod
-    public IRubyObject lookup(ThreadContext context, IRubyObject fieldName) {
-        return Helpers.nullToNil(fieldDescriptors.get(fieldName), context.nil);
-    }
+  protected void setName(IRubyObject name) {
+    this.name = name;
+  }
 
-    /*
-     * call-seq:
-     *     Descriptor.msgclass => message_klass
-     *
-     * Returns the Ruby class created for this message type. Valid only once the
-     * message type has been added to a pool.
-     */
-    @JRubyMethod
-    public IRubyObject msgclass(ThreadContext context) {
-        return klazz;
-    }
+  private RubyClass buildClassFromDescriptor(ThreadContext context) {
+    Ruby runtime = context.runtime;
 
-    /*
-     * call-seq:
-     *     Descriptor.each(&block)
-     *
-     * Iterates over fields in this message type, yielding to the block on each one.
-     */
-    @JRubyMethod
-    public IRubyObject each(ThreadContext context, Block block) {
-        for (Map.Entry<IRubyObject, RubyFieldDescriptor> entry : fieldDescriptors.entrySet()) {
-            block.yield(context, entry.getValue());
-        }
-        return context.nil;
-    }
-
-    /*
-     * call-seq:
-     *    Descriptor.file_descriptor
-     *
-     * Returns the FileDescriptor object this message belongs to.
-     */
-     @JRubyMethod(name = "file_descriptor")
-     public IRubyObject getFileDescriptor(ThreadContext context) {
-        return RubyFileDescriptor.getRubyFileDescriptor(context, descriptor);
-     }
-
-    /*
-     * call-seq:
-     *     Descriptor.each_oneof(&block) => nil
-     *
-     * Invokes the given block for each oneof in this message type, passing the
-     * corresponding OneofDescriptor.
-     */
-    @JRubyMethod(name = "each_oneof")
-    public IRubyObject eachOneof(ThreadContext context, Block block) {
-        for (RubyOneofDescriptor oneofDescriptor : oneofDescriptors.values()) {
-            block.yieldSpecific(context, oneofDescriptor);
-        }
-        return context.nil;
-    }
-
-    /*
-     * call-seq:
-     *     Descriptor.lookup_oneof(name) => OneofDescriptor
-     *
-     * Returns the oneof descriptor for the oneof with the given name, if present,
-     * or nil if none.
-     */
-    @JRubyMethod(name = "lookup_oneof")
-    public IRubyObject lookupOneof(ThreadContext context, IRubyObject name) {
-        return Helpers.nullToNil(oneofDescriptors.get(Utils.symToString(name)), context.nil);
-    }
-
-    protected FieldDescriptor getField(String name) {
-        return descriptor.findFieldByName(name);
-    }
-
-    protected void setDescriptor(ThreadContext context, Descriptor descriptor, RubyDescriptorPool pool) {
-        Ruby runtime = context.runtime;
-        Map<FieldDescriptor, RubyFieldDescriptor> cache = new HashMap();
-        this.descriptor = descriptor;
-
-        // Populate the field caches
-        fieldDescriptors = new HashMap<IRubyObject, RubyFieldDescriptor>();
-        oneofDescriptors = new HashMap<IRubyObject, RubyOneofDescriptor>();
-
-        for (FieldDescriptor fieldDescriptor : descriptor.getFields()) {
-            RubyFieldDescriptor fd = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK);
-            fd.setDescriptor(context, fieldDescriptor, pool);
-            fieldDescriptors.put(runtime.newString(fieldDescriptor.getName()), fd);
-            cache.put(fieldDescriptor, fd);
-        }
-
-        for (OneofDescriptor oneofDescriptor : descriptor.getRealOneofs()) {
-            RubyOneofDescriptor ood = (RubyOneofDescriptor) cOneofDescriptor.newInstance(context, Block.NULL_BLOCK);
-            ood.setDescriptor(context, oneofDescriptor, cache);
-            oneofDescriptors.put(runtime.newString(oneofDescriptor.getName()), ood);
-        }
-
-        // Make sure our class is built
-        this.klazz = buildClassFromDescriptor(context);
-    }
-
-    protected void setName(IRubyObject name) {
-        this.name = name;
-    }
-
-    private RubyClass buildClassFromDescriptor(ThreadContext context) {
-        Ruby runtime = context.runtime;
-
-        ObjectAllocator allocator = new ObjectAllocator() {
-            @Override
-            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
-                return new RubyMessage(runtime, klazz, descriptor);
-            }
+    ObjectAllocator allocator =
+        new ObjectAllocator() {
+          @Override
+          public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            return new RubyMessage(runtime, klazz, descriptor);
+          }
         };
 
-        // rb_define_class_id
-        RubyClass klass = RubyClass.newClass(runtime, runtime.getObject());
-        klass.setAllocator(allocator);
-        klass.makeMetaClass(runtime.getObject().getMetaClass());
-        klass.inherit(runtime.getObject());
-        RubyModule messageExts = runtime.getClassFromPath("Google::Protobuf::MessageExts");
-        klass.include(new IRubyObject[] {messageExts});
-        klass.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this);
-        klass.defineAnnotatedMethods(RubyMessage.class);
-        return klass;
-    }
+    // rb_define_class_id
+    RubyClass klass = RubyClass.newClass(runtime, runtime.getObject());
+    klass.setAllocator(allocator);
+    klass.makeMetaClass(runtime.getObject().getMetaClass());
+    klass.inherit(runtime.getObject());
+    RubyModule messageExts = runtime.getClassFromPath("Google::Protobuf::MessageExts");
+    klass.include(new IRubyObject[] {messageExts});
+    klass.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this);
+    klass.defineAnnotatedMethods(RubyMessage.class);
+    // Workaround for https://github.com/jruby/jruby/issues/7154
+    klass.searchMethod("respond_to?").setIsBuiltin(false);
+    return klass;
+  }
 
-    private static RubyClass cFieldDescriptor;
-    private static RubyClass cOneofDescriptor;
+  private static RubyClass cFieldDescriptor;
+  private static RubyClass cOneofDescriptor;
 
-    private Descriptor descriptor;
-    private IRubyObject name;
-    private Map<IRubyObject, RubyFieldDescriptor> fieldDescriptors;
-    private Map<IRubyObject, RubyOneofDescriptor> oneofDescriptors;
-    private RubyClass klazz;
+  private Descriptor descriptor;
+  private IRubyObject name;
+  private Map<IRubyObject, RubyFieldDescriptor> fieldDescriptors;
+  private Map<IRubyObject, RubyOneofDescriptor> oneofDescriptors;
+  private RubyClass klazz;
 }
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptorPool.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptorPool.java
index 6cdb341..d65b412 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptorPool.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptorPool.java
@@ -38,6 +38,10 @@
 import com.google.protobuf.Descriptors.EnumDescriptor;
 import com.google.protobuf.Descriptors.FileDescriptor;
 import com.google.protobuf.InvalidProtocolBufferException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import org.jruby.*;
 import org.jruby.anno.JRubyClass;
 import org.jruby.anno.JRubyMethod;
@@ -45,136 +49,147 @@
 import org.jruby.runtime.*;
 import org.jruby.runtime.builtin.IRubyObject;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 @JRubyClass(name = "DescriptorPool")
 public class RubyDescriptorPool extends RubyObject {
-    public static void createRubyDescriptorPool(Ruby runtime) {
-        RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
-        RubyClass cDescriptorPool = protobuf.defineClassUnder("DescriptorPool", runtime.getObject(), new ObjectAllocator() {
-            @Override
-            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+  public static void createRubyDescriptorPool(Ruby runtime) {
+    RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
+    RubyClass cDescriptorPool =
+        protobuf.defineClassUnder(
+            "DescriptorPool",
+            runtime.getObject(),
+            new ObjectAllocator() {
+              @Override
+              public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
                 return new RubyDescriptorPool(runtime, klazz);
-            }
-        });
+              }
+            });
 
-        cDescriptorPool.defineAnnotatedMethods(RubyDescriptorPool.class);
-        descriptorPool = (RubyDescriptorPool) cDescriptorPool.newInstance(runtime.getCurrentContext(), Block.NULL_BLOCK);
-        cDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Descriptor");
-        cEnumDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::EnumDescriptor");
+    cDescriptorPool.defineAnnotatedMethods(RubyDescriptorPool.class);
+    descriptorPool =
+        (RubyDescriptorPool)
+            cDescriptorPool.newInstance(runtime.getCurrentContext(), Block.NULL_BLOCK);
+    cDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Descriptor");
+    cEnumDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::EnumDescriptor");
+  }
+
+  public RubyDescriptorPool(Ruby runtime, RubyClass klazz) {
+    super(runtime, klazz);
+    this.fileDescriptors = new ArrayList<>();
+    this.symtab = new HashMap<IRubyObject, IRubyObject>();
+  }
+
+  @JRubyMethod
+  public IRubyObject build(ThreadContext context, Block block) {
+    RubyClass cBuilder =
+        (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::Internal::Builder");
+    RubyBasicObject ctx = (RubyBasicObject) cBuilder.newInstance(context, this, Block.NULL_BLOCK);
+    ctx.instance_eval(context, block);
+    ctx.callMethod(context, "build"); // Needs to be called to support the deprecated syntax
+    return context.nil;
+  }
+
+  /*
+   * call-seq:
+   *     DescriptorPool.lookup(name) => descriptor
+   *
+   * Finds a Descriptor or EnumDescriptor by name and returns it, or nil if none
+   * exists with the given name.
+   *
+   * This currently lazy loads the ruby descriptor objects as they are requested.
+   * This allows us to leave the heavy lifting to the java library
+   */
+  @JRubyMethod
+  public IRubyObject lookup(ThreadContext context, IRubyObject name) {
+    return Helpers.nullToNil(symtab.get(name), context.nil);
+  }
+
+  /*
+   * call-seq:
+   *     DescriptorPool.generated_pool => descriptor_pool
+   *
+   * Class method that returns the global DescriptorPool. This is a singleton into
+   * which generated-code message and enum types are registered. The user may also
+   * register types in this pool for convenience so that they do not have to hold
+   * a reference to a private pool instance.
+   */
+  @JRubyMethod(meta = true, name = "generated_pool")
+  public static IRubyObject generatedPool(ThreadContext context, IRubyObject recv) {
+    return descriptorPool;
+  }
+
+  @JRubyMethod(required = 1)
+  public IRubyObject add_serialized_file(ThreadContext context, IRubyObject data) {
+    byte[] bin = data.convertToString().getBytes();
+    try {
+      FileDescriptorProto.Builder builder = FileDescriptorProto.newBuilder().mergeFrom(bin);
+      registerFileDescriptor(context, builder);
+    } catch (InvalidProtocolBufferException e) {
+      throw RaiseException.from(
+          context.runtime,
+          (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::ParseError"),
+          e.getMessage());
+    }
+    return context.nil;
+  }
+
+  protected void registerFileDescriptor(
+      ThreadContext context, FileDescriptorProto.Builder builder) {
+    final FileDescriptor fd;
+    try {
+      fd = FileDescriptor.buildFrom(builder.build(), existingFileDescriptors());
+    } catch (DescriptorValidationException e) {
+      throw context.runtime.newRuntimeError(e.getMessage());
     }
 
-    public RubyDescriptorPool(Ruby runtime, RubyClass klazz) {
-        super(runtime, klazz);
-        this.fileDescriptors = new ArrayList<>();
-        this.symtab = new HashMap<IRubyObject, IRubyObject>();
+    String packageName = fd.getPackage();
+    if (!packageName.isEmpty()) {
+      packageName = packageName + ".";
     }
 
-    @JRubyMethod
-    public IRubyObject build(ThreadContext context, Block block) {
-        RubyClass cBuilder = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::Internal::Builder");
-        RubyBasicObject ctx = (RubyBasicObject) cBuilder.newInstance(context, this, Block.NULL_BLOCK);
-        ctx.instance_eval(context, block);
-        ctx.callMethod(context, "build"); // Needs to be called to support the deprecated syntax
-        return context.nil;
-    }
+    // Need to make sure enums are registered first in case anything references them
+    for (EnumDescriptor ed : fd.getEnumTypes()) registerEnumDescriptor(context, ed, packageName);
+    for (Descriptor message : fd.getMessageTypes())
+      registerDescriptor(context, message, packageName);
 
-    /*
-     * call-seq:
-     *     DescriptorPool.lookup(name) => descriptor
-     *
-     * Finds a Descriptor or EnumDescriptor by name and returns it, or nil if none
-     * exists with the given name.
-     *
-     * This currently lazy loads the ruby descriptor objects as they are requested.
-     * This allows us to leave the heavy lifting to the java library
-     */
-    @JRubyMethod
-    public IRubyObject lookup(ThreadContext context, IRubyObject name) {
-        return Helpers.nullToNil(symtab.get(name), context.nil);
-    }
+    // Mark this as a loaded file
+    fileDescriptors.add(fd);
+  }
 
-    /*
-     * call-seq:
-     *     DescriptorPool.generated_pool => descriptor_pool
-     *
-     * Class method that returns the global DescriptorPool. This is a singleton into
-     * which generated-code message and enum types are registered. The user may also
-     * register types in this pool for convenience so that they do not have to hold
-     * a reference to a private pool instance.
-     */
-    @JRubyMethod(meta = true, name = "generated_pool")
-    public static IRubyObject generatedPool(ThreadContext context, IRubyObject recv) {
-        return descriptorPool;
-    }
+  private void registerDescriptor(ThreadContext context, Descriptor descriptor, String parentPath) {
+    String fullName = parentPath + descriptor.getName();
+    String fullPath = fullName + ".";
+    RubyString name = context.runtime.newString(fullName);
 
-    @JRubyMethod(required = 1)
-    public IRubyObject add_serialized_file (ThreadContext context, IRubyObject data ) {
-        byte[] bin = data.convertToString().getBytes();
-        try {
-            FileDescriptorProto.Builder builder = FileDescriptorProto.newBuilder().mergeFrom(bin);
-            registerFileDescriptor(context, builder);
-        } catch (InvalidProtocolBufferException e) {
-            throw RaiseException.from(context.runtime, (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::ParseError"), e.getMessage());
-        }
-        return context.nil;
-    }
+    RubyDescriptor des = (RubyDescriptor) cDescriptor.newInstance(context, Block.NULL_BLOCK);
+    des.setName(name);
+    des.setDescriptor(context, descriptor, this);
+    symtab.put(name, des);
 
-    protected void registerFileDescriptor(ThreadContext context, FileDescriptorProto.Builder builder) {
-        final FileDescriptor fd;
-        try {
-            fd = FileDescriptor.buildFrom(builder.build(), existingFileDescriptors());
-        } catch (DescriptorValidationException e) {
-            throw context.runtime.newRuntimeError(e.getMessage());
-        }
+    // Need to make sure enums are registered first in case anything references them
+    for (EnumDescriptor ed : descriptor.getEnumTypes())
+      registerEnumDescriptor(context, ed, fullPath);
+    for (Descriptor message : descriptor.getNestedTypes())
+      registerDescriptor(context, message, fullPath);
+  }
 
-        String packageName = fd.getPackage();
-        if (!packageName.isEmpty()) {
-            packageName = packageName + ".";
-        }
+  private void registerEnumDescriptor(
+      ThreadContext context, EnumDescriptor descriptor, String parentPath) {
+    RubyString name = context.runtime.newString(parentPath + descriptor.getName());
+    RubyEnumDescriptor des =
+        (RubyEnumDescriptor) cEnumDescriptor.newInstance(context, Block.NULL_BLOCK);
+    des.setName(name);
+    des.setDescriptor(context, descriptor);
+    symtab.put(name, des);
+  }
 
-        // Need to make sure enums are registered first in case anything references them
-        for (EnumDescriptor ed : fd.getEnumTypes()) registerEnumDescriptor(context, ed, packageName);
-        for (Descriptor message : fd.getMessageTypes()) registerDescriptor(context, message, packageName);
+  private FileDescriptor[] existingFileDescriptors() {
+    return fileDescriptors.toArray(new FileDescriptor[fileDescriptors.size()]);
+  }
 
-        // Mark this as a loaded file
-        fileDescriptors.add(fd);
-    }
+  private static RubyClass cDescriptor;
+  private static RubyClass cEnumDescriptor;
+  private static RubyDescriptorPool descriptorPool;
 
-    private void registerDescriptor(ThreadContext context, Descriptor descriptor, String parentPath) {
-        String fullName = parentPath + descriptor.getName();
-        String fullPath = fullName + ".";
-        RubyString name = context.runtime.newString(fullName);
-
-        RubyDescriptor des = (RubyDescriptor) cDescriptor.newInstance(context, Block.NULL_BLOCK);
-        des.setName(name);
-        des.setDescriptor(context, descriptor, this);
-        symtab.put(name, des);
-
-        // Need to make sure enums are registered first in case anything references them
-        for (EnumDescriptor ed : descriptor.getEnumTypes()) registerEnumDescriptor(context, ed, fullPath);
-        for (Descriptor message : descriptor.getNestedTypes()) registerDescriptor(context, message, fullPath);
-    }
-
-    private void registerEnumDescriptor(ThreadContext context, EnumDescriptor descriptor, String parentPath) {
-        RubyString name = context.runtime.newString(parentPath + descriptor.getName());
-        RubyEnumDescriptor des = (RubyEnumDescriptor) cEnumDescriptor.newInstance(context, Block.NULL_BLOCK);
-        des.setName(name);
-        des.setDescriptor(context, descriptor);
-        symtab.put(name, des);
-    }
-
-    private FileDescriptor[] existingFileDescriptors() {
-        return fileDescriptors.toArray(new FileDescriptor[fileDescriptors.size()]);
-    }
-
-    private static RubyClass cDescriptor;
-    private static RubyClass cEnumDescriptor;
-    private static RubyDescriptorPool descriptorPool;
-
-    private List<FileDescriptor> fileDescriptors;
-    private Map<IRubyObject, IRubyObject> symtab;
+  private List<FileDescriptor> fileDescriptors;
+  private Map<IRubyObject, IRubyObject> symtab;
 }
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnum.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnum.java
index 17525df..95d961e 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnum.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnum.java
@@ -38,41 +38,41 @@
 import org.jruby.runtime.builtin.IRubyObject;
 
 public class RubyEnum {
-    /*
-     * call-seq:
-     *     Enum.lookup(number) => name
-     *
-     * This module method, provided on each generated enum module, looks up an enum
-     * value by number and returns its name as a Ruby symbol, or nil if not found.
-     */
-    @JRubyMethod(meta = true)
-    public static IRubyObject lookup(ThreadContext context, IRubyObject recv, IRubyObject number) {
-        RubyEnumDescriptor rubyEnumDescriptor = (RubyEnumDescriptor) getDescriptor(context, recv);
-        return rubyEnumDescriptor.numberToName(context, number);
-    }
+  /*
+   * call-seq:
+   *     Enum.lookup(number) => name
+   *
+   * This module method, provided on each generated enum module, looks up an enum
+   * value by number and returns its name as a Ruby symbol, or nil if not found.
+   */
+  @JRubyMethod(meta = true)
+  public static IRubyObject lookup(ThreadContext context, IRubyObject recv, IRubyObject number) {
+    RubyEnumDescriptor rubyEnumDescriptor = (RubyEnumDescriptor) getDescriptor(context, recv);
+    return rubyEnumDescriptor.numberToName(context, number);
+  }
 
-    /*
-     * call-seq:
-     *     Enum.resolve(name) => number
-     *
-     * This module method, provided on each generated enum module, looks up an enum
-     * value by name (as a Ruby symbol) and returns its name, or nil if not found.
-     */
-    @JRubyMethod(meta = true)
-    public static IRubyObject resolve(ThreadContext context, IRubyObject recv, IRubyObject name) {
-        RubyEnumDescriptor rubyEnumDescriptor = (RubyEnumDescriptor) getDescriptor(context, recv);
-        return rubyEnumDescriptor.nameToNumber(context, name);
-    }
+  /*
+   * call-seq:
+   *     Enum.resolve(name) => number
+   *
+   * This module method, provided on each generated enum module, looks up an enum
+   * value by name (as a Ruby symbol) and returns its name, or nil if not found.
+   */
+  @JRubyMethod(meta = true)
+  public static IRubyObject resolve(ThreadContext context, IRubyObject recv, IRubyObject name) {
+    RubyEnumDescriptor rubyEnumDescriptor = (RubyEnumDescriptor) getDescriptor(context, recv);
+    return rubyEnumDescriptor.nameToNumber(context, name);
+  }
 
-    /*
-     * call-seq:
-     *     Enum.descriptor
-     *
-     * This module method, provided on each generated enum module, returns the
-     * EnumDescriptor corresponding to this enum type.
-     */
-    @JRubyMethod(meta = true, name = "descriptor")
-    public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) {
-        return ((RubyModule) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR);
-    }
+  /*
+   * call-seq:
+   *     Enum.descriptor
+   *
+   * This module method, provided on each generated enum module, returns the
+   * EnumDescriptor corresponding to this enum type.
+   */
+  @JRubyMethod(meta = true, name = "descriptor")
+  public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) {
+    return ((RubyModule) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR);
+  }
 }
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java
index 26f00db..6532867 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java
@@ -39,8 +39,8 @@
 import org.jruby.Ruby;
 import org.jruby.RubyClass;
 import org.jruby.RubyModule;
-import org.jruby.RubyObject;
 import org.jruby.RubyNumeric;
+import org.jruby.RubyObject;
 import org.jruby.anno.JRubyClass;
 import org.jruby.anno.JRubyMethod;
 import org.jruby.runtime.Block;
@@ -50,132 +50,147 @@
 
 @JRubyClass(name = "EnumDescriptor", include = "Enumerable")
 public class RubyEnumDescriptor extends RubyObject {
-    public static void createRubyEnumDescriptor(Ruby runtime) {
-        RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
-        RubyClass cEnumDescriptor = mProtobuf.defineClassUnder("EnumDescriptor", runtime.getObject(), new ObjectAllocator() {
-            @Override
-            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+  public static void createRubyEnumDescriptor(Ruby runtime) {
+    RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
+    RubyClass cEnumDescriptor =
+        mProtobuf.defineClassUnder(
+            "EnumDescriptor",
+            runtime.getObject(),
+            new ObjectAllocator() {
+              @Override
+              public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
                 return new RubyEnumDescriptor(runtime, klazz);
-            }
-        });
-        cEnumDescriptor.includeModule(runtime.getEnumerable());
-        cEnumDescriptor.defineAnnotatedMethods(RubyEnumDescriptor.class);
+              }
+            });
+    cEnumDescriptor.includeModule(runtime.getEnumerable());
+    cEnumDescriptor.defineAnnotatedMethods(RubyEnumDescriptor.class);
+  }
+
+  public RubyEnumDescriptor(Ruby runtime, RubyClass klazz) {
+    super(runtime, klazz);
+  }
+
+  /*
+   * call-seq:
+   *     EnumDescriptor.name => name
+   *
+   * Returns the name of this enum type.
+   */
+  @JRubyMethod(name = "name")
+  public IRubyObject getName(ThreadContext context) {
+    return this.name;
+  }
+
+  /*
+   * call-seq:
+   *     EnumDescriptor.each(&block)
+   *
+   * Iterates over key => value mappings in this enum's definition, yielding to
+   * the block with (key, value) arguments for each one.
+   */
+  @JRubyMethod
+  public IRubyObject each(ThreadContext context, Block block) {
+    Ruby runtime = context.runtime;
+    for (EnumValueDescriptor enumValueDescriptor : descriptor.getValues()) {
+      block.yield(
+          context,
+          runtime.newArray(
+              runtime.newSymbol(enumValueDescriptor.getName()),
+              runtime.newFixnum(enumValueDescriptor.getNumber())));
+    }
+    return context.nil;
+  }
+
+  /*
+   * call-seq:
+   *     EnumDescriptor.enummodule => module
+   *
+   * Returns the Ruby module corresponding to this enum type. Cannot be called
+   * until the enum descriptor has been added to a pool.
+   */
+  @JRubyMethod
+  public IRubyObject enummodule(ThreadContext context) {
+    return module;
+  }
+
+  /*
+   * call-seq:
+   *    EnumDescriptor.file_descriptor
+   *
+   * Returns the FileDescriptor object this enum belongs to.
+   */
+  @JRubyMethod(name = "file_descriptor")
+  public IRubyObject getFileDescriptor(ThreadContext context) {
+    return RubyFileDescriptor.getRubyFileDescriptor(context, descriptor);
+  }
+
+  public boolean isValidValue(ThreadContext context, IRubyObject value) {
+    EnumValueDescriptor enumValue;
+
+    if (Utils.isRubyNum(value)) {
+      enumValue = descriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
+    } else {
+      enumValue = descriptor.findValueByName(value.asJavaString());
     }
 
-    public RubyEnumDescriptor(Ruby runtime, RubyClass klazz) {
-        super(runtime, klazz);
+    return enumValue != null;
+  }
+
+  protected IRubyObject nameToNumber(ThreadContext context, IRubyObject name) {
+    EnumValueDescriptor value = descriptor.findValueByName(name.asJavaString());
+    return value == null ? context.nil : context.runtime.newFixnum(value.getNumber());
+  }
+
+  protected IRubyObject numberToName(ThreadContext context, IRubyObject number) {
+    EnumValueDescriptor value = descriptor.findValueByNumber(RubyNumeric.num2int(number));
+    return value == null ? context.nil : context.runtime.newSymbol(value.getName());
+  }
+
+  protected void setDescriptor(ThreadContext context, EnumDescriptor descriptor) {
+    this.descriptor = descriptor;
+    this.module = buildModuleFromDescriptor(context);
+  }
+
+  protected void setName(IRubyObject name) {
+    this.name = name;
+  }
+
+  private RubyModule buildModuleFromDescriptor(ThreadContext context) {
+    Ruby runtime = context.runtime;
+
+    RubyModule enumModule = RubyModule.newModule(runtime);
+    boolean defaultValueRequiredButNotFound =
+        descriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3;
+    for (EnumValueDescriptor value : descriptor.getValues()) {
+      String name = value.getName();
+      // Make sure its a valid constant name before trying to create it
+      if (Character.isUpperCase(name.codePointAt(0))) {
+        enumModule.defineConstant(name, runtime.newFixnum(value.getNumber()));
+      } else {
+        runtime
+            .getWarnings()
+            .warn(
+                "Enum value "
+                    + name
+                    + " does not start with an uppercase letter as is required for Ruby"
+                    + " constants.");
+      }
+      if (value.getNumber() == 0) {
+        defaultValueRequiredButNotFound = false;
+      }
     }
 
-    /*
-     * call-seq:
-     *     EnumDescriptor.name => name
-     *
-     * Returns the name of this enum type.
-     */
-    @JRubyMethod(name = "name")
-    public IRubyObject getName(ThreadContext context) {
-        return this.name;
+    if (defaultValueRequiredButNotFound) {
+      throw Utils.createTypeError(
+          context, "Enum definition " + name + " does not contain a value for '0'");
     }
+    enumModule.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this);
+    enumModule.defineAnnotatedMethods(RubyEnum.class);
+    return enumModule;
+  }
 
-    /*
-     * call-seq:
-     *     EnumDescriptor.each(&block)
-     *
-     * Iterates over key => value mappings in this enum's definition, yielding to
-     * the block with (key, value) arguments for each one.
-     */
-    @JRubyMethod
-    public IRubyObject each(ThreadContext context, Block block) {
-        Ruby runtime = context.runtime;
-        for (EnumValueDescriptor enumValueDescriptor : descriptor.getValues()) {
-            block.yield(context, runtime.newArray(runtime.newSymbol(enumValueDescriptor.getName()),
-                    runtime.newFixnum(enumValueDescriptor.getNumber())));
-        }
-        return context.nil;
-    }
-
-    /*
-     * call-seq:
-     *     EnumDescriptor.enummodule => module
-     *
-     * Returns the Ruby module corresponding to this enum type. Cannot be called
-     * until the enum descriptor has been added to a pool.
-     */
-    @JRubyMethod
-    public IRubyObject enummodule(ThreadContext context) {
-        return module;
-    }
-
-    /*
-     * call-seq:
-     *    EnumDescriptor.file_descriptor
-     *
-     * Returns the FileDescriptor object this enum belongs to.
-     */
-    @JRubyMethod(name = "file_descriptor")
-    public IRubyObject getFileDescriptor(ThreadContext context) {
-       return RubyFileDescriptor.getRubyFileDescriptor(context, descriptor);
-    }
-
-    public boolean isValidValue(ThreadContext context, IRubyObject value) {
-        EnumValueDescriptor enumValue;
-
-        if (Utils.isRubyNum(value)) {
-            enumValue = descriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
-        } else {
-            enumValue = descriptor.findValueByName(value.asJavaString());
-        }
-
-        return enumValue != null;
-    }
-
-    protected IRubyObject nameToNumber(ThreadContext context, IRubyObject name)  {
-        EnumValueDescriptor value = descriptor.findValueByName(name.asJavaString());
-        return value == null ? context.nil : context.runtime.newFixnum(value.getNumber());
-    }
-
-    protected IRubyObject numberToName(ThreadContext context, IRubyObject number)  {
-        EnumValueDescriptor value = descriptor.findValueByNumber(RubyNumeric.num2int(number));
-        return value == null ? context.nil : context.runtime.newSymbol(value.getName());
-    }
-
-    protected void setDescriptor(ThreadContext context, EnumDescriptor descriptor) {
-        this.descriptor = descriptor;
-        this.module = buildModuleFromDescriptor(context);
-    }
-
-    protected void setName(IRubyObject name) {
-        this.name = name;
-    }
-
-    private RubyModule buildModuleFromDescriptor(ThreadContext context) {
-        Ruby runtime = context.runtime;
-
-        RubyModule enumModule = RubyModule.newModule(runtime);
-        boolean defaultValueRequiredButNotFound = descriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3;
-        for (EnumValueDescriptor value : descriptor.getValues()) {
-            String name = value.getName();
-            // Make sure its a valid constant name before trying to create it
-            if (Character.isUpperCase(name.codePointAt(0))) {
-                enumModule.defineConstant(name, runtime.newFixnum(value.getNumber()));
-            } else {
-                runtime.getWarnings().warn("Enum value " + name + " does not start with an uppercase letter as is required for Ruby constants.");
-            }
-            if (value.getNumber() == 0) {
-                defaultValueRequiredButNotFound = false;
-            }
-        }
-
-        if (defaultValueRequiredButNotFound) {
-            throw Utils.createTypeError(context, "Enum definition " + name + " does not contain a value for '0'");
-        }
-        enumModule.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this);
-        enumModule.defineAnnotatedMethods(RubyEnum.class);
-        return enumModule;
-    }
-
-    private EnumDescriptor descriptor;
-    private EnumDescriptorProto.Builder builder;
-    private IRubyObject name;
-    private RubyModule module;
+  private EnumDescriptor descriptor;
+  private EnumDescriptorProto.Builder builder;
+  private IRubyObject name;
+  private RubyModule module;
 }
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java
index e9594d8..bc1fe0c 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java
@@ -43,228 +43,237 @@
 
 @JRubyClass(name = "FieldDescriptor")
 public class RubyFieldDescriptor extends RubyObject {
-    public static void createRubyFieldDescriptor(Ruby runtime) {
-        RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
-        RubyClass cFieldDescriptor = mProtobuf.defineClassUnder("FieldDescriptor", runtime.getObject(), new ObjectAllocator() {
-            @Override
-            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+  public static void createRubyFieldDescriptor(Ruby runtime) {
+    RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
+    RubyClass cFieldDescriptor =
+        mProtobuf.defineClassUnder(
+            "FieldDescriptor",
+            runtime.getObject(),
+            new ObjectAllocator() {
+              @Override
+              public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
                 return new RubyFieldDescriptor(runtime, klazz);
-            }
-        });
-        cFieldDescriptor.defineAnnotatedMethods(RubyFieldDescriptor.class);
+              }
+            });
+    cFieldDescriptor.defineAnnotatedMethods(RubyFieldDescriptor.class);
+  }
+
+  public RubyFieldDescriptor(Ruby runtime, RubyClass klazz) {
+    super(runtime, klazz);
+  }
+
+  /*
+   * call-seq:
+   *     FieldDescriptor.default => default
+   *
+   * Returns this field's default, as a Ruby object, or nil if not yet set.
+   */
+  // VALUE FieldDescriptor_default(VALUE _self) {
+  //   DEFINE_SELF(FieldDescriptor, self, _self);
+  //   return layout_get_default(self->fielddef);
+  // }
+
+  /*
+   * call-seq:
+   *     FieldDescriptor.label => label
+   *
+   * Returns this field's label (i.e., plurality), as a Ruby symbol.
+   *
+   * Valid field labels are:
+   *     :optional, :repeated
+   */
+  @JRubyMethod(name = "label")
+  public IRubyObject getLabel(ThreadContext context) {
+    if (label == null) {
+      calculateLabel(context);
     }
+    return label;
+  }
 
-    public RubyFieldDescriptor(Ruby runtime, RubyClass klazz) {
-        super(runtime, klazz);
+  /*
+   * call-seq:
+   *     FieldDescriptor.name => name
+   *
+   * Returns the name of this field as a Ruby String, or nil if it is not set.
+   */
+  @JRubyMethod(name = "name")
+  public IRubyObject getName(ThreadContext context) {
+    return this.name;
+  }
+
+  /*
+   * call-seq:
+   *     FieldDescriptor.subtype => message_or_enum_descriptor
+   *
+   * Returns the message or enum descriptor corresponding to this field's type if
+   * it is a message or enum field, respectively, or nil otherwise. Cannot be
+   * called *until* the containing message type is added to a pool (and thus
+   * resolved).
+   */
+  @JRubyMethod(name = "subtype")
+  public IRubyObject getSubtype(ThreadContext context) {
+    if (subtype == null) {
+      calculateSubtype(context);
     }
+    return subtype;
+  }
 
-    /*
-     * call-seq:
-     *     FieldDescriptor.default => default
-     *
-     * Returns this field's default, as a Ruby object, or nil if not yet set.
-     */
-    // VALUE FieldDescriptor_default(VALUE _self) {
-    //   DEFINE_SELF(FieldDescriptor, self, _self);
-    //   return layout_get_default(self->fielddef);
-    // }
+  /*
+   * call-seq:
+   *     FieldDescriptor.type => type
+   *
+   * Returns this field's type, as a Ruby symbol, or nil if not yet set.
+   *
+   * Valid field types are:
+   *     :int32, :int64, :uint32, :uint64, :float, :double, :bool, :string,
+   *     :bytes, :message.
+   */
+  @JRubyMethod(name = "type")
+  public IRubyObject getType(ThreadContext context) {
+    return Utils.fieldTypeToRuby(context, descriptor.getType());
+  }
 
-    /*
-     * call-seq:
-     *     FieldDescriptor.label => label
-     *
-     * Returns this field's label (i.e., plurality), as a Ruby symbol.
-     *
-     * Valid field labels are:
-     *     :optional, :repeated
-     */
-    @JRubyMethod(name = "label")
-    public IRubyObject getLabel(ThreadContext context) {
-        if (label == null) {
-            calculateLabel(context);
-        }
-        return label;
+  /*
+   * call-seq:
+   *     FieldDescriptor.number => number
+   *
+   * Returns the tag number for this field.
+   */
+  @JRubyMethod(name = "number")
+  public IRubyObject getNumber(ThreadContext context) {
+    return this.number;
+  }
+
+  /*
+   * call-seq:
+   *     FieldDescriptor.submsg_name => submsg_name
+   *
+   * Returns the name of the message or enum type corresponding to this field, if
+   * it is a message or enum field (respectively), or nil otherwise. This type
+   * name will be resolved within the context of the pool to which the containing
+   * message type is added.
+   */
+  // VALUE FieldDescriptor_submsg_name(VALUE _self) {
+  //   DEFINE_SELF(FieldDescriptor, self, _self);
+  //   switch (upb_fielddef_type(self->fielddef)) {
+  //     case UPB_TYPE_ENUM:
+  //       return rb_str_new2(
+  //           upb_enumdef_fullname(upb_fielddef_enumsubdef(self->fielddef)));
+  //     case UPB_TYPE_MESSAGE:
+  //       return rb_str_new2(
+  //           upb_msgdef_fullname(upb_fielddef_msgsubdef(self->fielddef)));
+  //     default:
+  //       return Qnil;
+  //   }
+  // }
+  /*
+   * call-seq:
+   *     FieldDescriptor.submsg_name = submsg_name
+   *
+   * Sets the name of the message or enum type corresponding to this field, if it
+   * is a message or enum field (respectively). This type name will be resolved
+   * within the context of the pool to which the containing message type is added.
+   * Cannot be called on field that are not of message or enum type, or on fields
+   * that are part of a message type already added to a pool.
+   */
+  // @JRubyMethod(name = "submsg_name=")
+  // public IRubyObject setSubmsgName(ThreadContext context, IRubyObject name) {
+  //     this.builder.setTypeName("." + Utils.escapeIdentifier(name.asJavaString()));
+  //     return context.runtime.getNil();
+  // }
+
+  /*
+   * call-seq:
+   *     FieldDescriptor.clear(message)
+   *
+   * Clears the field from the message if it's set.
+   */
+  @JRubyMethod(name = "clear")
+  public IRubyObject clearValue(ThreadContext context, IRubyObject message) {
+    return ((RubyMessage) message).clearField(context, descriptor);
+  }
+
+  /*
+   * call-seq:
+   *     FieldDescriptor.get(message) => value
+   *
+   * Returns the value set for this field on the given message. Raises an
+   * exception if message is of the wrong type.
+   */
+  @JRubyMethod(name = "get")
+  public IRubyObject getValue(ThreadContext context, IRubyObject message) {
+    return ((RubyMessage) message).getField(context, descriptor);
+  }
+
+  /*
+   * call-seq:
+   *     FieldDescriptor.has?(message) => boolean
+   *
+   * Returns whether the value is set on the given message. Raises an
+   * exception when calling for fields that do not have presence.
+   */
+  @JRubyMethod(name = "has?")
+  public IRubyObject has(ThreadContext context, IRubyObject message) {
+    return ((RubyMessage) message).hasField(context, descriptor);
+  }
+
+  /*
+   * call-seq:
+   *     FieldDescriptor.set(message, value)
+   *
+   * Sets the value corresponding to this field to the given value on the given
+   * message. Raises an exception if message is of the wrong type. Performs the
+   * ordinary type-checks for field setting.
+   */
+  @JRubyMethod(name = "set")
+  public IRubyObject setValue(ThreadContext context, IRubyObject message, IRubyObject value) {
+    ((RubyMessage) message).setField(context, descriptor, value);
+    return context.nil;
+  }
+
+  protected void setDescriptor(
+      ThreadContext context, FieldDescriptor descriptor, RubyDescriptorPool pool) {
+    if (descriptor.isRequired()
+        && descriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3) {
+      throw Utils.createTypeError(
+          context,
+          descriptor.getName()
+              + " is labeled required but required fields are unsupported in proto3");
     }
+    this.descriptor = descriptor;
+    this.name = context.runtime.newString(descriptor.getName());
+    this.pool = pool;
+  }
 
-    /*
-     * call-seq:
-     *     FieldDescriptor.name => name
-     *
-     * Returns the name of this field as a Ruby String, or nil if it is not set.
-     */
-    @JRubyMethod(name = "name")
-    public IRubyObject getName(ThreadContext context) {
-        return this.name;
+  private void calculateLabel(ThreadContext context) {
+    if (descriptor.isRepeated()) {
+      this.label = context.runtime.newSymbol("repeated");
+    } else if (descriptor.isOptional()) {
+      this.label = context.runtime.newSymbol("optional");
+    } else {
+      this.label = context.nil;
     }
+  }
 
-    /*
-     * call-seq:
-     *     FieldDescriptor.subtype => message_or_enum_descriptor
-     *
-     * Returns the message or enum descriptor corresponding to this field's type if
-     * it is a message or enum field, respectively, or nil otherwise. Cannot be
-     * called *until* the containing message type is added to a pool (and thus
-     * resolved).
-     */
-    @JRubyMethod(name = "subtype")
-    public IRubyObject getSubtype(ThreadContext context) {
-        if (subtype == null) {
-            calculateSubtype(context);
-        }
-        return subtype;
+  private void calculateSubtype(ThreadContext context) {
+    FieldDescriptor.Type fdType = descriptor.getType();
+    if (fdType == FieldDescriptor.Type.MESSAGE) {
+      RubyString messageName = context.runtime.newString(descriptor.getMessageType().getFullName());
+      this.subtype = pool.lookup(context, messageName);
+    } else if (fdType == FieldDescriptor.Type.ENUM) {
+      RubyString enumName = context.runtime.newString(descriptor.getEnumType().getFullName());
+      this.subtype = pool.lookup(context, enumName);
+    } else {
+      this.subtype = context.nil;
     }
+  }
 
-    /*
-     * call-seq:
-     *     FieldDescriptor.type => type
-     *
-     * Returns this field's type, as a Ruby symbol, or nil if not yet set.
-     *
-     * Valid field types are:
-     *     :int32, :int64, :uint32, :uint64, :float, :double, :bool, :string,
-     *     :bytes, :message.
-     */
-    @JRubyMethod(name = "type")
-    public IRubyObject getType(ThreadContext context) {
-        return Utils.fieldTypeToRuby(context, descriptor.getType());
-    }
+  private static final String DOT = ".";
 
-    /*
-     * call-seq:
-     *     FieldDescriptor.number => number
-     *
-     * Returns the tag number for this field.
-     */
-    @JRubyMethod(name = "number")
-    public IRubyObject getNumber(ThreadContext context) {
-        return this.number;
-    }
-
-    /*
-     * call-seq:
-     *     FieldDescriptor.submsg_name => submsg_name
-     *
-     * Returns the name of the message or enum type corresponding to this field, if
-     * it is a message or enum field (respectively), or nil otherwise. This type
-     * name will be resolved within the context of the pool to which the containing
-     * message type is added.
-     */
-    // VALUE FieldDescriptor_submsg_name(VALUE _self) {
-    //   DEFINE_SELF(FieldDescriptor, self, _self);
-    //   switch (upb_fielddef_type(self->fielddef)) {
-    //     case UPB_TYPE_ENUM:
-    //       return rb_str_new2(
-    //           upb_enumdef_fullname(upb_fielddef_enumsubdef(self->fielddef)));
-    //     case UPB_TYPE_MESSAGE:
-    //       return rb_str_new2(
-    //           upb_msgdef_fullname(upb_fielddef_msgsubdef(self->fielddef)));
-    //     default:
-    //       return Qnil;
-    //   }
-    // }
-    /*
-     * call-seq:
-     *     FieldDescriptor.submsg_name = submsg_name
-     *
-     * Sets the name of the message or enum type corresponding to this field, if it
-     * is a message or enum field (respectively). This type name will be resolved
-     * within the context of the pool to which the containing message type is added.
-     * Cannot be called on field that are not of message or enum type, or on fields
-     * that are part of a message type already added to a pool.
-     */
-    // @JRubyMethod(name = "submsg_name=")
-    // public IRubyObject setSubmsgName(ThreadContext context, IRubyObject name) {
-    //     this.builder.setTypeName("." + Utils.escapeIdentifier(name.asJavaString()));
-    //     return context.runtime.getNil();
-    // }
-
-    /*
-     * call-seq:
-     *     FieldDescriptor.clear(message)
-     *
-     * Clears the field from the message if it's set.
-     */
-    @JRubyMethod(name = "clear")
-    public IRubyObject clearValue(ThreadContext context, IRubyObject message) {
-        return ((RubyMessage) message).clearField(context, descriptor);
-    }
-
-    /*
-     * call-seq:
-     *     FieldDescriptor.get(message) => value
-     *
-     * Returns the value set for this field on the given message. Raises an
-     * exception if message is of the wrong type.
-     */
-    @JRubyMethod(name = "get")
-    public IRubyObject getValue(ThreadContext context, IRubyObject message) {
-        return ((RubyMessage) message).getField(context, descriptor);
-    }
-
-    /*
-     * call-seq:
-     *     FieldDescriptor.has?(message) => boolean
-     *
-     * Returns whether the value is set on the given message. Raises an
-     * exception when calling for fields that do not have presence.
-     */
-     @JRubyMethod(name = "has?")
-     public IRubyObject has(ThreadContext context, IRubyObject message) {
-        return ((RubyMessage) message).hasField(context, descriptor);
-     }
-
-    /*
-     * call-seq:
-     *     FieldDescriptor.set(message, value)
-     *
-     * Sets the value corresponding to this field to the given value on the given
-     * message. Raises an exception if message is of the wrong type. Performs the
-     * ordinary type-checks for field setting.
-     */
-    @JRubyMethod(name = "set")
-    public IRubyObject setValue(ThreadContext context, IRubyObject message, IRubyObject value) {
-        ((RubyMessage) message).setField(context, descriptor, value);
-        return context.nil;
-    }
-
-    protected void setDescriptor(ThreadContext context, FieldDescriptor descriptor, RubyDescriptorPool pool) {
-        if (descriptor.isRequired() && descriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3) {
-            throw Utils.createTypeError(context, descriptor.getName() + " is labeled required but required fields are unsupported in proto3");
-        }
-        this.descriptor = descriptor;
-        this.name = context.runtime.newString(descriptor.getName());
-        this.pool = pool;
-    }
-
-    private void calculateLabel(ThreadContext context) {
-        if (descriptor.isRepeated()) {
-            this.label = context.runtime.newSymbol("repeated");
-        } else if (descriptor.isOptional()) {
-            this.label = context.runtime.newSymbol("optional");
-        } else {
-            this.label = context.nil;
-        }
-    }
-
-    private void calculateSubtype(ThreadContext context) {
-        FieldDescriptor.Type fdType = descriptor.getType();
-        if (fdType == FieldDescriptor.Type.MESSAGE) {
-            RubyString messageName = context.runtime.newString(descriptor.getMessageType().getFullName());
-            this.subtype = pool.lookup(context, messageName);
-        } else if (fdType == FieldDescriptor.Type.ENUM) {
-            RubyString enumName = context.runtime.newString(descriptor.getEnumType().getFullName());
-            this.subtype = pool.lookup(context, enumName);
-        } else {
-            this.subtype = context.nil;
-        }
-    }
-
-    private static final String DOT = ".";
-
-    private FieldDescriptor descriptor;
-    private IRubyObject name;
-    private IRubyObject label;
-    private IRubyObject number;
-    private IRubyObject subtype;
-    private RubyDescriptorPool pool;
+  private FieldDescriptor descriptor;
+  private IRubyObject name;
+  private IRubyObject label;
+  private IRubyObject number;
+  private IRubyObject subtype;
+  private RubyDescriptorPool pool;
 }
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyFileDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyFileDescriptor.java
index b3e1816..972510b 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyFileDescriptor.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyFileDescriptor.java
@@ -32,7 +32,6 @@
 
 package com.google.protobuf.jruby;
 
-import com.google.protobuf.Descriptors.Descriptor;
 import com.google.protobuf.Descriptors.FileDescriptor;
 import com.google.protobuf.Descriptors.FileDescriptor.Syntax.*;
 import com.google.protobuf.Descriptors.GenericDescriptor;
@@ -46,61 +45,67 @@
 
 @JRubyClass(name = "FileDescriptor")
 public class RubyFileDescriptor extends RubyObject {
-    public static void createRubyFileDescriptor(Ruby runtime) {
-        RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
-        cFileDescriptor = mProtobuf.defineClassUnder("FileDescriptor", runtime.getObject(), new ObjectAllocator() {
-            @Override
-            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+  public static void createRubyFileDescriptor(Ruby runtime) {
+    RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
+    cFileDescriptor =
+        mProtobuf.defineClassUnder(
+            "FileDescriptor",
+            runtime.getObject(),
+            new ObjectAllocator() {
+              @Override
+              public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
                 return new RubyFileDescriptor(runtime, klazz);
-            }
-        });
-        cFileDescriptor.defineAnnotatedMethods(RubyFileDescriptor.class);
+              }
+            });
+    cFileDescriptor.defineAnnotatedMethods(RubyFileDescriptor.class);
+  }
+
+  public static RubyFileDescriptor getRubyFileDescriptor(
+      ThreadContext context, GenericDescriptor descriptor) {
+    RubyFileDescriptor rfd =
+        (RubyFileDescriptor) cFileDescriptor.newInstance(context, Block.NULL_BLOCK);
+    rfd.fileDescriptor = descriptor.getFile();
+    return rfd;
+  }
+
+  public RubyFileDescriptor(Ruby runtime, RubyClass klazz) {
+    super(runtime, klazz);
+  }
+
+  /*
+   * call-seq:
+   *     FileDescriptor.name => name
+   *
+   * Returns the name of the file.
+   */
+  @JRubyMethod(name = "name")
+  public IRubyObject getName(ThreadContext context) {
+    String name = fileDescriptor.getName();
+    return name == null ? context.nil : context.runtime.newString(name);
+  }
+
+  /*
+   * call-seq:
+   *     FileDescriptor.syntax => syntax
+   *
+   * Returns this file descriptors syntax.
+   *
+   * Valid syntax versions are:
+   *     :proto2 or :proto3.
+   */
+  @JRubyMethod(name = "syntax")
+  public IRubyObject getSyntax(ThreadContext context) {
+    switch (fileDescriptor.getSyntax()) {
+      case PROTO2:
+        return context.runtime.newSymbol("proto2");
+      case PROTO3:
+        return context.runtime.newSymbol("proto3");
+      default:
+        return context.nil;
     }
+  }
 
-    public static RubyFileDescriptor getRubyFileDescriptor(ThreadContext context, GenericDescriptor descriptor) {
-        RubyFileDescriptor rfd = (RubyFileDescriptor) cFileDescriptor.newInstance(context, Block.NULL_BLOCK);
-        rfd.fileDescriptor = descriptor.getFile();
-        return rfd;
-    }
+  private static RubyClass cFileDescriptor;
 
-    public RubyFileDescriptor(Ruby runtime, RubyClass klazz) {
-        super(runtime, klazz);
-    }
-
-    /*
-     * call-seq:
-     *     FileDescriptor.name => name
-     *
-     * Returns the name of the file.
-     */
-    @JRubyMethod(name = "name")
-    public IRubyObject getName(ThreadContext context) {
-        String name = fileDescriptor.getName();
-        return name == null ? context.nil : context.runtime.newString(name);
-    }
-
-    /*
-     * call-seq:
-     *     FileDescriptor.syntax => syntax
-     *
-     * Returns this file descriptors syntax.
-     *
-     * Valid syntax versions are:
-     *     :proto2 or :proto3.
-     */
-    @JRubyMethod(name = "syntax")
-    public IRubyObject getSyntax(ThreadContext context) {
-        switch (fileDescriptor.getSyntax()) {
-            case PROTO2:
-                return context.runtime.newSymbol("proto2");
-            case PROTO3:
-                return context.runtime.newSymbol("proto3");
-            default:
-                return context.nil;
-        }
-    }
-
-    private static RubyClass cFileDescriptor;
-
-    private FileDescriptor fileDescriptor;
+  private FileDescriptor fileDescriptor;
 }
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java
index 7956eeb..8727b13 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java
@@ -34,6 +34,13 @@
 
 import com.google.protobuf.Descriptors.FieldDescriptor;
 import com.google.protobuf.DynamicMessage;
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import org.jruby.*;
 import org.jruby.anno.JRubyClass;
 import org.jruby.anno.JRubyMethod;
@@ -43,432 +50,438 @@
 import org.jruby.runtime.ThreadContext;
 import org.jruby.runtime.builtin.IRubyObject;
 
-import java.nio.ByteBuffer;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 @JRubyClass(name = "Map", include = "Enumerable")
 public class RubyMap extends RubyObject {
-    public static void createRubyMap(Ruby runtime) {
-        RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
-        RubyClass cMap = protobuf.defineClassUnder("Map", runtime.getObject(), new ObjectAllocator() {
-            @Override
-            public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
+  public static void createRubyMap(Ruby runtime) {
+    RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
+    RubyClass cMap =
+        protobuf.defineClassUnder(
+            "Map",
+            runtime.getObject(),
+            new ObjectAllocator() {
+              @Override
+              public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
                 return new RubyMap(ruby, rubyClass);
+              }
+            });
+    cMap.includeModule(runtime.getEnumerable());
+    cMap.defineAnnotatedMethods(RubyMap.class);
+  }
+
+  public RubyMap(Ruby ruby, RubyClass rubyClass) {
+    super(ruby, rubyClass);
+  }
+
+  /*
+   * call-seq:
+   *     Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {})
+   *     => new map
+   *
+   * Allocates a new Map container. This constructor may be called with 2, 3, or 4
+   * arguments. The first two arguments are always present and are symbols (taking
+   * on the same values as field-type symbols in message descriptors) that
+   * indicate the type of the map key and value fields.
+   *
+   * The supported key types are: :int32, :int64, :uint32, :uint64, :fixed32,
+   * :fixed64, :sfixed32, :sfixed64, :sint32, :sint64, :bool, :string, :bytes.
+   *
+   * The supported value types are: :int32, :int64, :uint32, :uint64, :fixed32,
+   * :fixed64, :sfixed32, :sfixed64, :sint32, :sint64, :bool, :string, :bytes,
+   * :enum, :message.
+   *
+   * The third argument, value_typeclass, must be present if value_type is :enum
+   * or :message. As in RepeatedField#new, this argument must be a message class
+   * (for :message) or enum module (for :enum).
+   *
+   * The last argument, if present, provides initial content for map. Note that
+   * this may be an ordinary Ruby hashmap or another Map instance with identical
+   * key and value types. Also note that this argument may be present whether or
+   * not value_typeclass is present (and it is unambiguously separate from
+   * value_typeclass because value_typeclass's presence is strictly determined by
+   * value_type). The contents of this initial hashmap or Map instance are
+   * shallow-copied into the new Map: the original map is unmodified, but
+   * references to underlying objects will be shared if the value type is a
+   * message type.
+   */
+  @JRubyMethod(required = 2, optional = 2)
+  public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
+    this.table = new HashMap<IRubyObject, IRubyObject>();
+    this.keyType = Utils.rubyToFieldType(args[0]);
+    this.valueType = Utils.rubyToFieldType(args[1]);
+
+    switch (keyType) {
+      case STRING:
+      case BYTES:
+        this.keyTypeIsString = true;
+        break;
+      case INT32:
+      case INT64:
+      case SINT32:
+      case SINT64:
+      case UINT32:
+      case UINT64:
+      case FIXED32:
+      case FIXED64:
+      case SFIXED32:
+      case SFIXED64:
+      case BOOL:
+        // These are OK.
+        break;
+      default:
+        throw context.runtime.newArgumentError("Invalid key type for map.");
+    }
+
+    int initValueArg = 2;
+    if (needTypeclass(this.valueType) && args.length > 2) {
+      this.valueTypeClass = args[2];
+      Utils.validateTypeClass(context, this.valueType, this.valueTypeClass);
+      initValueArg = 3;
+    } else {
+      this.valueTypeClass = context.runtime.getNilClass();
+    }
+
+    if (args.length > initValueArg) {
+      mergeIntoSelf(context, args[initValueArg]);
+    }
+    return this;
+  }
+
+  /*
+   * call-seq:
+   *     Map.[]=(key, value) => value
+   *
+   * Inserts or overwrites the value at the given key with the given new value.
+   * Throws an exception if the key type is incorrect. Returns the new value that
+   * was just inserted.
+   */
+  @JRubyMethod(name = "[]=")
+  public IRubyObject indexSet(ThreadContext context, IRubyObject key, IRubyObject value) {
+    checkFrozen();
+
+    /*
+     * String types for keys return a different error than
+     * other types for keys, so deal with them specifically first
+     */
+    if (keyTypeIsString && !(key instanceof RubySymbol || key instanceof RubyString)) {
+      throw Utils.createTypeError(context, "Expected string for map key");
+    }
+    key = Utils.checkType(context, keyType, "key", key, (RubyModule) valueTypeClass);
+    value = Utils.checkType(context, valueType, "value", value, (RubyModule) valueTypeClass);
+    IRubyObject symbol;
+    if (valueType == FieldDescriptor.Type.ENUM
+        && Utils.isRubyNum(value)
+        && !(symbol = RubyEnum.lookup(context, valueTypeClass, value)).isNil()) {
+      value = symbol;
+    }
+    this.table.put(key, value);
+    return value;
+  }
+
+  /*
+   * call-seq:
+   *     Map.[](key) => value
+   *
+   * Accesses the element at the given key. Throws an exception if the key type is
+   * incorrect. Returns nil when the key is not present in the map.
+   */
+  @JRubyMethod(name = "[]")
+  public IRubyObject index(ThreadContext context, IRubyObject key) {
+    key = Utils.symToString(key);
+    return Helpers.nullToNil(table.get(key), context.nil);
+  }
+
+  /*
+   * call-seq:
+   *     Map.==(other) => boolean
+   *
+   * Compares this map to another. Maps are equal if they have identical key sets,
+   * and for each key, the values in both maps compare equal. Elements are
+   * compared as per normal Ruby semantics, by calling their :== methods (or
+   * performing a more efficient comparison for primitive types).
+   *
+   * Maps with dissimilar key types or value types/typeclasses are never equal,
+   * even if value comparison (for example, between integers and floats) would
+   * have otherwise indicated that every element has equal value.
+   */
+  @JRubyMethod(name = "==")
+  public IRubyObject eq(ThreadContext context, IRubyObject _other) {
+    if (_other instanceof RubyHash) return singleLevelHash(context).op_equal(context, _other);
+    RubyMap other = (RubyMap) _other;
+    if (this == other) return context.runtime.getTrue();
+    if (!typeCompatible(other) || this.table.size() != other.table.size())
+      return context.runtime.getFalse();
+    for (IRubyObject key : table.keySet()) {
+      if (!other.table.containsKey(key)) return context.runtime.getFalse();
+      if (!other.table.get(key).equals(table.get(key))) return context.runtime.getFalse();
+    }
+    return context.runtime.getTrue();
+  }
+
+  /*
+   * call-seq:
+   *     Map.inspect => string
+   *
+   * Returns a string representing this map's elements. It will be formatted as
+   * "{key => value, key => value, ...}", with each key and value string
+   * representation computed by its own #inspect method.
+   */
+  @JRubyMethod
+  public IRubyObject inspect() {
+    return singleLevelHash(getRuntime().getCurrentContext()).inspect();
+  }
+
+  /*
+   * call-seq:
+   *     Map.hash => hash_value
+   *
+   * Returns a hash value based on this map's contents.
+   */
+  @JRubyMethod
+  public IRubyObject hash(ThreadContext context) {
+    try {
+      MessageDigest digest = MessageDigest.getInstance("SHA-256");
+      for (IRubyObject key : table.keySet()) {
+        digest.update((byte) key.hashCode());
+        digest.update((byte) table.get(key).hashCode());
+      }
+      return context.runtime.newFixnum(ByteBuffer.wrap(digest.digest()).getLong());
+    } catch (NoSuchAlgorithmException ignore) {
+      return context.runtime.newFixnum(System.identityHashCode(table));
+    }
+  }
+
+  /*
+   * call-seq:
+   *     Map.keys => [list_of_keys]
+   *
+   * Returns the list of keys contained in the map, in unspecified order.
+   */
+  @JRubyMethod
+  public IRubyObject keys(ThreadContext context) {
+    return RubyArray.newArray(context.runtime, table.keySet());
+  }
+
+  /*
+   * call-seq:
+   *     Map.values => [list_of_values]
+   *
+   * Returns the list of values contained in the map, in unspecified order.
+   */
+  @JRubyMethod
+  public IRubyObject values(ThreadContext context) {
+    return RubyArray.newArray(context.runtime, table.values());
+  }
+
+  /*
+   * call-seq:
+   *     Map.clear
+   *
+   * Removes all entries from the map.
+   */
+  @JRubyMethod
+  public IRubyObject clear(ThreadContext context) {
+    checkFrozen();
+    table.clear();
+    return context.nil;
+  }
+
+  /*
+   * call-seq:
+   *     Map.each(&block)
+   *
+   * Invokes &block on each |key, value| pair in the map, in unspecified order.
+   * Note that Map also includes Enumerable; map thus acts like a normal Ruby
+   * sequence.
+   */
+  @JRubyMethod
+  public IRubyObject each(ThreadContext context, Block block) {
+    for (IRubyObject key : table.keySet()) {
+      block.yieldSpecific(context, key, table.get(key));
+    }
+    return context.nil;
+  }
+
+  /*
+   * call-seq:
+   *     Map.delete(key) => old_value
+   *
+   * Deletes the value at the given key, if any, returning either the old value or
+   * nil if none was present. Throws an exception if the key is of the wrong type.
+   */
+  @JRubyMethod
+  public IRubyObject delete(ThreadContext context, IRubyObject key) {
+    checkFrozen();
+    return table.remove(key);
+  }
+
+  /*
+   * call-seq:
+   *     Map.has_key?(key) => bool
+   *
+   * Returns true if the given key is present in the map. Throws an exception if
+   * the key has the wrong type.
+   */
+  @JRubyMethod(name = "has_key?")
+  public IRubyObject hasKey(ThreadContext context, IRubyObject key) {
+    return this.table.containsKey(key) ? context.runtime.getTrue() : context.runtime.getFalse();
+  }
+
+  /*
+   * call-seq:
+   *     Map.length
+   *
+   * Returns the number of entries (key-value pairs) in the map.
+   */
+  @JRubyMethod(name = {"length", "size"})
+  public IRubyObject length(ThreadContext context) {
+    return context.runtime.newFixnum(this.table.size());
+  }
+
+  /*
+   * call-seq:
+   *     Map.dup => new_map
+   *
+   * Duplicates this map with a shallow copy. References to all non-primitive
+   * element objects (e.g., submessages) are shared.
+   */
+  @JRubyMethod
+  public IRubyObject dup(ThreadContext context) {
+    RubyMap newMap = newThisType(context);
+    for (Map.Entry<IRubyObject, IRubyObject> entry : table.entrySet()) {
+      newMap.table.put(entry.getKey(), entry.getValue());
+    }
+    return newMap;
+  }
+
+  @JRubyMethod(name = "to_h")
+  public RubyHash toHash(ThreadContext context) {
+    Map<IRubyObject, IRubyObject> mapForHash = new HashMap();
+
+    table.forEach(
+        (key, value) -> {
+          if (!value.isNil()) {
+            if (value.respondsTo("to_h")) {
+              value = Helpers.invoke(context, value, "to_h");
+            } else if (value.respondsTo("to_a")) {
+              value = Helpers.invoke(context, value, "to_a");
             }
-        });
-        cMap.includeModule(runtime.getEnumerable());
-        cMap.defineAnnotatedMethods(RubyMap.class);
-    }
-
-    public RubyMap(Ruby ruby, RubyClass rubyClass) {
-        super(ruby, rubyClass);
-    }
-
-    /*
-     * call-seq:
-     *     Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {})
-     *     => new map
-     *
-     * Allocates a new Map container. This constructor may be called with 2, 3, or 4
-     * arguments. The first two arguments are always present and are symbols (taking
-     * on the same values as field-type symbols in message descriptors) that
-     * indicate the type of the map key and value fields.
-     *
-     * The supported key types are: :int32, :int64, :uint32, :uint64, :fixed32,
-     * :fixed64, :sfixed32, :sfixed64, :sint32, :sint64, :bool, :string, :bytes.
-     *
-     * The supported value types are: :int32, :int64, :uint32, :uint64, :fixed32,
-     * :fixed64, :sfixed32, :sfixed64, :sint32, :sint64, :bool, :string, :bytes,
-     * :enum, :message.
-     *
-     * The third argument, value_typeclass, must be present if value_type is :enum
-     * or :message. As in RepeatedField#new, this argument must be a message class
-     * (for :message) or enum module (for :enum).
-     *
-     * The last argument, if present, provides initial content for map. Note that
-     * this may be an ordinary Ruby hashmap or another Map instance with identical
-     * key and value types. Also note that this argument may be present whether or
-     * not value_typeclass is present (and it is unambiguously separate from
-     * value_typeclass because value_typeclass's presence is strictly determined by
-     * value_type). The contents of this initial hashmap or Map instance are
-     * shallow-copied into the new Map: the original map is unmodified, but
-     * references to underlying objects will be shared if the value type is a
-     * message type.
-     */
-    @JRubyMethod(required = 2, optional = 2)
-    public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
-        this.table = new HashMap<IRubyObject, IRubyObject>();
-        this.keyType = Utils.rubyToFieldType(args[0]);
-        this.valueType = Utils.rubyToFieldType(args[1]);
-
-        switch(keyType) {
-            case STRING:
-            case BYTES:
-                this.keyTypeIsString = true;
-                break;
-            case INT32:
-            case INT64:
-            case SINT32:
-            case SINT64:
-            case UINT32:
-            case UINT64:
-            case FIXED32:
-            case FIXED64:
-            case SFIXED32:
-            case SFIXED64:
-            case BOOL:
-                // These are OK.
-                break;
-            default:
-                throw context.runtime.newArgumentError("Invalid key type for map.");
-        }
-
-        int initValueArg = 2;
-        if (needTypeclass(this.valueType) && args.length > 2) {
-            this.valueTypeClass = args[2];
-            Utils.validateTypeClass(context, this.valueType, this.valueTypeClass);
-            initValueArg = 3;
-        } else {
-            this.valueTypeClass = context.runtime.getNilClass();
-        }
-
-        if (args.length > initValueArg) {
-            mergeIntoSelf(context, args[initValueArg]);
-        }
-        return this;
-    }
-
-    /*
-     * call-seq:
-     *     Map.[]=(key, value) => value
-     *
-     * Inserts or overwrites the value at the given key with the given new value.
-     * Throws an exception if the key type is incorrect. Returns the new value that
-     * was just inserted.
-     */
-    @JRubyMethod(name = "[]=")
-    public IRubyObject indexSet(ThreadContext context, IRubyObject key, IRubyObject value) {
-        checkFrozen();
-
-        /*
-         * String types for keys return a different error than
-         * other types for keys, so deal with them specifically first
-         */
-        if (keyTypeIsString && !(key instanceof RubySymbol || key instanceof RubyString)) {
-            throw Utils.createTypeError(context, "Expected string for map key");
-        }
-        key = Utils.checkType(context, keyType, "key", key, (RubyModule) valueTypeClass);
-        value = Utils.checkType(context, valueType, "value", value, (RubyModule) valueTypeClass);
-        IRubyObject symbol;
-        if (valueType == FieldDescriptor.Type.ENUM &&
-                Utils.isRubyNum(value) &&
-                ! (symbol = RubyEnum.lookup(context, valueTypeClass, value)).isNil()) {
-            value = symbol;
-        }
-        this.table.put(key, value);
-        return value;
-    }
-
-    /*
-     * call-seq:
-     *     Map.[](key) => value
-     *
-     * Accesses the element at the given key. Throws an exception if the key type is
-     * incorrect. Returns nil when the key is not present in the map.
-     */
-    @JRubyMethod(name = "[]")
-    public IRubyObject index(ThreadContext context, IRubyObject key) {
-        key = Utils.symToString(key);
-        return Helpers.nullToNil(table.get(key), context.nil);
-    }
-
-    /*
-     * call-seq:
-     *     Map.==(other) => boolean
-     *
-     * Compares this map to another. Maps are equal if they have identical key sets,
-     * and for each key, the values in both maps compare equal. Elements are
-     * compared as per normal Ruby semantics, by calling their :== methods (or
-     * performing a more efficient comparison for primitive types).
-     *
-     * Maps with dissimilar key types or value types/typeclasses are never equal,
-     * even if value comparison (for example, between integers and floats) would
-     * have otherwise indicated that every element has equal value.
-     */
-    @JRubyMethod(name = "==")
-    public IRubyObject eq(ThreadContext context, IRubyObject _other) {
-        if (_other instanceof RubyHash)
-            return singleLevelHash(context).op_equal(context, _other);
-        RubyMap other = (RubyMap) _other;
-        if (this == other) return context.runtime.getTrue();
-        if (!typeCompatible(other) || this.table.size() != other.table.size())
-            return context.runtime.getFalse();
-        for (IRubyObject key : table.keySet()) {
-            if (! other.table.containsKey(key))
-                return context.runtime.getFalse();
-            if (! other.table.get(key).equals(table.get(key)))
-                return context.runtime.getFalse();
-        }
-        return context.runtime.getTrue();
-    }
-
-    /*
-     * call-seq:
-     *     Map.inspect => string
-     *
-     * Returns a string representing this map's elements. It will be formatted as
-     * "{key => value, key => value, ...}", with each key and value string
-     * representation computed by its own #inspect method.
-     */
-    @JRubyMethod
-    public IRubyObject inspect() {
-        return singleLevelHash(getRuntime().getCurrentContext()).inspect();
-    }
-
-    /*
-     * call-seq:
-     *     Map.hash => hash_value
-     *
-     * Returns a hash value based on this map's contents.
-     */
-    @JRubyMethod
-    public IRubyObject hash(ThreadContext context) {
-        try {
-            MessageDigest digest = MessageDigest.getInstance("SHA-256");
-            for (IRubyObject key : table.keySet()) {
-                digest.update((byte) key.hashCode());
-                digest.update((byte) table.get(key).hashCode());
-            }
-            return context.runtime.newFixnum(ByteBuffer.wrap(digest.digest()).getLong());
-        } catch (NoSuchAlgorithmException ignore) {
-            return context.runtime.newFixnum(System.identityHashCode(table));
-        }
-    }
-
-    /*
-     * call-seq:
-     *     Map.keys => [list_of_keys]
-     *
-     * Returns the list of keys contained in the map, in unspecified order.
-     */
-    @JRubyMethod
-    public IRubyObject keys(ThreadContext context) {
-        return RubyArray.newArray(context.runtime, table.keySet());
-    }
-
-    /*
-     * call-seq:
-     *     Map.values => [list_of_values]
-     *
-     * Returns the list of values contained in the map, in unspecified order.
-     */
-    @JRubyMethod
-    public IRubyObject values(ThreadContext context) {
-        return RubyArray.newArray(context.runtime, table.values());
-    }
-
-    /*
-     * call-seq:
-     *     Map.clear
-     *
-     * Removes all entries from the map.
-     */
-    @JRubyMethod
-    public IRubyObject clear(ThreadContext context) {
-        checkFrozen();
-        table.clear();
-        return context.nil;
-    }
-
-    /*
-     * call-seq:
-     *     Map.each(&block)
-     *
-     * Invokes &block on each |key, value| pair in the map, in unspecified order.
-     * Note that Map also includes Enumerable; map thus acts like a normal Ruby
-     * sequence.
-     */
-    @JRubyMethod
-    public IRubyObject each(ThreadContext context, Block block) {
-        for (IRubyObject key : table.keySet()) {
-            block.yieldSpecific(context, key, table.get(key));
-        }
-        return context.nil;
-    }
-
-    /*
-     * call-seq:
-     *     Map.delete(key) => old_value
-     *
-     * Deletes the value at the given key, if any, returning either the old value or
-     * nil if none was present. Throws an exception if the key is of the wrong type.
-     */
-    @JRubyMethod
-    public IRubyObject delete(ThreadContext context, IRubyObject key) {
-        checkFrozen();
-        return table.remove(key);
-    }
-
-    /*
-     * call-seq:
-     *     Map.has_key?(key) => bool
-     *
-     * Returns true if the given key is present in the map. Throws an exception if
-     * the key has the wrong type.
-     */
-    @JRubyMethod(name = "has_key?")
-    public IRubyObject hasKey(ThreadContext context, IRubyObject key) {
-        return this.table.containsKey(key) ? context.runtime.getTrue() : context.runtime.getFalse();
-    }
-
-    /*
-     * call-seq:
-     *     Map.length
-     *
-     * Returns the number of entries (key-value pairs) in the map.
-     */
-    @JRubyMethod(name = {"length", "size"})
-    public IRubyObject length(ThreadContext context) {
-        return context.runtime.newFixnum(this.table.size());
-    }
-
-    /*
-     * call-seq:
-     *     Map.dup => new_map
-     *
-     * Duplicates this map with a shallow copy. References to all non-primitive
-     * element objects (e.g., submessages) are shared.
-     */
-    @JRubyMethod
-    public IRubyObject dup(ThreadContext context) {
-        RubyMap newMap = newThisType(context);
-        for (Map.Entry<IRubyObject, IRubyObject> entry : table.entrySet()) {
-            newMap.table.put(entry.getKey(), entry.getValue());
-        }
-        return newMap;
-    }
-
-    @JRubyMethod(name = "to_h")
-    public RubyHash toHash(ThreadContext context) {
-        Map<IRubyObject, IRubyObject> mapForHash = new HashMap();
-
-        table.forEach((key, value) -> {
-            if (!value.isNil()) {
-                if (value.respondsTo("to_h")) {
-                    value = Helpers.invoke(context, value, "to_h");
-                } else if (value.respondsTo("to_a")) {
-                    value = Helpers.invoke(context, value, "to_a");
-                }
-                mapForHash.put(key, value);
-            }
+            mapForHash.put(key, value);
+          }
         });
 
-        return RubyHash.newHash(context.runtime, mapForHash, context.nil);
-    }
+    return RubyHash.newHash(context.runtime, mapForHash, context.nil);
+  }
 
-    // Used by Google::Protobuf.deep_copy but not exposed directly.
-    protected IRubyObject deepCopy(ThreadContext context) {
-        RubyMap newMap = newThisType(context);
-        switch (valueType) {
-            case MESSAGE:
-                for (IRubyObject key : table.keySet()) {
-                    RubyMessage message = (RubyMessage) table.get(key);
-                    newMap.table.put(key.dup(), message.deepCopy(context));
-                }
-                break;
-            default:
-                for (IRubyObject key : table.keySet()) {
-                    newMap.table.put(key.dup(), table.get(key).dup());
-                }
-        }
-        return newMap;
-    }
-
-    protected List<DynamicMessage> build(ThreadContext context, RubyDescriptor descriptor, int depth, int recursionLimit) {
-        List<DynamicMessage> list = new ArrayList<DynamicMessage>();
-        RubyClass rubyClass = (RubyClass) descriptor.msgclass(context);
-        FieldDescriptor keyField = descriptor.getField("key");
-        FieldDescriptor valueField = descriptor.getField("value");
+  // Used by Google::Protobuf.deep_copy but not exposed directly.
+  protected IRubyObject deepCopy(ThreadContext context) {
+    RubyMap newMap = newThisType(context);
+    switch (valueType) {
+      case MESSAGE:
         for (IRubyObject key : table.keySet()) {
-            RubyMessage mapMessage = (RubyMessage) rubyClass.newInstance(context, Block.NULL_BLOCK);
-            mapMessage.setField(context, keyField, key);
-            mapMessage.setField(context, valueField, table.get(key));
-            list.add(mapMessage.build(context, depth + 1, recursionLimit));
+          RubyMessage message = (RubyMessage) table.get(key);
+          newMap.table.put(key.dup(), message.deepCopy(context));
         }
-        return list;
+        break;
+      default:
+        for (IRubyObject key : table.keySet()) {
+          newMap.table.put(key.dup(), table.get(key).dup());
+        }
     }
+    return newMap;
+  }
 
-    protected RubyMap mergeIntoSelf(final ThreadContext context, IRubyObject hashmap) {
-        if (hashmap instanceof RubyHash) {
-            ((RubyHash) hashmap).visitAll(context, new RubyHash.Visitor() {
+  protected List<DynamicMessage> build(
+      ThreadContext context, RubyDescriptor descriptor, int depth, int recursionLimit) {
+    List<DynamicMessage> list = new ArrayList<DynamicMessage>();
+    RubyClass rubyClass = (RubyClass) descriptor.msgclass(context);
+    FieldDescriptor keyField = descriptor.getField("key");
+    FieldDescriptor valueField = descriptor.getField("value");
+    for (IRubyObject key : table.keySet()) {
+      RubyMessage mapMessage = (RubyMessage) rubyClass.newInstance(context, Block.NULL_BLOCK);
+      mapMessage.setField(context, keyField, key);
+      mapMessage.setField(context, valueField, table.get(key));
+      list.add(mapMessage.build(context, depth + 1, recursionLimit));
+    }
+    return list;
+  }
+
+  protected RubyMap mergeIntoSelf(final ThreadContext context, IRubyObject hashmap) {
+    if (hashmap instanceof RubyHash) {
+      ((RubyHash) hashmap)
+          .visitAll(
+              context,
+              new RubyHash.Visitor() {
                 @Override
                 public void visit(IRubyObject key, IRubyObject val) {
-                    if (val instanceof RubyHash && !valueTypeClass.isNil()) {
-                        val = ((RubyClass) valueTypeClass).newInstance(context, val, Block.NULL_BLOCK);
-                    }
-                    indexSet(context, key, val);
+                  if (val instanceof RubyHash && !valueTypeClass.isNil()) {
+                    val = ((RubyClass) valueTypeClass).newInstance(context, val, Block.NULL_BLOCK);
+                  }
+                  indexSet(context, key, val);
                 }
-            }, null);
-        } else if (hashmap instanceof RubyMap) {
-            RubyMap other = (RubyMap) hashmap;
-            if (!typeCompatible(other)) {
-                throw Utils.createTypeError(context, "Attempt to merge Map with mismatching types");
-            }
-        } else {
-            throw Utils.createTypeError(context, "Unknown type merging into Map");
-        }
-        return this;
+              },
+              null);
+    } else if (hashmap instanceof RubyMap) {
+      RubyMap other = (RubyMap) hashmap;
+      if (!typeCompatible(other)) {
+        throw Utils.createTypeError(context, "Attempt to merge Map with mismatching types");
+      }
+    } else {
+      throw Utils.createTypeError(context, "Unknown type merging into Map");
     }
+    return this;
+  }
 
-    protected boolean typeCompatible(RubyMap other) {
-        return this.keyType == other.keyType &&
-                this.valueType == other.valueType &&
-                this.valueTypeClass == other.valueTypeClass;
+  protected boolean typeCompatible(RubyMap other) {
+    return this.keyType == other.keyType
+        && this.valueType == other.valueType
+        && this.valueTypeClass == other.valueTypeClass;
+  }
+
+  private RubyMap newThisType(ThreadContext context) {
+    RubyMap newMap;
+    if (needTypeclass(valueType)) {
+      newMap =
+          (RubyMap)
+              metaClass.newInstance(
+                  context,
+                  Utils.fieldTypeToRuby(context, keyType),
+                  Utils.fieldTypeToRuby(context, valueType),
+                  valueTypeClass,
+                  Block.NULL_BLOCK);
+    } else {
+      newMap =
+          (RubyMap)
+              metaClass.newInstance(
+                  context,
+                  Utils.fieldTypeToRuby(context, keyType),
+                  Utils.fieldTypeToRuby(context, valueType),
+                  Block.NULL_BLOCK);
     }
+    newMap.table = new HashMap<IRubyObject, IRubyObject>();
+    return newMap;
+  }
 
-    private RubyMap newThisType(ThreadContext context) {
-        RubyMap newMap;
-        if (needTypeclass(valueType)) {
-            newMap = (RubyMap) metaClass.newInstance(context,
-                    Utils.fieldTypeToRuby(context, keyType),
-                    Utils.fieldTypeToRuby(context, valueType),
-                    valueTypeClass, Block.NULL_BLOCK);
-        } else {
-            newMap = (RubyMap) metaClass.newInstance(context,
-                    Utils.fieldTypeToRuby(context, keyType),
-                    Utils.fieldTypeToRuby(context, valueType),
-                    Block.NULL_BLOCK);
-        }
-        newMap.table = new HashMap<IRubyObject, IRubyObject>();
-        return newMap;
+  /*
+   * toHash calls toHash on values, for some camparisons we only need
+   * a hash with the original objects still as values
+   */
+  private RubyHash singleLevelHash(ThreadContext context) {
+    return RubyHash.newHash(context.runtime, table, context.nil);
+  }
+
+  private boolean needTypeclass(FieldDescriptor.Type type) {
+    switch (type) {
+      case MESSAGE:
+      case ENUM:
+        return true;
+      default:
+        return false;
     }
+  }
 
-    /*
-     * toHash calls toHash on values, for some camparisons we only need
-     * a hash with the original objects still as values
-     */
-    private RubyHash singleLevelHash(ThreadContext context) {
-        return RubyHash.newHash(context.runtime, table, context.nil);
-    }
-
-    private boolean needTypeclass(FieldDescriptor.Type type) {
-        switch(type) {
-            case MESSAGE:
-            case ENUM:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    private FieldDescriptor.Type keyType;
-    private FieldDescriptor.Type valueType;
-    private IRubyObject valueTypeClass;
-    private Map<IRubyObject, IRubyObject> table;
-    private boolean keyTypeIsString = false;
+  private FieldDescriptor.Type keyType;
+  private FieldDescriptor.Type valueType;
+  private IRubyObject valueTypeClass;
+  private Map<IRubyObject, IRubyObject> table;
+  private boolean keyTypeIsString = false;
 }
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java
index 0d76116..301b957 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java
@@ -32,19 +32,25 @@
 
 package com.google.protobuf.jruby;
 
+import com.google.protobuf.ByteString;
+import com.google.protobuf.CodedInputStream;
 import com.google.protobuf.Descriptors.Descriptor;
 import com.google.protobuf.Descriptors.EnumDescriptor;
 import com.google.protobuf.Descriptors.EnumValueDescriptor;
 import com.google.protobuf.Descriptors.FieldDescriptor;
 import com.google.protobuf.Descriptors.FileDescriptor;
 import com.google.protobuf.Descriptors.OneofDescriptor;
-import com.google.protobuf.ByteString;
-import com.google.protobuf.CodedInputStream;
 import com.google.protobuf.DynamicMessage;
 import com.google.protobuf.InvalidProtocolBufferException;
 import com.google.protobuf.Message;
 import com.google.protobuf.UnknownFieldSet;
 import com.google.protobuf.util.JsonFormat;
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import org.jruby.*;
 import org.jruby.anno.JRubyMethod;
 import org.jruby.exceptions.RaiseException;
@@ -54,1212 +60,1405 @@
 import org.jruby.runtime.builtin.IRubyObject;
 import org.jruby.util.ByteList;
 
-import java.nio.ByteBuffer;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 public class RubyMessage extends RubyObject {
-    private final String DEFAULT_VALUE = "google.protobuf.FieldDescriptorProto.default_value";
-    private final String TYPE = "type";
+  private final String DEFAULT_VALUE = "google.protobuf.FieldDescriptorProto.default_value";
+  private final String TYPE = "type";
 
-    public RubyMessage(Ruby runtime, RubyClass klazz, Descriptor descriptor) {
-        super(runtime, klazz);
+  public RubyMessage(Ruby runtime, RubyClass klazz, Descriptor descriptor) {
+    super(runtime, klazz);
 
-        this.descriptor = descriptor;
-        this.cRepeatedField = (RubyClass) runtime.getClassFromPath("Google::Protobuf::RepeatedField");
-        this.cMap = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Map");
-        this.builder = DynamicMessage.newBuilder(descriptor);
-        this.fields = new HashMap<FieldDescriptor, IRubyObject>();
-        this.oneofCases = new HashMap<OneofDescriptor, FieldDescriptor>();
-        this.proto3 = descriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3;
-    }
+    this.descriptor = descriptor;
+    this.cRepeatedField = (RubyClass) runtime.getClassFromPath("Google::Protobuf::RepeatedField");
+    this.cMap = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Map");
+    this.builder = DynamicMessage.newBuilder(descriptor);
+    this.fields = new HashMap<FieldDescriptor, IRubyObject>();
+    this.oneofCases = new HashMap<OneofDescriptor, FieldDescriptor>();
+    this.proto3 = descriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3;
+  }
 
-    /*
-     * call-seq:
-     *     Message.new(kwargs) => new_message
-     *
-     * Creates a new instance of the given message class. Keyword arguments may be
-     * provided with keywords corresponding to field names.
-     *
-     * Note that no literal Message class exists. Only concrete classes per message
-     * type exist, as provided by the #msgclass method on Descriptors after they
-     * have been added to a pool. The method definitions described here on the
-     * Message class are provided on each concrete message class.
-     */
-    @JRubyMethod(optional = 1)
-    public IRubyObject initialize(final ThreadContext context, IRubyObject[] args) {
-        final Ruby runtime = context.runtime;
-        if (args.length == 1) {
-            if (!(args[0] instanceof RubyHash)) {
-                throw runtime.newArgumentError("expected Hash arguments.");
-            }
-            RubyHash hash = args[0].convertToHash();
-            hash.visitAll(context, new RubyHash.Visitor() {
-                @Override
-                public void visit(IRubyObject key, IRubyObject value) {
-                    if (!(key instanceof RubySymbol) && !(key instanceof RubyString)) {
-                        throw Utils.createTypeError(context,
-                            "Expected string or symbols as hash keys in initialization map.");
-                    }
-                    final FieldDescriptor fieldDescriptor = findField(context, key, ignoreUnknownFieldsOnInit);
+  /*
+   * call-seq:
+   *     Message.new(kwargs) => new_message
+   *
+   * Creates a new instance of the given message class. Keyword arguments may be
+   * provided with keywords corresponding to field names.
+   *
+   * Note that no literal Message class exists. Only concrete classes per message
+   * type exist, as provided by the #msgclass method on Descriptors after they
+   * have been added to a pool. The method definitions described here on the
+   * Message class are provided on each concrete message class.
+   */
+  @JRubyMethod(optional = 1)
+  public IRubyObject initialize(final ThreadContext context, IRubyObject[] args) {
+    final Ruby runtime = context.runtime;
+    if (args.length == 1) {
+      if (!(args[0] instanceof RubyHash)) {
+        throw runtime.newArgumentError("expected Hash arguments.");
+      }
+      RubyHash hash = args[0].convertToHash();
+      hash.visitAll(
+          context,
+          new RubyHash.Visitor() {
+            @Override
+            public void visit(IRubyObject key, IRubyObject value) {
+              if (!(key instanceof RubySymbol) && !(key instanceof RubyString)) {
+                throw Utils.createTypeError(
+                    context, "Expected string or symbols as hash keys in initialization map.");
+              }
+              final FieldDescriptor fieldDescriptor =
+                  findField(context, key, ignoreUnknownFieldsOnInit);
 
-                    if (value == null || value.isNil()) return;
+              if (value == null || value.isNil()) return;
 
-                    if (fieldDescriptor.isMapField()) {
-                        if (!(value instanceof RubyHash))
-                            throw runtime.newArgumentError("Expected Hash object as initializer value for map field '" +  key.asJavaString() + "' (given " + value.getMetaClass() + ").");
+              if (fieldDescriptor.isMapField()) {
+                if (!(value instanceof RubyHash))
+                  throw runtime.newArgumentError(
+                      "Expected Hash object as initializer value for map field '"
+                          + key.asJavaString()
+                          + "' (given "
+                          + value.getMetaClass()
+                          + ").");
 
-                        final RubyMap map = newMapForField(context, fieldDescriptor);
-                        map.mergeIntoSelf(context, value);
-                        fields.put(fieldDescriptor, map);
-                    } else if (fieldDescriptor.isRepeated()) {
-                        if (!(value instanceof RubyArray))
-                            throw runtime.newArgumentError("Expected array as initializer value for repeated field '" +  key.asJavaString() + "' (given " + value.getMetaClass() + ").");
-                        fields.put(fieldDescriptor, rubyToRepeatedField(context, fieldDescriptor, value));
-                    } else {
-                        OneofDescriptor oneof = fieldDescriptor.getContainingOneof();
-                        if (oneof != null) {
-                            oneofCases.put(oneof, fieldDescriptor);
-                        }
-
-                        if (value instanceof RubyHash && fieldDescriptor.getType() == FieldDescriptor.Type.MESSAGE) {
-                            RubyDescriptor descriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
-                            RubyClass typeClass = (RubyClass) descriptor.msgclass(context);
-                            value = (IRubyObject) typeClass.newInstance(context, value, Block.NULL_BLOCK);
-                            fields.put(fieldDescriptor, value);
-                        } else {
-                            indexSet(context, key, value);
-                        }
-
-                    }
-                }
-            }, null);
-        }
-        return this;
-    }
-
-    /*
-     * call-seq:
-     *     Message.[]=(index, value)
-     *
-     * Sets a field's value by field name. The provided field name should be a
-     * string.
-     */
-    @JRubyMethod(name = "[]=")
-    public IRubyObject indexSet(ThreadContext context, IRubyObject fieldName, IRubyObject value) {
-        FieldDescriptor fieldDescriptor = findField(context, fieldName);
-        return setFieldInternal(context, fieldDescriptor, value);
-    }
-
-    /*
-     * call-seq:
-     *     Message.[](index) => value
-     *
-     * Accesses a field's value by field name. The provided field name should be a
-     * string.
-     */
-    @JRubyMethod(name = "[]")
-    public IRubyObject index(ThreadContext context, IRubyObject fieldName) {
-        FieldDescriptor fieldDescriptor = findField(context, fieldName);
-        return getFieldInternal(context, fieldDescriptor);
-    }
-
-    /*
-     * call-seq:
-     *     Message.inspect => string
-     *
-     * Returns a human-readable string representing this message. It will be
-     * formatted as "<MessageType: field1: value1, field2: value2, ...>". Each
-     * field's value is represented according to its own #inspect method.
-     */
-    @JRubyMethod(name = {"inspect", "to_s"})
-    public IRubyObject inspect() {
-        ThreadContext context = getRuntime().getCurrentContext();
-        String cname = metaClass.getName();
-        String colon = ": ";
-        String comma = ", ";
-        StringBuilder sb = new StringBuilder("<");
-        boolean addComma = false;
-
-        sb.append(cname).append(colon);
-
-        for (FieldDescriptor fd : descriptor.getFields()) {
-            if (fd.hasPresence() && !fields.containsKey(fd)) {
-                continue;
-            }
-            if (addComma) {
-                sb.append(comma);
-            } else {
-                addComma = true;
-            }
-
-            sb.append(fd.getName()).append(colon);
-
-            IRubyObject value = getFieldInternal(context, fd);
-            if (value instanceof RubyBoolean) {
-                // Booleans don't implement internal "inspect" methods so have to call handle them manually
-                sb.append(value.isTrue() ? "true" : "false");
-            } else {
-                sb.append(value.inspect());
-            }
-        }
-        sb.append(">");
-
-        return context.runtime.newString(sb.toString());
-    }
-
-    /*
-     * call-seq:
-     *     Message.hash => hash_value
-     *
-     * Returns a hash value that represents this message's field values.
-     */
-    @JRubyMethod
-    public IRubyObject hash(ThreadContext context) {
-        try {
-            MessageDigest digest = MessageDigest.getInstance("SHA-256");
-            for (FieldDescriptor fd : descriptor.getFields()) {
-                digest.update((byte) getFieldInternal(context, fd).hashCode());
-            }
-            return context.runtime.newFixnum(ByteBuffer.wrap(digest.digest()).getLong());
-        } catch (NoSuchAlgorithmException ignore) {
-            return context.runtime.newFixnum(System.identityHashCode(this));
-        }
-    }
-
-    /*
-     * call-seq:
-     *     Message.==(other) => boolean
-     *
-     * Performs a deep comparison of this message with another. Messages are equal
-     * if they have the same type and if each field is equal according to the :==
-     * method's semantics (a more efficient comparison may actually be done if the
-     * field is of a primitive type).
-     */
-    @JRubyMethod(name = {"==", "eql?"})
-    public IRubyObject eq(ThreadContext context, IRubyObject other) {
-        Ruby runtime = context.runtime;
-        if (!(other instanceof RubyMessage))
-            return runtime.getFalse();
-        RubyMessage message = (RubyMessage) other;
-        if (descriptor != message.descriptor) {
-            return runtime.getFalse();
-        }
-
-        for (FieldDescriptor fdef : descriptor.getFields()) {
-            IRubyObject thisVal = getFieldInternal(context, fdef);
-            IRubyObject thatVal = message.getFieldInternal(context, fdef);
-            IRubyObject ret = thisVal.callMethod(context, "==", thatVal);
-            if (!ret.isTrue()) {
-                return runtime.getFalse();
-            }
-        }
-        return runtime.getTrue();
-    }
-
-    /*
-     * call-seq:
-     *     Message.method_missing(*args)
-     *
-     * Provides accessors and setters and methods to clear and check for presence of
-     * message fields according to their field names.
-     *
-     * For any field whose name does not conflict with a built-in method, an
-     * accessor is provided with the same name as the field, and a setter is
-     * provided with the name of the field plus the '=' suffix. Thus, given a
-     * message instance 'msg' with field 'foo', the following code is valid:
-     *
-     *     msg.foo = 42
-     *     puts msg.foo
-     *
-     * This method also provides read-only accessors for oneofs. If a oneof exists
-     * with name 'my_oneof', then msg.my_oneof will return a Ruby symbol equal to
-     * the name of the field in that oneof that is currently set, or nil if none.
-     *
-     * It also provides methods of the form 'clear_fieldname' to clear the value
-     * of the field 'fieldname'. For basic data types, this will set the default
-     * value of the field.
-     *
-     * Additionally, it provides methods of the form 'has_fieldname?', which returns
-     * true if the field 'fieldname' is set in the message object, else false. For
-     * 'proto3' syntax, calling this for a basic type field will result in an error.
-     */
-    @JRubyMethod(name = "method_missing", rest = true)
-    public IRubyObject methodMissing(ThreadContext context, IRubyObject[] args) {
-        Ruby runtime = context.runtime;
-        String methodName = args[0].asJavaString();
-
-        if (args.length == 1) {
-            RubyDescriptor rubyDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
-
-            // If we find a Oneof return it's name (use lookupOneof because it has an index)
-            IRubyObject oneofDescriptor = rubyDescriptor.lookupOneof(context, args[0]);
-
-            if (!oneofDescriptor.isNil()) {
-                RubyOneofDescriptor rubyOneofDescriptor = (RubyOneofDescriptor) oneofDescriptor;
-                OneofDescriptor ood = rubyOneofDescriptor.getDescriptor();
-
-                // Check to see if we set this through ruby
-                FieldDescriptor fieldDescriptor = oneofCases.get(ood);
-
-                if (fieldDescriptor == null) {
-                    // See if we set this from decoding a message
-                    fieldDescriptor = builder.getOneofFieldDescriptor(ood);
-
-                    if (fieldDescriptor == null) {
-                        return context.nil;
-                    } else {
-                        // Cache it so we don't need to do multiple checks next time
-                        oneofCases.put(ood, fieldDescriptor);
-                        return runtime.newSymbol(fieldDescriptor.getName());
-                    }
-                } else {
-                    return runtime.newSymbol(fieldDescriptor.getName());
-                }
-            }
-
-            // If we find a field return its value
-            FieldDescriptor fieldDescriptor = descriptor.findFieldByName(methodName);
-
-            if (fieldDescriptor != null) {
-                return getFieldInternal(context, fieldDescriptor);
-            }
-
-            if (methodName.startsWith(CLEAR_PREFIX)) {
-                methodName = methodName.substring(6);
-                oneofDescriptor = rubyDescriptor.lookupOneof(context, runtime.newSymbol(methodName));
-
-                if (!oneofDescriptor.isNil()) {
-                    fieldDescriptor = oneofCases.get(((RubyOneofDescriptor) oneofDescriptor).getDescriptor());
-                }
-
-                if (fieldDescriptor == null) {
-                    fieldDescriptor = descriptor.findFieldByName(methodName);
-                }
-
-                if (fieldDescriptor != null) {
-                    return clearFieldInternal(context, fieldDescriptor);
-                }
-
-            } else if (methodName.startsWith(HAS_PREFIX) && methodName.endsWith(QUESTION_MARK)) {
-                methodName = methodName.substring(4, methodName.length() - 1); // Trim "has_" and "?" off the field name
-                oneofDescriptor = rubyDescriptor.lookupOneof(context, runtime.newSymbol(methodName));
-                if (!oneofDescriptor.isNil()) {
-                    RubyOneofDescriptor rubyOneofDescriptor = (RubyOneofDescriptor) oneofDescriptor;
-                    return oneofCases.containsKey(rubyOneofDescriptor.getDescriptor()) ? runtime.getTrue() : runtime.getFalse();
-                }
-
-                fieldDescriptor = descriptor.findFieldByName(methodName);
-
-                if (fieldDescriptor != null &&
-                    (!proto3 || fieldDescriptor.getContainingOneof() == null || fieldDescriptor
-                        .getContainingOneof().isSynthetic()) &&
-                    fieldDescriptor.hasPresence()) {
-                    return fields.containsKey(fieldDescriptor) ? runtime.getTrue()
-                        : runtime.getFalse();
-                }
-
-            } else if (methodName.endsWith(AS_VALUE_SUFFIX)) {
-                methodName = methodName.substring(0, methodName.length() - 9);
-                fieldDescriptor = descriptor.findFieldByName(methodName);
-
-                if (fieldDescriptor != null && isWrappable(fieldDescriptor)) {
-                    IRubyObject value = getFieldInternal(context, fieldDescriptor);
-
-                    if (!value.isNil() && value instanceof RubyMessage) {
-                        return ((RubyMessage) value).index(context, runtime.newString("value"));
-                    }
-
-                    return value;
-                }
-
-            } else if (methodName.endsWith(CONST_SUFFIX)) {
-                methodName = methodName.substring(0, methodName.length() - 6);
-                fieldDescriptor = descriptor.findFieldByName(methodName);
-
-                if (fieldDescriptor.getType() == FieldDescriptor.Type.ENUM) {
-                    IRubyObject enumValue = getFieldInternal(context, fieldDescriptor);
-
-                    if (!enumValue.isNil()) {
-                        EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType();
-                        if (enumValue instanceof RubyRepeatedField) {
-                            RubyArray values = (RubyArray) ((RubyRepeatedField) enumValue).toArray(context);
-                            RubyArray retValues = runtime.newArray(values.getLength());
-                            for (int i = 0; i < values.getLength(); i++) {
-                                String val = values.eltInternal(i).toString();
-                                retValues.store((long) i, runtime.newFixnum(enumDescriptor.findValueByName(val).getNumber()));
-                            }
-                            return retValues;
-                        }
-
-                        return runtime.newFixnum(enumDescriptor.findValueByName(enumValue.asJavaString()).getNumber());
-                    }
-                }
-            }
-
-        } else if (args.length == 2 && methodName.endsWith(Utils.EQUAL_SIGN)) {
-
-            methodName = methodName.substring(0, methodName.length() - 1); // Trim equals sign
-            FieldDescriptor fieldDescriptor = descriptor.findFieldByName(methodName);
-
-            if (fieldDescriptor != null) {
-                return setFieldInternal(context, fieldDescriptor, args[1]);
-            }
-
-            if (methodName.endsWith(AS_VALUE_SUFFIX)) {
-                methodName = methodName.substring(0, methodName.length() - 9);
-
-                fieldDescriptor = descriptor.findFieldByName(methodName);
-
-                if (fieldDescriptor != null) {
-                    if (args[1].isNil()) {
-                        return setFieldInternal(context, fieldDescriptor, args[1]);
-                    }
-
-                    RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
-                    RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK);
-                    msg.indexSet(context, runtime.newString("value"), args[1]);
-                    return setFieldInternal(context, fieldDescriptor, msg);
-                }
-            }
-
-        }
-
-        return Helpers.invokeSuper(context, this, metaClass, "method_missing", args, Block.NULL_BLOCK);
-    }
-
-    /**
-     * call-seq:
-     * Message.dup => new_message
-     * Performs a shallow copy of this message and returns the new copy.
-     */
-    @JRubyMethod
-    public IRubyObject dup(ThreadContext context) {
-        RubyMessage dup = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK);
-        for (FieldDescriptor fieldDescriptor : this.descriptor.getFields()) {
-            if (fieldDescriptor.isRepeated()) {
-                dup.fields.put(fieldDescriptor, this.getRepeatedField(context, fieldDescriptor));
-            } else if (fields.containsKey(fieldDescriptor)) {
-                dup.setFieldInternal(context, fieldDescriptor, fields.get(fieldDescriptor));
-            } else if (this.builder.hasField(fieldDescriptor)) {
-                dup.fields.put(fieldDescriptor, wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor)));
-            }
-        }
-        return dup;
-    }
-
-    /*
-     * call-seq:
-     *     Message.descriptor => descriptor
-     *
-     * Class method that returns the Descriptor instance corresponding to this
-     * message class's type.
-     */
-    @JRubyMethod(name = "descriptor", meta = true)
-    public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) {
-        return ((RubyClass) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR);
-    }
-
-    /*
-     * call-seq:
-     *     MessageClass.encode(msg, options = {}) => bytes
-     *
-     * Encodes the given message object to its serialized form in protocol buffers
-     * wire format.
-     * @param options [Hash] options for the encoder
-     *  recursion_limit: set to maximum encoding depth for message (default is 64)
-     */
-    @JRubyMethod(required = 1, optional = 1, meta = true)
-    public static IRubyObject encode(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
-        if (recv != args[0].getMetaClass()) {
-            throw context.runtime.newArgumentError("Tried to encode a " + args[0].getMetaClass() + " message with " + recv);
-        }
-        RubyMessage message = (RubyMessage) args[0];
-        int recursionLimitInt = SINK_MAXIMUM_NESTING;
-
-        if (args.length > 1) {
-            RubyHash options = (RubyHash) args[1];
-            IRubyObject recursionLimit = options.fastARef(context.runtime.newSymbol("recursion_limit"));
-
-            if (recursionLimit != null) {
-                recursionLimitInt = ((RubyNumeric) recursionLimit).getIntValue();
-            }
-        }
-        return context.runtime.newString(new ByteList(message.build(context, 0, recursionLimitInt).toByteArray()));
-    }
-
-    /*
-     * call-seq:
-     *     MessageClass.decode(data, options = {}) => message
-     *
-     * Decodes the given data (as a string containing bytes in protocol buffers wire
-     * format) under the interpretation given by this message class's definition
-     * and returns a message object with the corresponding field values.
-     * @param options [Hash] options for the decoder
-     *  recursion_limit: set to maximum decoding depth for message (default is 100)
-     */
-    @JRubyMethod(required = 1, optional = 1, meta = true)
-    public static IRubyObject decode(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
-        IRubyObject data = args[0];
-        byte[] bin = data.convertToString().getBytes();
-        CodedInputStream input = CodedInputStream.newInstance(bin);
-        RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK);
-
-        if (args.length == 2) {
-            if (!(args[1] instanceof RubyHash)) {
-                throw context.runtime.newArgumentError("Expected hash arguments.");
-            }
-
-            IRubyObject recursionLimit = ((RubyHash) args[1]).fastARef(context.runtime.newSymbol("recursion_limit"));
-            if (recursionLimit != null) {
-                input.setRecursionLimit(((RubyNumeric) recursionLimit).getIntValue());
-            }
-        }
-
-        try {
-            ret.builder.mergeFrom(input);
-        } catch (Exception e) {
-            throw RaiseException.from(context.runtime, (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::ParseError"), e.getMessage());
-        }
-
-        if (!ret.proto3) {
-            // Need to reset unknown values in repeated enum fields
-            ret.builder.getUnknownFields().asMap().forEach((i, values) -> {
-                FieldDescriptor fd = ret.builder.getDescriptorForType().findFieldByNumber(i);
-                if (fd != null && fd.isRepeated() && fd.getType() == FieldDescriptor.Type.ENUM) {
-                    EnumDescriptor ed = fd.getEnumType();
-                    values.getVarintList().forEach(value -> {
-                        ret.builder.addRepeatedField(fd, ed.findValueByNumberCreatingIfUnknown(value.intValue()));
-                    });
-                }
-            });
-        }
-
-        return ret;
-    }
-
-    /*
-     * call-seq:
-     *     MessageClass.encode_json(msg, options = {}) => json_string
-     *
-     * Encodes the given message object into its serialized JSON representation.
-     * @param options [Hash] options for the decoder
-     *  preserve_proto_fieldnames: set true to use original fieldnames (default is to camelCase)
-     *  emit_defaults: set true to emit 0/false values (default is to omit them)
-     */
-    @JRubyMethod(name = "encode_json", required = 1, optional = 1, meta = true)
-    public static IRubyObject encodeJson(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
-        Ruby runtime = context.runtime;
-        RubyMessage message = (RubyMessage) args[0];
-        JsonFormat.Printer printer = JsonFormat.printer().omittingInsignificantWhitespace();
-        String result;
-
-        if (args.length > 1) {
-            RubyHash options;
-            if (args[1] instanceof RubyHash) {
-                options = (RubyHash) args[1];
-            } else if (args[1].respondsTo("to_h")) {
-                options = (RubyHash) args[1].callMethod(context, "to_h");
-            } else {
-                throw runtime.newArgumentError("Expected hash arguments.");
-            }
-
-            IRubyObject emitDefaults = options.fastARef(runtime.newSymbol("emit_defaults"));
-            IRubyObject preserveNames = options.fastARef(runtime.newSymbol("preserve_proto_fieldnames"));
-
-            if (emitDefaults != null && emitDefaults.isTrue()) {
-                printer = printer.includingDefaultValueFields();
-            }
-
-            if (preserveNames != null && preserveNames.isTrue()) {
-                printer = printer.preservingProtoFieldNames();
-            }
-        }
-        printer = printer.usingTypeRegistry(JsonFormat.TypeRegistry.newBuilder().add(message.descriptor).build());
-
-        try {
-            result = printer.print(message.build(context, 0, SINK_MAXIMUM_NESTING));
-        } catch (InvalidProtocolBufferException e) {
-            throw runtime.newRuntimeError(e.getMessage());
-        } catch (IllegalArgumentException e) {
-            throw createParseError(context, e.getMessage());
-        }
-
-        return runtime.newString(result);
-    }
-
-    /*
-     * call-seq:
-     *     MessageClass.decode_json(data, options = {}) => message
-     *
-     * Decodes the given data (as a string containing bytes in protocol buffers wire
-     * format) under the interpretation given by this message class's definition
-     * and returns a message object with the corresponding field values.
-     *
-     *  @param options [Hash] options for the decoder
-     *   ignore_unknown_fields: set true to ignore unknown fields (default is to
-     *   raise an error)
-     */
-    @JRubyMethod(name = "decode_json", required = 1, optional = 1, meta = true)
-    public static IRubyObject decodeJson(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
-        Ruby runtime = context.runtime;
-        boolean ignoreUnknownFields = false;
-        IRubyObject data = args[0];
-        JsonFormat.Parser parser = JsonFormat.parser();
-
-        if (args.length == 2) {
-            if (!(args[1] instanceof RubyHash)) {
-                throw runtime.newArgumentError("Expected hash arguments.");
-            }
-
-            IRubyObject ignoreSetting = ((RubyHash) args[1]).fastARef(runtime.newSymbol("ignore_unknown_fields"));
-            if (ignoreSetting != null && ignoreSetting.isTrue()) {
-                parser = parser.ignoringUnknownFields();
-            }
-        }
-
-        if (!(data instanceof RubyString)) {
-            throw runtime.newArgumentError("Expected string for JSON data.");
-        }
-
-        RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK);
-        parser = parser.usingTypeRegistry(JsonFormat.TypeRegistry.newBuilder().add(ret.descriptor).build());
-
-        try {
-            parser.merge(data.asJavaString(), ret.builder);
-        } catch(InvalidProtocolBufferException e) {
-            throw createParseError(context, e.getMessage().replace("Cannot find", "No such"));
-        }
-
-        if (isWrapper(ret.descriptor)) {
-            throw runtime.newRuntimeError("Parsing a wrapper type from JSON at the top level does not work.");
-        }
-
-        return ret;
-    }
-
-    @JRubyMethod(name = "to_h")
-    public IRubyObject toHash(ThreadContext context) {
-        Ruby runtime = context.runtime;
-        RubyHash ret = RubyHash.newHash(runtime);
-        for (FieldDescriptor fdef : this.descriptor.getFields()) {
-            IRubyObject value = getFieldInternal(context, fdef, proto3);
-
-            if (!value.isNil()) {
-                if (fdef.isRepeated() && !fdef.isMapField()) {
-                    if (!proto3 && ((RubyRepeatedField) value).size() == 0) continue; // Don't output empty repeated fields for proto2
-                    if (fdef.getType() != FieldDescriptor.Type.MESSAGE) {
-                        value = Helpers.invoke(context, value, "to_a");
-                    } else {
-                        RubyArray ary = value.convertToArray();
-                        for (int i = 0; i < ary.size(); i++) {
-                            IRubyObject submsg = Helpers.invoke(context, ary.eltInternal(i), "to_h");
-                            ary.eltInternalSet(i, submsg);
-                        }
-
-                        value = ary.to_ary();
-                    }
-                } else if (value.respondsTo("to_h")) {
-                    value = Helpers.invoke(context, value, "to_h");
-                } else if (value.respondsTo("to_a")) {
-                    value = Helpers.invoke(context, value, "to_a");
-                }
-            }
-            if (proto3 || !value.isNil()) {
-                ret.fastASet(runtime.newSymbol(fdef.getName()), value);
-            }
-        }
-        return ret;
-    }
-
-    protected DynamicMessage build(ThreadContext context, int depth, int recursionLimit) {
-        if (depth >= recursionLimit) {
-            throw context.runtime.newRuntimeError("Recursion limit exceeded during encoding.");
-        }
-
-        RubySymbol typeBytesSymbol = RubySymbol.newSymbol(context.runtime, "TYPE_BYTES");
-
-        // Handle the typical case where the fields.keySet contain the fieldDescriptors
-        for (FieldDescriptor fieldDescriptor : fields.keySet()) {
-            IRubyObject value = fields.get(fieldDescriptor);
-
-            if (value instanceof RubyMap) {
-                builder.clearField(fieldDescriptor);
-                RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
-                for (DynamicMessage kv : ((RubyMap) value).build(context, mapDescriptor, depth, recursionLimit)) {
-                    builder.addRepeatedField(fieldDescriptor, kv);
-                }
-
-            } else if (value instanceof RubyRepeatedField) {
-                RubyRepeatedField repeatedField = (RubyRepeatedField) value;
-
-                builder.clearField(fieldDescriptor);
-                for (int i = 0; i < repeatedField.size(); i++) {
-                    Object item = convert(context, fieldDescriptor, repeatedField.get(i), depth, recursionLimit,
-                        /*isDefaultValueForBytes*/ false);
-                    builder.addRepeatedField(fieldDescriptor, item);
-                }
-
-            } else if (!value.isNil()) {
-                /**
-                 * Detect the special case where default_value strings are provided for byte fields.
-                 * If so, disable normal string encoding behavior within convert.
-                 * For a more detailed explanation of other possible workarounds, see the comments
-                 * above {@code com.google.protobuf.Internal#stringDefaultValue()
-                 * stringDefaultValue}.
-                 */
-                boolean isDefaultStringForBytes = false;
-                if (DEFAULT_VALUE.equals(fieldDescriptor.getFullName())) {
-                    FieldDescriptor enumFieldDescriptorForType =
-                        this.builder.getDescriptorForType().findFieldByName(TYPE);
-                    if (typeBytesSymbol.equals(fields.get(enumFieldDescriptorForType))) {
-                        isDefaultStringForBytes = true;
-                    }
-                }
-                builder.setField(fieldDescriptor, convert(context, fieldDescriptor, value, depth, recursionLimit, isDefaultStringForBytes));
-            }
-        }
-
-        // Handle cases where {@code fields} doesn't contain the value until after getFieldInternal
-        // is called - typical of a deserialized message. Skip non-maps and descriptors that already
-        // have an entry in {@code fields}.
-        for (FieldDescriptor fieldDescriptor : descriptor.getFields()) {
-            if (!fieldDescriptor.isMapField()) {
-                continue;
-            }
-            IRubyObject value = fields.get(fieldDescriptor);
-            if (value!=null) {
-                continue;
-            }
-            value = getFieldInternal(context, fieldDescriptor);
-            if (value instanceof RubyMap) {
-                builder.clearField(fieldDescriptor);
-                RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context,
-                    fieldDescriptor);
-                for (DynamicMessage kv : ((RubyMap) value).build(context, mapDescriptor, depth, recursionLimit)) {
-                    builder.addRepeatedField(fieldDescriptor, kv);
-                }
-            }
-        }
-        return builder.build();
-    }
-
-    // Internal use only, called by Google::Protobuf.deep_copy
-    protected IRubyObject deepCopy(ThreadContext context) {
-        RubyMessage copy = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK);
-        for (FieldDescriptor fdef : descriptor.getFields()) {
-            if (fdef.isRepeated()) {
-                copy.fields.put(fdef, this.getRepeatedField(context, fdef).deepCopy(context));
-            } else if (fields.containsKey(fdef)) {
-                copy.setFieldInternal(context, fdef, fields.get(fdef));
-            } else if (builder.hasField(fdef)) {
-                copy.fields.put(fdef, wrapField(context, fdef, builder.getField(fdef)));
-            }
-        }
-        return copy;
-    }
-
-    protected IRubyObject clearField(ThreadContext context, FieldDescriptor fieldDescriptor) {
-        validateMessageType(context, fieldDescriptor, "clear");
-        return clearFieldInternal(context, fieldDescriptor);
-    }
-
-    protected void discardUnknownFields(ThreadContext context) {
-        discardUnknownFields(context, builder);
-    }
-
-    protected IRubyObject getField(ThreadContext context, FieldDescriptor fieldDescriptor) {
-        validateMessageType(context, fieldDescriptor, "get");
-        return getFieldInternal(context, fieldDescriptor);
-    }
-
-    protected IRubyObject hasField(ThreadContext context, FieldDescriptor fieldDescriptor) {
-        validateMessageType(context, fieldDescriptor, "has?");
-        if (!fieldDescriptor.hasPresence()) {
-            throw context.runtime.newArgumentError("does not track presence");
-        }
-        return fields.containsKey(fieldDescriptor) ? context.runtime.getTrue() : context.runtime.getFalse();
-    }
-
-    protected IRubyObject setField(ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) {
-        validateMessageType(context, fieldDescriptor, "set");
-        return setFieldInternal(context, fieldDescriptor, value);
-    }
-
-    private RubyRepeatedField getRepeatedField(ThreadContext context, FieldDescriptor fieldDescriptor) {
-        if (fields.containsKey(fieldDescriptor)) {
-            return (RubyRepeatedField) fields.get(fieldDescriptor);
-        }
-        int count = this.builder.getRepeatedFieldCount(fieldDescriptor);
-        RubyRepeatedField ret = repeatedFieldForFieldDescriptor(context, fieldDescriptor);
-        for (int i = 0; i < count; i++) {
-            ret.push(context, new IRubyObject[] {wrapField(context, fieldDescriptor, this.builder.getRepeatedField(fieldDescriptor, i))});
-        }
-        fields.put(fieldDescriptor, ret);
-        return ret;
-    }
-
-    private IRubyObject buildFrom(ThreadContext context, DynamicMessage dynamicMessage) {
-        this.builder.mergeFrom(dynamicMessage);
-        return this;
-    }
-
-    private IRubyObject clearFieldInternal(ThreadContext context, FieldDescriptor fieldDescriptor) {
-        OneofDescriptor ood = fieldDescriptor.getContainingOneof();
-        if (ood != null) oneofCases.remove(ood);
-        fields.remove(fieldDescriptor);
-        builder.clearField(fieldDescriptor);
-        return context.nil;
-    }
-
-    private void discardUnknownFields(ThreadContext context, Message.Builder messageBuilder) {
-        messageBuilder.setUnknownFields(UnknownFieldSet.getDefaultInstance());
-        messageBuilder.getAllFields().forEach((fd, value) -> {
-            if (fd.getType() == FieldDescriptor.Type.MESSAGE) {
-                if (fd.isRepeated()) {
-                    messageBuilder.clearField(fd);
-                    ((List) value).forEach((val) -> {
-                        Message.Builder submessageBuilder = ((DynamicMessage) val).toBuilder();
-                        discardUnknownFields(context, submessageBuilder);
-                        messageBuilder.addRepeatedField(fd, submessageBuilder.build());
-                    });
-                } else {
-                    Message.Builder submessageBuilder = ((DynamicMessage) value).toBuilder();
-                    discardUnknownFields(context, submessageBuilder);
-                    messageBuilder.setField(fd, submessageBuilder.build());
-                }
-            }
-        });
-    }
-
-    private FieldDescriptor findField(ThreadContext context, IRubyObject fieldName) {
-        return findField(context, fieldName, false);
-    }
-
-    private FieldDescriptor findField(ThreadContext context, IRubyObject fieldName, boolean ignoreUnknownField) {
-        String nameStr = fieldName.asJavaString();
-        FieldDescriptor ret = this.descriptor.findFieldByName(nameStr);
-        if (ret == null && !ignoreUnknownField) {
-            throw context.runtime.newArgumentError("field " + fieldName.asJavaString() + " is not found");
-        }
-        return ret;
-    }
-
-    // convert a ruby object to protobuf type, skip type check since it is checked on the way in
-    private Object convert(ThreadContext context,
-                           FieldDescriptor fieldDescriptor,
-                           IRubyObject value, int depth, int recursionLimit,
-                           boolean isDefaultStringForBytes) {
-        Object val = null;
-        switch (fieldDescriptor.getType()) {
-            case INT32:
-            case SFIXED32:
-            case SINT32:
-                val = RubyNumeric.num2int(value);
-                break;
-            case INT64:
-            case SFIXED64:
-            case SINT64:
-                val = RubyNumeric.num2long(value);
-                break;
-            case FIXED32:
-            case UINT32:
-                val = Utils.num2uint(value);
-                break;
-            case FIXED64:
-            case UINT64:
-                val = Utils.num2ulong(context.runtime, value);
-                break;
-            case FLOAT:
-                val = (float) RubyNumeric.num2dbl(value);
-                break;
-            case DOUBLE:
-                val = (double) RubyNumeric.num2dbl(value);
-                break;
-            case BOOL:
-                val = value.isTrue();
-                break;
-            case BYTES:
-                val = ByteString.copyFrom(((RubyString) value).getBytes());
-                break;
-            case STRING:
-                if (isDefaultStringForBytes) {
-                    val = ((RubyString) value).getByteList().toString();
-                } else {
-                    val = value.asJavaString();
-                }
-                break;
-            case MESSAGE:
-                val = ((RubyMessage) value).build(context, depth + 1, recursionLimit);
-                break;
-            case ENUM:
-                EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType();
-                if (Utils.isRubyNum(value)) {
-                    val = enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
-                } else {
-                    val = enumDescriptor.findValueByName(value.asJavaString());
-                }
-                break;
-            default:
-                break;
-        }
-
-        return val;
-    }
-
-    private static RaiseException createParseError(ThreadContext context, String message) {
-        if (parseErrorClass == null) {
-            parseErrorClass = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::ParseError");
-        }
-        return RaiseException.from(context.runtime, parseErrorClass, message);
-    }
-
-    private IRubyObject wrapField(ThreadContext context, FieldDescriptor fieldDescriptor, Object value) {
-        return wrapField(context, fieldDescriptor, value, false);
-    }
-
-    private IRubyObject wrapField(ThreadContext context, FieldDescriptor fieldDescriptor, Object value, boolean encodeBytes) {
-        if (value == null) {
-            return context.runtime.getNil();
-        }
-        Ruby runtime = context.runtime;
-
-        switch (fieldDescriptor.getType()) {
-            case INT32:
-            case INT64:
-            case FIXED32:
-            case SINT32:
-            case FIXED64:
-            case SINT64:
-            case SFIXED64:
-            case SFIXED32:
-            case UINT32:
-            case UINT64:
-            case FLOAT:
-            case DOUBLE:
-            case BOOL:
-            case BYTES:
-            case STRING:
-                return Utils.wrapPrimaryValue(context, fieldDescriptor.getType(), value, encodeBytes);
-            case MESSAGE:
-                RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
-                RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK);
-                return msg.buildFrom(context, (DynamicMessage) value);
-            case ENUM:
-                EnumValueDescriptor enumValueDescriptor = (EnumValueDescriptor) value;
-                if (enumValueDescriptor.getIndex() == -1) { // UNKNOWN ENUM VALUE
-                    return runtime.newFixnum(enumValueDescriptor.getNumber());
-                }
-                return runtime.newSymbol(enumValueDescriptor.getName());
-            default:
-                return runtime.newString(value.toString());
-        }
-    }
-
-    private RubyRepeatedField repeatedFieldForFieldDescriptor(ThreadContext context, FieldDescriptor fieldDescriptor) {
-        IRubyObject typeClass = context.runtime.getNilClass();
-        IRubyObject descriptor = getDescriptorForField(context, fieldDescriptor);
-        FieldDescriptor.Type type = fieldDescriptor.getType();
-
-        if (type == FieldDescriptor.Type.MESSAGE) {
-            typeClass = ((RubyDescriptor) descriptor).msgclass(context);
-
-        } else if (type == FieldDescriptor.Type.ENUM) {
-            typeClass = ((RubyEnumDescriptor) descriptor).enummodule(context);
-        }
-
-        RubyRepeatedField field = new RubyRepeatedField(context.runtime, cRepeatedField, type, typeClass);
-        field.setName(fieldDescriptor.getName());
-
-        return field;
-    }
-
-    private IRubyObject getFieldInternal(ThreadContext context, FieldDescriptor fieldDescriptor) {
-        return getFieldInternal(context, fieldDescriptor, true);
-    }
-
-    private IRubyObject getFieldInternal(ThreadContext context, FieldDescriptor fieldDescriptor,
-        boolean returnDefaults) {
-        OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof();
-        if (oneofDescriptor != null) {
-            if (oneofCases.get(oneofDescriptor) == fieldDescriptor) {
-                IRubyObject value = fields.get(fieldDescriptor);
-                if (value == null) {
-                    FieldDescriptor oneofCase = builder.getOneofFieldDescriptor(oneofDescriptor);
-                    if (oneofCase != null) {
-                        Object builderValue = builder.getField(oneofCase);
-                        if (builderValue != null) {
-                            boolean encodeBytes = oneofCase.hasDefaultValue() && builderValue.equals(oneofCase.getDefaultValue());
-                            value = wrapField(context, oneofCase, builderValue, encodeBytes);
-                        }
-                    }
-                    if (value == null) {
-                        return context.nil;
-                    } else {
-                        return value;
-                    }
-                } else {
-                    return value;
-                }
-            } else {
-                FieldDescriptor oneofCase = builder.getOneofFieldDescriptor(oneofDescriptor);
-                if (oneofCase != fieldDescriptor) {
-                    if (fieldDescriptor.getType() == FieldDescriptor.Type.MESSAGE
-                        || !returnDefaults) {
-                        return context.nil;
-                    } else {
-                        return wrapField(context, fieldDescriptor,
-                            fieldDescriptor.getDefaultValue(), true);
-                    }
-                }
-                if (returnDefaults || builder.hasField(fieldDescriptor)) {
-                    Object rawValue = builder.getField(oneofCase);
-                    boolean encodeBytes = oneofCase.hasDefaultValue() && rawValue.equals(oneofCase.getDefaultValue());
-                    IRubyObject value = wrapField(context, oneofCase, rawValue, encodeBytes);
-                    fields.put(fieldDescriptor, value);
-                    return value;
-                } else {
-                    return context.nil;
-                }
-            }
-        }
-
-        if (fieldDescriptor.isMapField()) {
-            RubyMap map = (RubyMap) fields.get(fieldDescriptor);
-            if (map == null) {
-                map = newMapForField(context, fieldDescriptor);
-                int mapSize = this.builder.getRepeatedFieldCount(fieldDescriptor);
-                FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1);
-                FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2);
-                RubyDescriptor kvDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
-                RubyClass kvClass = (RubyClass) kvDescriptor.msgclass(context);
-                for (int i = 0; i < mapSize; i++) {
-                    RubyMessage kvMessage = (RubyMessage) kvClass.newInstance(context, Block.NULL_BLOCK);
-                    DynamicMessage message = (DynamicMessage) this.builder.getRepeatedField(fieldDescriptor, i);
-                    kvMessage.buildFrom(context, message);
-                    map.indexSet(context, kvMessage.getField(context, keyField), kvMessage.getField(context, valueField));
-                }
+                final RubyMap map = newMapForField(context, fieldDescriptor);
+                map.mergeIntoSelf(context, value);
                 fields.put(fieldDescriptor, map);
-            }
-            return map;
-        }
-
-        if (fieldDescriptor.isRepeated()) {
-            return getRepeatedField(context, fieldDescriptor);
-        }
-
-        if (fieldDescriptor.getType() != FieldDescriptor.Type.MESSAGE ||
-                builder.hasField(fieldDescriptor) || fields.containsKey(fieldDescriptor)) {
-            if (fields.containsKey(fieldDescriptor)) {
-                return fields.get(fieldDescriptor);
-            } else if (returnDefaults || builder.hasField(fieldDescriptor)) {
-                Object rawValue = builder.getField(fieldDescriptor);
-                boolean encodeBytes = fieldDescriptor.hasDefaultValue() && rawValue.equals(fieldDescriptor.getDefaultValue());
-                IRubyObject value = wrapField(context, fieldDescriptor, rawValue, encodeBytes);
-                if (builder.hasField(fieldDescriptor)) {
-                    fields.put(fieldDescriptor, value);
-                }
-                return value;
-            }
-        }
-        return context.nil;
-    }
-
-    private IRubyObject setFieldInternal(ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) {
-        testFrozen("can't modify frozen " + getMetaClass());
-
-        if (fieldDescriptor.isMapField()) {
-            if (!(value instanceof RubyMap)) {
-                throw Utils.createTypeError(context, "Expected Map instance");
-            }
-            RubyMap thisMap = (RubyMap) getFieldInternal(context, fieldDescriptor);
-            thisMap.mergeIntoSelf(context, value);
-
-        } else if (fieldDescriptor.isRepeated()) {
-            if (value instanceof RubyRepeatedField) {
-                fields.put(fieldDescriptor, value);
-            } else {
-                throw Utils.createTypeError(context, "Expected repeated field array");
-            }
-
-        } else {
-            boolean addValue = true;
-            FieldDescriptor.Type fieldType = fieldDescriptor.getType();
-            OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof();
-
-            // Determine the typeclass, if any
-            IRubyObject typeClass = context.runtime.getObject();
-            if (fieldType == FieldDescriptor.Type.MESSAGE) {
-                typeClass = ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
-                if (value.isNil()){
-                    addValue = false;
-                }
-            } else if (fieldType == FieldDescriptor.Type.ENUM) {
-                typeClass = ((RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor)).enummodule(context);
-                value = enumToSymbol(context, fieldDescriptor.getEnumType(), value);
-            }
-
-            if (oneofDescriptor != null) {
-                FieldDescriptor oneofCase = oneofCases.get(oneofDescriptor);
-
-                // Remove the existing field if we are setting a different field in the Oneof
-                if (oneofCase != null && oneofCase != fieldDescriptor) {
-                    fields.remove(oneofCase);
+              } else if (fieldDescriptor.isRepeated()) {
+                if (!(value instanceof RubyArray))
+                  throw runtime.newArgumentError(
+                      "Expected array as initializer value for repeated field '"
+                          + key.asJavaString()
+                          + "' (given "
+                          + value.getMetaClass()
+                          + ").");
+                fields.put(fieldDescriptor, rubyToRepeatedField(context, fieldDescriptor, value));
+              } else {
+                OneofDescriptor oneof = fieldDescriptor.getContainingOneof();
+                if (oneof != null) {
+                  oneofCases.put(oneof, fieldDescriptor);
                 }
 
-                // Keep track of what Oneofs are set
-                if (value.isNil()) {
-                    oneofCases.remove(oneofDescriptor);
-                    if (!oneofDescriptor.isSynthetic()) {
-                        addValue = false;
-                    }
+                if (value instanceof RubyHash
+                    && fieldDescriptor.getType() == FieldDescriptor.Type.MESSAGE) {
+                  RubyDescriptor descriptor =
+                      (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
+                  RubyClass typeClass = (RubyClass) descriptor.msgclass(context);
+                  value = (IRubyObject) typeClass.newInstance(context, value, Block.NULL_BLOCK);
+                  fields.put(fieldDescriptor, value);
                 } else {
-                    oneofCases.put(oneofDescriptor, fieldDescriptor);
+                  indexSet(context, key, value);
                 }
+              }
             }
+          },
+          null);
+    }
+    return this;
+  }
 
-            if (addValue) {
-                value = Utils.checkType(context, fieldType, fieldDescriptor.getName(), value, (RubyModule) typeClass);
-                fields.put(fieldDescriptor, value);
-            } else {
-                fields.remove(fieldDescriptor);
-            }
-        }
-        return context.nil;
+  /*
+   * call-seq:
+   *     Message.[]=(index, value)
+   *
+   * Sets a field's value by field name. The provided field name should be a
+   * string.
+   */
+  @JRubyMethod(name = "[]=")
+  public IRubyObject indexSet(ThreadContext context, IRubyObject fieldName, IRubyObject value) {
+    FieldDescriptor fieldDescriptor = findField(context, fieldName);
+    return setFieldInternal(context, fieldDescriptor, value);
+  }
+
+  /*
+   * call-seq:
+   *     Message.[](index) => value
+   *
+   * Accesses a field's value by field name. The provided field name should be a
+   * string.
+   */
+  @JRubyMethod(name = "[]")
+  public IRubyObject index(ThreadContext context, IRubyObject fieldName) {
+    FieldDescriptor fieldDescriptor = findField(context, fieldName);
+    return getFieldInternal(context, fieldDescriptor);
+  }
+
+  /*
+   * call-seq:
+   *     Message.inspect => string
+   *
+   * Returns a human-readable string representing this message. It will be
+   * formatted as "<MessageType: field1: value1, field2: value2, ...>". Each
+   * field's value is represented according to its own #inspect method.
+   */
+  @JRubyMethod(name = {"inspect", "to_s"})
+  public IRubyObject inspect() {
+    ThreadContext context = getRuntime().getCurrentContext();
+    String cname = metaClass.getName();
+    String colon = ": ";
+    String comma = ", ";
+    StringBuilder sb = new StringBuilder("<");
+    boolean addComma = false;
+
+    sb.append(cname).append(colon);
+
+    for (FieldDescriptor fd : descriptor.getFields()) {
+      if (fd.hasPresence() && !fields.containsKey(fd)) {
+        continue;
+      }
+      if (addComma) {
+        sb.append(comma);
+      } else {
+        addComma = true;
+      }
+
+      sb.append(fd.getName()).append(colon);
+
+      IRubyObject value = getFieldInternal(context, fd);
+      if (value instanceof RubyBoolean) {
+        // Booleans don't implement internal "inspect" methods so have to call handle them manually
+        sb.append(value.isTrue() ? "true" : "false");
+      } else {
+        sb.append(value.inspect());
+      }
+    }
+    sb.append(">");
+
+    return context.runtime.newString(sb.toString());
+  }
+
+  /*
+   * call-seq:
+   *     Message.hash => hash_value
+   *
+   * Returns a hash value that represents this message's field values.
+   */
+  @JRubyMethod
+  public IRubyObject hash(ThreadContext context) {
+    try {
+      MessageDigest digest = MessageDigest.getInstance("SHA-256");
+      for (FieldDescriptor fd : descriptor.getFields()) {
+        digest.update((byte) getFieldInternal(context, fd).hashCode());
+      }
+      return context.runtime.newFixnum(ByteBuffer.wrap(digest.digest()).getLong());
+    } catch (NoSuchAlgorithmException ignore) {
+      return context.runtime.newFixnum(System.identityHashCode(this));
+    }
+  }
+
+  /*
+   * call-seq:
+   *     Message.==(other) => boolean
+   *
+   * Performs a deep comparison of this message with another. Messages are equal
+   * if they have the same type and if each field is equal according to the :==
+   * method's semantics (a more efficient comparison may actually be done if the
+   * field is of a primitive type).
+   */
+  @JRubyMethod(name = {"==", "eql?"})
+  public IRubyObject eq(ThreadContext context, IRubyObject other) {
+    Ruby runtime = context.runtime;
+    if (!(other instanceof RubyMessage)) return runtime.getFalse();
+    RubyMessage message = (RubyMessage) other;
+    if (descriptor != message.descriptor) {
+      return runtime.getFalse();
     }
 
-    private IRubyObject getDescriptorForField(ThreadContext context, FieldDescriptor fieldDescriptor) {
-        RubyDescriptor thisRbDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
-        RubyFieldDescriptor fd = (RubyFieldDescriptor) thisRbDescriptor.lookup(context, context.runtime.newString(fieldDescriptor.getName()));
-        return fd.getSubtype(context);
+    for (FieldDescriptor fdef : descriptor.getFields()) {
+      IRubyObject thisVal = getFieldInternal(context, fdef);
+      IRubyObject thatVal = message.getFieldInternal(context, fdef);
+      IRubyObject ret = thisVal.callMethod(context, "==", thatVal);
+      if (!ret.isTrue()) {
+        return runtime.getFalse();
+      }
     }
+    return runtime.getTrue();
+  }
 
-    private IRubyObject enumToSymbol(ThreadContext context, EnumDescriptor enumDescriptor, IRubyObject value) {
-        if (value instanceof RubySymbol) {
-            return (RubySymbol) value;
-        } else if (Utils.isRubyNum(value)) {
-            EnumValueDescriptor enumValue = enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
-            if (enumValue.getIndex() != -1) {
-                return context.runtime.newSymbol(enumValue.getName());
-            } else {
-                return value;
-            }
-        } else if (value instanceof RubyString) {
-            return ((RubyString) value).intern();
-        }
-
-        return context.runtime.newSymbol("UNKNOWN");
+  /*
+   * call-seq:
+   *     Message.respond_to?(method_name, search_private_and_protected) => boolean
+   *
+   *  Parallels method_missing, returning true when this object implements a method with the given
+   *  method_name.
+   */
+  @JRubyMethod(name = "respond_to?", required = 1, optional = 1)
+  public IRubyObject respondTo(ThreadContext context, IRubyObject[] args) {
+    String methodName = args[0].asJavaString();
+    if (descriptor.findFieldByName(methodName) != null) {
+      return context.runtime.getTrue();
     }
-
-    private RubyRepeatedField rubyToRepeatedField(ThreadContext context,
-                                                  FieldDescriptor fieldDescriptor, IRubyObject value) {
-        RubyArray arr = value.convertToArray();
-        RubyRepeatedField repeatedField = repeatedFieldForFieldDescriptor(context, fieldDescriptor);
-        IRubyObject[] values = new IRubyObject[arr.size()];
-        FieldDescriptor.Type fieldType = fieldDescriptor.getType();
-
-        RubyModule typeClass = null;
-        if (fieldType == FieldDescriptor.Type.MESSAGE) {
-            RubyDescriptor descriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
-            typeClass = (RubyModule) descriptor.msgclass(context);
-        } else if (fieldType == FieldDescriptor.Type.ENUM) {
-            RubyEnumDescriptor enumDescriptor = (RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor);
-            typeClass = (RubyModule) enumDescriptor.enummodule(context);
-        }
-
-        for (int i = 0; i < arr.size(); i++) {
-            IRubyObject item = arr.eltInternal(i);
-            if (item.isNil()) {
-                throw Utils.createTypeError(context, "nil message not allowed here.");
-            }
-            if (item instanceof RubyHash && typeClass != null) {
-                values[i] = ((RubyClass) typeClass).newInstance(context, item, Block.NULL_BLOCK);
-            } else {
-                if (fieldType == FieldDescriptor.Type.ENUM) {
-                    item = enumToSymbol(context, fieldDescriptor.getEnumType(), item);
-                }
-
-                values[i] = item;
-            }
-        }
-        repeatedField.push(context, values);
-
-        return repeatedField;
+    RubyDescriptor rubyDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
+    IRubyObject oneofDescriptor = rubyDescriptor.lookupOneof(context, args[0]);
+    if (!oneofDescriptor.isNil()) {
+      return context.runtime.getTrue();
     }
+    if (methodName.startsWith(CLEAR_PREFIX)) {
+      String strippedMethodName = methodName.substring(6);
+      oneofDescriptor =
+          rubyDescriptor.lookupOneof(context, context.runtime.newSymbol(strippedMethodName));
+      if (!oneofDescriptor.isNil()) {
+        return context.runtime.getTrue();
+      }
 
-    private RubyMap newMapForField(ThreadContext context, FieldDescriptor fieldDescriptor) {
-        RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
-        FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1);
-        FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2);
-        IRubyObject keyType = RubySymbol.newSymbol(context.runtime, keyField.getType().name());
-        IRubyObject valueType = RubySymbol.newSymbol(context.runtime, valueField.getType().name());
+      if (descriptor.findFieldByName(strippedMethodName) != null) {
+        return context.runtime.getTrue();
+      }
+    }
+    if (methodName.startsWith(HAS_PREFIX) && methodName.endsWith(QUESTION_MARK)) {
+      String strippedMethodName = methodName.substring(4, methodName.length() - 1);
+      FieldDescriptor fieldDescriptor = descriptor.findFieldByName(strippedMethodName);
+      if (fieldDescriptor != null
+          && (!proto3
+              || fieldDescriptor.getContainingOneof() == null
+              || fieldDescriptor.getContainingOneof().isSynthetic())
+          && fieldDescriptor.hasPresence()) {
+        return context.runtime.getTrue();
+      }
+      oneofDescriptor =
+          rubyDescriptor.lookupOneof(
+              context, RubyString.newString(context.runtime, strippedMethodName));
+      if (!oneofDescriptor.isNil()) {
+        return context.runtime.getTrue();
+      }
+    }
+    if (methodName.endsWith(AS_VALUE_SUFFIX)) {
+      FieldDescriptor fieldDescriptor =
+          descriptor.findFieldByName(methodName.substring(0, methodName.length() - 9));
+      if (fieldDescriptor != null && isWrappable(fieldDescriptor)) {
+        return context.runtime.getTrue();
+      }
+    }
+    if (methodName.endsWith(CONST_SUFFIX)) {
+      FieldDescriptor fieldDescriptor =
+          descriptor.findFieldByName(methodName.substring(0, methodName.length() - 6));
+      if (fieldDescriptor != null) {
+        if (fieldDescriptor.getType() == FieldDescriptor.Type.ENUM) {
+          return context.runtime.getTrue();
+        }
+      }
+    }
+    if (methodName.endsWith(Utils.EQUAL_SIGN)) {
+      String strippedMethodName = methodName.substring(0, methodName.length() - 1);
+      FieldDescriptor fieldDescriptor = descriptor.findFieldByName(strippedMethodName);
+      if (fieldDescriptor != null) {
+        return context.runtime.getTrue();
+      }
+      if (strippedMethodName.endsWith(AS_VALUE_SUFFIX)) {
+        strippedMethodName = methodName.substring(0, strippedMethodName.length() - 9);
+        fieldDescriptor = descriptor.findFieldByName(strippedMethodName);
+        if (fieldDescriptor != null && isWrappable(fieldDescriptor)) {
+          return context.runtime.getTrue();
+        }
+      }
+    }
+    boolean includePrivate = false;
+    if (args.length == 2) {
+      includePrivate = context.runtime.getTrue().equals(args[1]);
+    }
+    return metaClass.respondsToMethod(methodName, includePrivate)
+        ? context.runtime.getTrue()
+        : context.runtime.getFalse();
+  }
 
-        if (valueField.getType() == FieldDescriptor.Type.MESSAGE) {
-            RubyFieldDescriptor rubyFieldDescriptor = (RubyFieldDescriptor) mapDescriptor.lookup(context,
-                    context.runtime.newString("value"));
-            RubyDescriptor rubyDescriptor = (RubyDescriptor) rubyFieldDescriptor.getSubtype(context);
-            return (RubyMap) cMap.newInstance(context, keyType, valueType,
-                    rubyDescriptor.msgclass(context), Block.NULL_BLOCK);
+  /*
+   * call-seq:
+   *     Message.method_missing(*args)
+   *
+   * Provides accessors and setters and methods to clear and check for presence of
+   * message fields according to their field names.
+   *
+   * For any field whose name does not conflict with a built-in method, an
+   * accessor is provided with the same name as the field, and a setter is
+   * provided with the name of the field plus the '=' suffix. Thus, given a
+   * message instance 'msg' with field 'foo', the following code is valid:
+   *
+   *     msg.foo = 42
+   *     puts msg.foo
+   *
+   * This method also provides read-only accessors for oneofs. If a oneof exists
+   * with name 'my_oneof', then msg.my_oneof will return a Ruby symbol equal to
+   * the name of the field in that oneof that is currently set, or nil if none.
+   *
+   * It also provides methods of the form 'clear_fieldname' to clear the value
+   * of the field 'fieldname'. For basic data types, this will set the default
+   * value of the field.
+   *
+   * Additionally, it provides methods of the form 'has_fieldname?', which returns
+   * true if the field 'fieldname' is set in the message object, else false. For
+   * 'proto3' syntax, calling this for a basic type field will result in an error.
+   */
+  @JRubyMethod(name = "method_missing", rest = true)
+  public IRubyObject methodMissing(ThreadContext context, IRubyObject[] args) {
+    Ruby runtime = context.runtime;
+    String methodName = args[0].asJavaString();
+    RubyDescriptor rubyDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
 
-        } else if (valueField.getType() == FieldDescriptor.Type.ENUM) {
-            RubyFieldDescriptor rubyFieldDescriptor = (RubyFieldDescriptor) mapDescriptor.lookup(context,
-                    context.runtime.newString("value"));
-            RubyEnumDescriptor rubyEnumDescriptor = (RubyEnumDescriptor) rubyFieldDescriptor.getSubtype(context);
-            return (RubyMap) cMap.newInstance(context, keyType, valueType,
-                    rubyEnumDescriptor.enummodule(context), Block.NULL_BLOCK);
+    if (args.length == 1) {
+      // If we find a Oneof return it's name (use lookupOneof because it has an index)
+      IRubyObject oneofDescriptor = rubyDescriptor.lookupOneof(context, args[0]);
 
+      if (!oneofDescriptor.isNil()) {
+        RubyOneofDescriptor rubyOneofDescriptor = (RubyOneofDescriptor) oneofDescriptor;
+        OneofDescriptor ood = rubyOneofDescriptor.getDescriptor();
+
+        // Check to see if we set this through ruby
+        FieldDescriptor fieldDescriptor = oneofCases.get(ood);
+
+        if (fieldDescriptor == null) {
+          // See if we set this from decoding a message
+          fieldDescriptor = builder.getOneofFieldDescriptor(ood);
+
+          if (fieldDescriptor == null) {
+            return context.nil;
+          } else {
+            // Cache it so we don't need to do multiple checks next time
+            oneofCases.put(ood, fieldDescriptor);
+            return runtime.newSymbol(fieldDescriptor.getName());
+          }
         } else {
-            return (RubyMap) cMap.newInstance(context, keyType, valueType, Block.NULL_BLOCK);
+          return runtime.newSymbol(fieldDescriptor.getName());
         }
-    }
+      }
 
-    private boolean isWrappable(FieldDescriptor fieldDescriptor) {
-      if (fieldDescriptor.getType() != FieldDescriptor.Type.MESSAGE) return false;
+      // If we find a field return its value
+      FieldDescriptor fieldDescriptor = descriptor.findFieldByName(methodName);
 
-      return isWrapper(fieldDescriptor.getMessageType());
-    }
+      if (fieldDescriptor != null) {
+        return getFieldInternal(context, fieldDescriptor);
+      }
 
-    private static boolean isWrapper(Descriptor messageDescriptor) {
-      switch(messageDescriptor.getFullName()) {
-          case "google.protobuf.DoubleValue":
-          case "google.protobuf.FloatValue":
-          case "google.protobuf.Int64Value":
-          case "google.protobuf.UInt64Value":
-          case "google.protobuf.Int32Value":
-          case "google.protobuf.UInt32Value":
-          case "google.protobuf.BoolValue":
-          case "google.protobuf.StringValue":
-          case "google.protobuf.BytesValue":
-              return true;
-          default:
-              return false;
+      if (methodName.startsWith(CLEAR_PREFIX)) {
+        methodName = methodName.substring(6);
+        oneofDescriptor = rubyDescriptor.lookupOneof(context, runtime.newSymbol(methodName));
+        if (!oneofDescriptor.isNil()) {
+          fieldDescriptor = oneofCases.get(((RubyOneofDescriptor) oneofDescriptor).getDescriptor());
+          if (fieldDescriptor == null) {
+            // Clearing an already cleared oneof; return here to avoid NoMethodError.
+            return context.nil;
+          }
+        }
+
+        if (fieldDescriptor == null) {
+          fieldDescriptor = descriptor.findFieldByName(methodName);
+        }
+
+        if (fieldDescriptor != null) {
+          return clearFieldInternal(context, fieldDescriptor);
+        }
+
+      } else if (methodName.startsWith(HAS_PREFIX) && methodName.endsWith(QUESTION_MARK)) {
+        methodName =
+            methodName.substring(
+                4, methodName.length() - 1); // Trim "has_" and "?" off the field name
+        oneofDescriptor = rubyDescriptor.lookupOneof(context, runtime.newSymbol(methodName));
+        if (!oneofDescriptor.isNil()) {
+          RubyOneofDescriptor rubyOneofDescriptor = (RubyOneofDescriptor) oneofDescriptor;
+          return oneofCases.containsKey(rubyOneofDescriptor.getDescriptor())
+              ? runtime.getTrue()
+              : runtime.getFalse();
+        }
+
+        fieldDescriptor = descriptor.findFieldByName(methodName);
+
+        if (fieldDescriptor != null
+            && (!proto3
+                || fieldDescriptor.getContainingOneof() == null
+                || fieldDescriptor.getContainingOneof().isSynthetic())
+            && fieldDescriptor.hasPresence()) {
+          return fields.containsKey(fieldDescriptor) ? runtime.getTrue() : runtime.getFalse();
+        }
+
+      } else if (methodName.endsWith(AS_VALUE_SUFFIX)) {
+        methodName = methodName.substring(0, methodName.length() - 9);
+        fieldDescriptor = descriptor.findFieldByName(methodName);
+
+        if (fieldDescriptor != null && isWrappable(fieldDescriptor)) {
+          IRubyObject value = getFieldInternal(context, fieldDescriptor);
+
+          if (!value.isNil() && value instanceof RubyMessage) {
+            return ((RubyMessage) value).index(context, runtime.newString("value"));
+          }
+
+          return value;
+        }
+
+      } else if (methodName.endsWith(CONST_SUFFIX)) {
+        methodName = methodName.substring(0, methodName.length() - 6);
+        fieldDescriptor = descriptor.findFieldByName(methodName);
+        if (fieldDescriptor != null && fieldDescriptor.getType() == FieldDescriptor.Type.ENUM) {
+          IRubyObject enumValue = getFieldInternal(context, fieldDescriptor);
+
+          if (!enumValue.isNil()) {
+            EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType();
+            if (enumValue instanceof RubyRepeatedField) {
+              RubyArray values = (RubyArray) ((RubyRepeatedField) enumValue).toArray(context);
+              RubyArray retValues = runtime.newArray(values.getLength());
+              for (int i = 0; i < values.getLength(); i++) {
+                String val = values.eltInternal(i).toString();
+                retValues.store(
+                    (long) i, runtime.newFixnum(enumDescriptor.findValueByName(val).getNumber()));
+              }
+              return retValues;
+            }
+
+            return runtime.newFixnum(
+                enumDescriptor.findValueByName(enumValue.asJavaString()).getNumber());
+          }
+        }
+      }
+
+    } else if (args.length == 2 && methodName.endsWith(Utils.EQUAL_SIGN)) {
+
+      methodName = methodName.substring(0, methodName.length() - 1); // Trim equals sign
+      FieldDescriptor fieldDescriptor = descriptor.findFieldByName(methodName);
+      if (fieldDescriptor != null) {
+        return setFieldInternal(context, fieldDescriptor, args[1]);
+      }
+
+      IRubyObject oneofDescriptor =
+          rubyDescriptor.lookupOneof(context, RubyString.newString(context.runtime, methodName));
+      if (!oneofDescriptor.isNil()) {
+        throw runtime.newRuntimeError("Oneof accessors are read-only.");
+      }
+
+      if (methodName.endsWith(AS_VALUE_SUFFIX)) {
+        methodName = methodName.substring(0, methodName.length() - 9);
+
+        fieldDescriptor = descriptor.findFieldByName(methodName);
+
+        if (fieldDescriptor != null && isWrappable(fieldDescriptor)) {
+          if (args[1].isNil()) {
+            return setFieldInternal(context, fieldDescriptor, args[1]);
+          }
+
+          RubyClass typeClass =
+              (RubyClass)
+                  ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor))
+                      .msgclass(context);
+          RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK);
+          msg.indexSet(context, runtime.newString("value"), args[1]);
+          return setFieldInternal(context, fieldDescriptor, msg);
+        }
       }
     }
 
-    private void validateMessageType(ThreadContext context, FieldDescriptor fieldDescriptor, String methodName) {
-        if (descriptor != fieldDescriptor.getContainingType()) {
-            throw Utils.createTypeError(context, methodName + " method called on wrong message type");
-        }
+    return Helpers.invokeSuper(context, this, metaClass, "method_missing", args, Block.NULL_BLOCK);
+  }
+
+  /**
+   * call-seq: Message.dup => new_message Performs a shallow copy of this message and returns the
+   * new copy.
+   */
+  @JRubyMethod
+  public IRubyObject dup(ThreadContext context) {
+    RubyMessage dup = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK);
+    for (FieldDescriptor fieldDescriptor : this.descriptor.getFields()) {
+      if (fieldDescriptor.isRepeated()) {
+        dup.fields.put(fieldDescriptor, this.getRepeatedField(context, fieldDescriptor));
+      } else if (fields.containsKey(fieldDescriptor)) {
+        dup.setFieldInternal(context, fieldDescriptor, fields.get(fieldDescriptor));
+      } else if (this.builder.hasField(fieldDescriptor)) {
+        dup.fields.put(
+            fieldDescriptor,
+            wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor)));
+      }
+    }
+    return dup;
+  }
+
+  /*
+   * call-seq:
+   *     Message.descriptor => descriptor
+   *
+   * Class method that returns the Descriptor instance corresponding to this
+   * message class's type.
+   */
+  @JRubyMethod(name = "descriptor", meta = true)
+  public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) {
+    return ((RubyClass) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR);
+  }
+
+  /*
+   * call-seq:
+   *     MessageClass.encode(msg, options = {}) => bytes
+   *
+   * Encodes the given message object to its serialized form in protocol buffers
+   * wire format.
+   * @param options [Hash] options for the encoder
+   *  recursion_limit: set to maximum encoding depth for message (default is 64)
+   */
+  @JRubyMethod(required = 1, optional = 1, meta = true)
+  public static IRubyObject encode(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
+    if (recv != args[0].getMetaClass()) {
+      throw context.runtime.newArgumentError(
+          "Tried to encode a " + args[0].getMetaClass() + " message with " + recv);
+    }
+    RubyMessage message = (RubyMessage) args[0];
+    int recursionLimitInt = SINK_MAXIMUM_NESTING;
+
+    if (args.length > 1) {
+      RubyHash options = (RubyHash) args[1];
+      IRubyObject recursionLimit = options.fastARef(context.runtime.newSymbol("recursion_limit"));
+
+      if (recursionLimit != null) {
+        recursionLimitInt = ((RubyNumeric) recursionLimit).getIntValue();
+      }
+    }
+    return context.runtime.newString(
+        new ByteList(message.build(context, 0, recursionLimitInt).toByteArray()));
+  }
+
+  /*
+   * call-seq:
+   *     MessageClass.decode(data, options = {}) => message
+   *
+   * Decodes the given data (as a string containing bytes in protocol buffers wire
+   * format) under the interpretation given by this message class's definition
+   * and returns a message object with the corresponding field values.
+   * @param options [Hash] options for the decoder
+   *  recursion_limit: set to maximum decoding depth for message (default is 100)
+   */
+  @JRubyMethod(required = 1, optional = 1, meta = true)
+  public static IRubyObject decode(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
+    IRubyObject data = args[0];
+    byte[] bin = data.convertToString().getBytes();
+    CodedInputStream input = CodedInputStream.newInstance(bin);
+    RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK);
+
+    if (args.length == 2) {
+      if (!(args[1] instanceof RubyHash)) {
+        throw context.runtime.newArgumentError("Expected hash arguments.");
+      }
+
+      IRubyObject recursionLimit =
+          ((RubyHash) args[1]).fastARef(context.runtime.newSymbol("recursion_limit"));
+      if (recursionLimit != null) {
+        input.setRecursionLimit(((RubyNumeric) recursionLimit).getIntValue());
+      }
     }
 
-    private static RubyClass parseErrorClass;
+    try {
+      ret.builder.mergeFrom(input);
+    } catch (Exception e) {
+      throw RaiseException.from(
+          context.runtime,
+          (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::ParseError"),
+          e.getMessage());
+    }
 
-    private static final String AS_VALUE_SUFFIX = "_as_value";
-    private static final String CLEAR_PREFIX = "clear_";
-    private static final String CONST_SUFFIX = "_const";
-    private static final String HAS_PREFIX = "has_";
-    private static final String QUESTION_MARK = "?";
-    private static final int SINK_MAXIMUM_NESTING = 64;
+    if (!ret.proto3) {
+      // Need to reset unknown values in repeated enum fields
+      ret.builder
+          .getUnknownFields()
+          .asMap()
+          .forEach(
+              (i, values) -> {
+                FieldDescriptor fd = ret.builder.getDescriptorForType().findFieldByNumber(i);
+                if (fd != null && fd.isRepeated() && fd.getType() == FieldDescriptor.Type.ENUM) {
+                  EnumDescriptor ed = fd.getEnumType();
+                  values
+                      .getVarintList()
+                      .forEach(
+                          value -> {
+                            ret.builder.addRepeatedField(
+                                fd, ed.findValueByNumberCreatingIfUnknown(value.intValue()));
+                          });
+                }
+              });
+    }
 
-    private Descriptor descriptor;
-    private DynamicMessage.Builder builder;
-    private Map<FieldDescriptor, IRubyObject> fields;
-    private Map<OneofDescriptor, FieldDescriptor> oneofCases;
-    private RubyClass cRepeatedField;
-    private RubyClass cMap;
-    private boolean ignoreUnknownFieldsOnInit = false;
-    private boolean proto3;
+    return ret;
+  }
+
+  /*
+   * call-seq:
+   *     MessageClass.encode_json(msg, options = {}) => json_string
+   *
+   * Encodes the given message object into its serialized JSON representation.
+   * @param options [Hash] options for the decoder
+   *  preserve_proto_fieldnames: set true to use original fieldnames (default is to camelCase)
+   *  emit_defaults: set true to emit 0/false values (default is to omit them)
+   */
+  @JRubyMethod(name = "encode_json", required = 1, optional = 1, meta = true)
+  public static IRubyObject encodeJson(
+      ThreadContext context, IRubyObject recv, IRubyObject[] args) {
+    Ruby runtime = context.runtime;
+    RubyMessage message = (RubyMessage) args[0];
+    JsonFormat.Printer printer = JsonFormat.printer().omittingInsignificantWhitespace();
+    String result;
+
+    if (args.length > 1) {
+      RubyHash options;
+      if (args[1] instanceof RubyHash) {
+        options = (RubyHash) args[1];
+      } else if (args[1].respondsTo("to_h")) {
+        options = (RubyHash) args[1].callMethod(context, "to_h");
+      } else {
+        throw runtime.newArgumentError("Expected hash arguments.");
+      }
+
+      IRubyObject emitDefaults = options.fastARef(runtime.newSymbol("emit_defaults"));
+      IRubyObject preserveNames = options.fastARef(runtime.newSymbol("preserve_proto_fieldnames"));
+
+      if (emitDefaults != null && emitDefaults.isTrue()) {
+        printer = printer.includingDefaultValueFields();
+      }
+
+      if (preserveNames != null && preserveNames.isTrue()) {
+        printer = printer.preservingProtoFieldNames();
+      }
+    }
+    printer =
+        printer.usingTypeRegistry(
+            JsonFormat.TypeRegistry.newBuilder().add(message.descriptor).build());
+
+    try {
+      result = printer.print(message.build(context, 0, SINK_MAXIMUM_NESTING));
+    } catch (InvalidProtocolBufferException e) {
+      throw runtime.newRuntimeError(e.getMessage());
+    } catch (IllegalArgumentException e) {
+      throw createParseError(context, e.getMessage());
+    }
+
+    return runtime.newString(result);
+  }
+
+  /*
+   * call-seq:
+   *     MessageClass.decode_json(data, options = {}) => message
+   *
+   * Decodes the given data (as a string containing bytes in protocol buffers wire
+   * format) under the interpretation given by this message class's definition
+   * and returns a message object with the corresponding field values.
+   *
+   *  @param options [Hash] options for the decoder
+   *   ignore_unknown_fields: set true to ignore unknown fields (default is to
+   *   raise an error)
+   */
+  @JRubyMethod(name = "decode_json", required = 1, optional = 1, meta = true)
+  public static IRubyObject decodeJson(
+      ThreadContext context, IRubyObject recv, IRubyObject[] args) {
+    Ruby runtime = context.runtime;
+    boolean ignoreUnknownFields = false;
+    IRubyObject data = args[0];
+    JsonFormat.Parser parser = JsonFormat.parser();
+
+    if (args.length == 2) {
+      if (!(args[1] instanceof RubyHash)) {
+        throw runtime.newArgumentError("Expected hash arguments.");
+      }
+
+      IRubyObject ignoreSetting =
+          ((RubyHash) args[1]).fastARef(runtime.newSymbol("ignore_unknown_fields"));
+      if (ignoreSetting != null && ignoreSetting.isTrue()) {
+        parser = parser.ignoringUnknownFields();
+      }
+    }
+
+    if (!(data instanceof RubyString)) {
+      throw runtime.newArgumentError("Expected string for JSON data.");
+    }
+
+    RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK);
+    parser =
+        parser.usingTypeRegistry(JsonFormat.TypeRegistry.newBuilder().add(ret.descriptor).build());
+
+    try {
+      parser.merge(data.asJavaString(), ret.builder);
+    } catch (InvalidProtocolBufferException e) {
+      throw createParseError(context, e.getMessage().replace("Cannot find", "No such"));
+    }
+
+    if (isWrapper(ret.descriptor)) {
+      throw runtime.newRuntimeError(
+          "Parsing a wrapper type from JSON at the top level does not work.");
+    }
+
+    return ret;
+  }
+
+  @JRubyMethod(name = "to_h")
+  public IRubyObject toHash(ThreadContext context) {
+    Ruby runtime = context.runtime;
+    RubyHash ret = RubyHash.newHash(runtime);
+    for (FieldDescriptor fdef : this.descriptor.getFields()) {
+      IRubyObject value = getFieldInternal(context, fdef, proto3);
+
+      if (!value.isNil()) {
+        if (fdef.isRepeated() && !fdef.isMapField()) {
+          if (!proto3 && ((RubyRepeatedField) value).size() == 0)
+            continue; // Don't output empty repeated fields for proto2
+          if (fdef.getType() != FieldDescriptor.Type.MESSAGE) {
+            value = Helpers.invoke(context, value, "to_a");
+          } else {
+            RubyArray ary = value.convertToArray();
+            for (int i = 0; i < ary.size(); i++) {
+              IRubyObject submsg = Helpers.invoke(context, ary.eltInternal(i), "to_h");
+              ary.eltInternalSet(i, submsg);
+            }
+
+            value = ary.to_ary();
+          }
+        } else if (value.respondsTo("to_h")) {
+          value = Helpers.invoke(context, value, "to_h");
+        } else if (value.respondsTo("to_a")) {
+          value = Helpers.invoke(context, value, "to_a");
+        }
+      }
+      if (proto3 || !value.isNil()) {
+        ret.fastASet(runtime.newSymbol(fdef.getName()), value);
+      }
+    }
+    return ret;
+  }
+
+  protected DynamicMessage build(ThreadContext context, int depth, int recursionLimit) {
+    if (depth >= recursionLimit) {
+      throw context.runtime.newRuntimeError("Recursion limit exceeded during encoding.");
+    }
+
+    RubySymbol typeBytesSymbol = RubySymbol.newSymbol(context.runtime, "TYPE_BYTES");
+
+    // Handle the typical case where the fields.keySet contain the fieldDescriptors
+    for (FieldDescriptor fieldDescriptor : fields.keySet()) {
+      IRubyObject value = fields.get(fieldDescriptor);
+
+      if (value instanceof RubyMap) {
+        builder.clearField(fieldDescriptor);
+        RubyDescriptor mapDescriptor =
+            (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
+        for (DynamicMessage kv :
+            ((RubyMap) value).build(context, mapDescriptor, depth, recursionLimit)) {
+          builder.addRepeatedField(fieldDescriptor, kv);
+        }
+
+      } else if (value instanceof RubyRepeatedField) {
+        RubyRepeatedField repeatedField = (RubyRepeatedField) value;
+
+        builder.clearField(fieldDescriptor);
+        for (int i = 0; i < repeatedField.size(); i++) {
+          Object item =
+              convert(
+                  context,
+                  fieldDescriptor,
+                  repeatedField.get(i),
+                  depth,
+                  recursionLimit,
+                  /*isDefaultValueForBytes*/ false);
+          builder.addRepeatedField(fieldDescriptor, item);
+        }
+
+      } else if (!value.isNil()) {
+        /**
+         * Detect the special case where default_value strings are provided for byte fields. If so,
+         * disable normal string encoding behavior within convert. For a more detailed explanation
+         * of other possible workarounds, see the comments above {@code
+         * com.google.protobuf.Internal#stringDefaultValue() stringDefaultValue}.
+         */
+        boolean isDefaultStringForBytes = false;
+        if (DEFAULT_VALUE.equals(fieldDescriptor.getFullName())) {
+          FieldDescriptor enumFieldDescriptorForType =
+              this.builder.getDescriptorForType().findFieldByName(TYPE);
+          if (typeBytesSymbol.equals(fields.get(enumFieldDescriptorForType))) {
+            isDefaultStringForBytes = true;
+          }
+        }
+        builder.setField(
+            fieldDescriptor,
+            convert(
+                context, fieldDescriptor, value, depth, recursionLimit, isDefaultStringForBytes));
+      }
+    }
+
+    // Handle cases where {@code fields} doesn't contain the value until after getFieldInternal
+    // is called - typical of a deserialized message. Skip non-maps and descriptors that already
+    // have an entry in {@code fields}.
+    for (FieldDescriptor fieldDescriptor : descriptor.getFields()) {
+      if (!fieldDescriptor.isMapField()) {
+        continue;
+      }
+      IRubyObject value = fields.get(fieldDescriptor);
+      if (value != null) {
+        continue;
+      }
+      value = getFieldInternal(context, fieldDescriptor);
+      if (value instanceof RubyMap) {
+        builder.clearField(fieldDescriptor);
+        RubyDescriptor mapDescriptor =
+            (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
+        for (DynamicMessage kv :
+            ((RubyMap) value).build(context, mapDescriptor, depth, recursionLimit)) {
+          builder.addRepeatedField(fieldDescriptor, kv);
+        }
+      }
+    }
+    return builder.build();
+  }
+
+  // Internal use only, called by Google::Protobuf.deep_copy
+  protected IRubyObject deepCopy(ThreadContext context) {
+    RubyMessage copy = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK);
+    for (FieldDescriptor fdef : descriptor.getFields()) {
+      if (fdef.isRepeated()) {
+        copy.fields.put(fdef, this.getRepeatedField(context, fdef).deepCopy(context));
+      } else if (fields.containsKey(fdef)) {
+        copy.setFieldInternal(context, fdef, fields.get(fdef));
+      } else if (builder.hasField(fdef)) {
+        copy.fields.put(fdef, wrapField(context, fdef, builder.getField(fdef)));
+      }
+    }
+    return copy;
+  }
+
+  protected IRubyObject clearField(ThreadContext context, FieldDescriptor fieldDescriptor) {
+    validateMessageType(context, fieldDescriptor, "clear");
+    return clearFieldInternal(context, fieldDescriptor);
+  }
+
+  protected void discardUnknownFields(ThreadContext context) {
+    discardUnknownFields(context, builder);
+  }
+
+  protected IRubyObject getField(ThreadContext context, FieldDescriptor fieldDescriptor) {
+    validateMessageType(context, fieldDescriptor, "get");
+    return getFieldInternal(context, fieldDescriptor);
+  }
+
+  protected IRubyObject hasField(ThreadContext context, FieldDescriptor fieldDescriptor) {
+    validateMessageType(context, fieldDescriptor, "has?");
+    if (!fieldDescriptor.hasPresence()) {
+      throw context.runtime.newArgumentError("does not track presence");
+    }
+    return fields.containsKey(fieldDescriptor)
+        ? context.runtime.getTrue()
+        : context.runtime.getFalse();
+  }
+
+  protected IRubyObject setField(
+      ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) {
+    validateMessageType(context, fieldDescriptor, "set");
+    return setFieldInternal(context, fieldDescriptor, value);
+  }
+
+  private RubyRepeatedField getRepeatedField(
+      ThreadContext context, FieldDescriptor fieldDescriptor) {
+    if (fields.containsKey(fieldDescriptor)) {
+      return (RubyRepeatedField) fields.get(fieldDescriptor);
+    }
+    int count = this.builder.getRepeatedFieldCount(fieldDescriptor);
+    RubyRepeatedField ret = repeatedFieldForFieldDescriptor(context, fieldDescriptor);
+    for (int i = 0; i < count; i++) {
+      ret.push(
+          context,
+          new IRubyObject[] {
+            wrapField(context, fieldDescriptor, this.builder.getRepeatedField(fieldDescriptor, i))
+          });
+    }
+    fields.put(fieldDescriptor, ret);
+    return ret;
+  }
+
+  private IRubyObject buildFrom(ThreadContext context, DynamicMessage dynamicMessage) {
+    this.builder.mergeFrom(dynamicMessage);
+    return this;
+  }
+
+  private IRubyObject clearFieldInternal(ThreadContext context, FieldDescriptor fieldDescriptor) {
+    OneofDescriptor ood = fieldDescriptor.getContainingOneof();
+    if (ood != null) oneofCases.remove(ood);
+    fields.remove(fieldDescriptor);
+    builder.clearField(fieldDescriptor);
+    return context.nil;
+  }
+
+  private void discardUnknownFields(ThreadContext context, Message.Builder messageBuilder) {
+    messageBuilder.setUnknownFields(UnknownFieldSet.getDefaultInstance());
+    messageBuilder
+        .getAllFields()
+        .forEach(
+            (fd, value) -> {
+              if (fd.getType() == FieldDescriptor.Type.MESSAGE) {
+                if (fd.isRepeated()) {
+                  messageBuilder.clearField(fd);
+                  ((List) value)
+                      .forEach(
+                          (val) -> {
+                            Message.Builder submessageBuilder = ((DynamicMessage) val).toBuilder();
+                            discardUnknownFields(context, submessageBuilder);
+                            messageBuilder.addRepeatedField(fd, submessageBuilder.build());
+                          });
+                } else {
+                  Message.Builder submessageBuilder = ((DynamicMessage) value).toBuilder();
+                  discardUnknownFields(context, submessageBuilder);
+                  messageBuilder.setField(fd, submessageBuilder.build());
+                }
+              }
+            });
+  }
+
+  private FieldDescriptor findField(ThreadContext context, IRubyObject fieldName) {
+    return findField(context, fieldName, false);
+  }
+
+  private FieldDescriptor findField(
+      ThreadContext context, IRubyObject fieldName, boolean ignoreUnknownField) {
+    String nameStr = fieldName.asJavaString();
+    FieldDescriptor ret = this.descriptor.findFieldByName(nameStr);
+    if (ret == null && !ignoreUnknownField) {
+      throw context.runtime.newArgumentError("field " + fieldName.asJavaString() + " is not found");
+    }
+    return ret;
+  }
+
+  // convert a ruby object to protobuf type, skip type check since it is checked on the way in
+  private Object convert(
+      ThreadContext context,
+      FieldDescriptor fieldDescriptor,
+      IRubyObject value,
+      int depth,
+      int recursionLimit,
+      boolean isDefaultStringForBytes) {
+    Object val = null;
+    switch (fieldDescriptor.getType()) {
+      case INT32:
+      case SFIXED32:
+      case SINT32:
+        val = RubyNumeric.num2int(value);
+        break;
+      case INT64:
+      case SFIXED64:
+      case SINT64:
+        val = RubyNumeric.num2long(value);
+        break;
+      case FIXED32:
+      case UINT32:
+        val = Utils.num2uint(value);
+        break;
+      case FIXED64:
+      case UINT64:
+        val = Utils.num2ulong(context.runtime, value);
+        break;
+      case FLOAT:
+        val = (float) RubyNumeric.num2dbl(value);
+        break;
+      case DOUBLE:
+        val = (double) RubyNumeric.num2dbl(value);
+        break;
+      case BOOL:
+        val = value.isTrue();
+        break;
+      case BYTES:
+        val = ByteString.copyFrom(((RubyString) value).getBytes());
+        break;
+      case STRING:
+        if (isDefaultStringForBytes) {
+          val = ((RubyString) value).getByteList().toString();
+        } else {
+          val = value.asJavaString();
+        }
+        break;
+      case MESSAGE:
+        val = ((RubyMessage) value).build(context, depth + 1, recursionLimit);
+        break;
+      case ENUM:
+        EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType();
+        if (Utils.isRubyNum(value)) {
+          val = enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
+        } else {
+          val = enumDescriptor.findValueByName(value.asJavaString());
+        }
+        break;
+      default:
+        break;
+    }
+
+    return val;
+  }
+
+  private static RaiseException createParseError(ThreadContext context, String message) {
+    if (parseErrorClass == null) {
+      parseErrorClass =
+          (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::ParseError");
+    }
+    return RaiseException.from(context.runtime, parseErrorClass, message);
+  }
+
+  private IRubyObject wrapField(
+      ThreadContext context, FieldDescriptor fieldDescriptor, Object value) {
+    return wrapField(context, fieldDescriptor, value, false);
+  }
+
+  private IRubyObject wrapField(
+      ThreadContext context, FieldDescriptor fieldDescriptor, Object value, boolean encodeBytes) {
+    if (value == null) {
+      return context.runtime.getNil();
+    }
+    Ruby runtime = context.runtime;
+
+    switch (fieldDescriptor.getType()) {
+      case INT32:
+      case INT64:
+      case FIXED32:
+      case SINT32:
+      case FIXED64:
+      case SINT64:
+      case SFIXED64:
+      case SFIXED32:
+      case UINT32:
+      case UINT64:
+      case FLOAT:
+      case DOUBLE:
+      case BOOL:
+      case BYTES:
+      case STRING:
+        return Utils.wrapPrimaryValue(context, fieldDescriptor.getType(), value, encodeBytes);
+      case MESSAGE:
+        RubyClass typeClass =
+            (RubyClass)
+                ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor))
+                    .msgclass(context);
+        RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK);
+        return msg.buildFrom(context, (DynamicMessage) value);
+      case ENUM:
+        EnumValueDescriptor enumValueDescriptor = (EnumValueDescriptor) value;
+        if (enumValueDescriptor.getIndex() == -1) { // UNKNOWN ENUM VALUE
+          return runtime.newFixnum(enumValueDescriptor.getNumber());
+        }
+        return runtime.newSymbol(enumValueDescriptor.getName());
+      default:
+        return runtime.newString(value.toString());
+    }
+  }
+
+  private RubyRepeatedField repeatedFieldForFieldDescriptor(
+      ThreadContext context, FieldDescriptor fieldDescriptor) {
+    IRubyObject typeClass = context.runtime.getNilClass();
+    IRubyObject descriptor = getDescriptorForField(context, fieldDescriptor);
+    FieldDescriptor.Type type = fieldDescriptor.getType();
+
+    if (type == FieldDescriptor.Type.MESSAGE) {
+      typeClass = ((RubyDescriptor) descriptor).msgclass(context);
+
+    } else if (type == FieldDescriptor.Type.ENUM) {
+      typeClass = ((RubyEnumDescriptor) descriptor).enummodule(context);
+    }
+
+    RubyRepeatedField field =
+        new RubyRepeatedField(context.runtime, cRepeatedField, type, typeClass);
+    field.setName(fieldDescriptor.getName());
+
+    return field;
+  }
+
+  private IRubyObject getFieldInternal(ThreadContext context, FieldDescriptor fieldDescriptor) {
+    return getFieldInternal(context, fieldDescriptor, true);
+  }
+
+  private IRubyObject getFieldInternal(
+      ThreadContext context, FieldDescriptor fieldDescriptor, boolean returnDefaults) {
+    OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof();
+    if (oneofDescriptor != null) {
+      if (oneofCases.get(oneofDescriptor) == fieldDescriptor) {
+        IRubyObject value = fields.get(fieldDescriptor);
+        if (value == null) {
+          FieldDescriptor oneofCase = builder.getOneofFieldDescriptor(oneofDescriptor);
+          if (oneofCase != null) {
+            Object builderValue = builder.getField(oneofCase);
+            if (builderValue != null) {
+              boolean encodeBytes =
+                  oneofCase.hasDefaultValue() && builderValue.equals(oneofCase.getDefaultValue());
+              value = wrapField(context, oneofCase, builderValue, encodeBytes);
+            }
+          }
+          if (value == null) {
+            return context.nil;
+          } else {
+            return value;
+          }
+        } else {
+          return value;
+        }
+      } else {
+        FieldDescriptor oneofCase = builder.getOneofFieldDescriptor(oneofDescriptor);
+        if (oneofCase != fieldDescriptor) {
+          if (fieldDescriptor.getType() == FieldDescriptor.Type.MESSAGE || !returnDefaults) {
+            return context.nil;
+          } else {
+            return wrapField(context, fieldDescriptor, fieldDescriptor.getDefaultValue(), true);
+          }
+        }
+        if (returnDefaults || builder.hasField(fieldDescriptor)) {
+          Object rawValue = builder.getField(oneofCase);
+          boolean encodeBytes =
+              oneofCase.hasDefaultValue() && rawValue.equals(oneofCase.getDefaultValue());
+          IRubyObject value = wrapField(context, oneofCase, rawValue, encodeBytes);
+          fields.put(fieldDescriptor, value);
+          return value;
+        } else {
+          return context.nil;
+        }
+      }
+    }
+
+    if (fieldDescriptor.isMapField()) {
+      RubyMap map = (RubyMap) fields.get(fieldDescriptor);
+      if (map == null) {
+        map = newMapForField(context, fieldDescriptor);
+        int mapSize = this.builder.getRepeatedFieldCount(fieldDescriptor);
+        FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1);
+        FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2);
+        RubyDescriptor kvDescriptor =
+            (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
+        RubyClass kvClass = (RubyClass) kvDescriptor.msgclass(context);
+        for (int i = 0; i < mapSize; i++) {
+          RubyMessage kvMessage = (RubyMessage) kvClass.newInstance(context, Block.NULL_BLOCK);
+          DynamicMessage message =
+              (DynamicMessage) this.builder.getRepeatedField(fieldDescriptor, i);
+          kvMessage.buildFrom(context, message);
+          map.indexSet(
+              context,
+              kvMessage.getField(context, keyField),
+              kvMessage.getField(context, valueField));
+        }
+        fields.put(fieldDescriptor, map);
+      }
+      return map;
+    }
+
+    if (fieldDescriptor.isRepeated()) {
+      return getRepeatedField(context, fieldDescriptor);
+    }
+
+    if (fieldDescriptor.getType() != FieldDescriptor.Type.MESSAGE
+        || builder.hasField(fieldDescriptor)
+        || fields.containsKey(fieldDescriptor)) {
+      if (fields.containsKey(fieldDescriptor)) {
+        return fields.get(fieldDescriptor);
+      } else if (returnDefaults || builder.hasField(fieldDescriptor)) {
+        Object rawValue = builder.getField(fieldDescriptor);
+        boolean encodeBytes =
+            fieldDescriptor.hasDefaultValue() && rawValue.equals(fieldDescriptor.getDefaultValue());
+        IRubyObject value = wrapField(context, fieldDescriptor, rawValue, encodeBytes);
+        if (builder.hasField(fieldDescriptor)) {
+          fields.put(fieldDescriptor, value);
+        }
+        return value;
+      }
+    }
+    return context.nil;
+  }
+
+  private IRubyObject setFieldInternal(
+      ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) {
+    testFrozen("can't modify frozen " + getMetaClass());
+
+    if (fieldDescriptor.isMapField()) {
+      if (!(value instanceof RubyMap)) {
+        throw Utils.createTypeError(context, "Expected Map instance");
+      }
+      RubyMap thisMap = (RubyMap) getFieldInternal(context, fieldDescriptor);
+      thisMap.mergeIntoSelf(context, value);
+
+    } else if (fieldDescriptor.isRepeated()) {
+      if (value instanceof RubyRepeatedField) {
+        fields.put(fieldDescriptor, value);
+      } else {
+        throw Utils.createTypeError(context, "Expected repeated field array");
+      }
+
+    } else {
+      boolean addValue = true;
+      FieldDescriptor.Type fieldType = fieldDescriptor.getType();
+      OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof();
+
+      // Determine the typeclass, if any
+      IRubyObject typeClass = context.runtime.getObject();
+      if (fieldType == FieldDescriptor.Type.MESSAGE) {
+        typeClass =
+            ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
+        if (value.isNil()) {
+          addValue = false;
+        }
+      } else if (fieldType == FieldDescriptor.Type.ENUM) {
+        typeClass =
+            ((RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor))
+                .enummodule(context);
+        value = enumToSymbol(context, fieldDescriptor.getEnumType(), value);
+      }
+
+      if (oneofDescriptor != null) {
+        FieldDescriptor oneofCase = oneofCases.get(oneofDescriptor);
+
+        // Remove the existing field if we are setting a different field in the Oneof
+        if (oneofCase != null && oneofCase != fieldDescriptor) {
+          fields.remove(oneofCase);
+        }
+
+        // Keep track of what Oneofs are set
+        if (value.isNil()) {
+          oneofCases.remove(oneofDescriptor);
+          if (!oneofDescriptor.isSynthetic()) {
+            addValue = false;
+          }
+        } else {
+          oneofCases.put(oneofDescriptor, fieldDescriptor);
+        }
+      }
+
+      if (addValue) {
+        value =
+            Utils.checkType(
+                context, fieldType, fieldDescriptor.getName(), value, (RubyModule) typeClass);
+        fields.put(fieldDescriptor, value);
+      } else {
+        fields.remove(fieldDescriptor);
+      }
+    }
+    return context.nil;
+  }
+
+  private IRubyObject getDescriptorForField(
+      ThreadContext context, FieldDescriptor fieldDescriptor) {
+    RubyDescriptor thisRbDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
+    RubyFieldDescriptor fd =
+        (RubyFieldDescriptor)
+            thisRbDescriptor.lookup(context, context.runtime.newString(fieldDescriptor.getName()));
+    return fd.getSubtype(context);
+  }
+
+  private IRubyObject enumToSymbol(
+      ThreadContext context, EnumDescriptor enumDescriptor, IRubyObject value) {
+    if (value instanceof RubySymbol) {
+      return (RubySymbol) value;
+    } else if (Utils.isRubyNum(value)) {
+      EnumValueDescriptor enumValue =
+          enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
+      if (enumValue.getIndex() != -1) {
+        return context.runtime.newSymbol(enumValue.getName());
+      } else {
+        return value;
+      }
+    } else if (value instanceof RubyString) {
+      return ((RubyString) value).intern();
+    }
+
+    return context.runtime.newSymbol("UNKNOWN");
+  }
+
+  private RubyRepeatedField rubyToRepeatedField(
+      ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) {
+    RubyArray arr = value.convertToArray();
+    RubyRepeatedField repeatedField = repeatedFieldForFieldDescriptor(context, fieldDescriptor);
+    IRubyObject[] values = new IRubyObject[arr.size()];
+    FieldDescriptor.Type fieldType = fieldDescriptor.getType();
+
+    RubyModule typeClass = null;
+    if (fieldType == FieldDescriptor.Type.MESSAGE) {
+      RubyDescriptor descriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
+      typeClass = (RubyModule) descriptor.msgclass(context);
+    } else if (fieldType == FieldDescriptor.Type.ENUM) {
+      RubyEnumDescriptor enumDescriptor =
+          (RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor);
+      typeClass = (RubyModule) enumDescriptor.enummodule(context);
+    }
+
+    for (int i = 0; i < arr.size(); i++) {
+      IRubyObject item = arr.eltInternal(i);
+      if (item.isNil()) {
+        throw Utils.createTypeError(context, "nil message not allowed here.");
+      }
+      if (item instanceof RubyHash && typeClass != null) {
+        values[i] = ((RubyClass) typeClass).newInstance(context, item, Block.NULL_BLOCK);
+      } else {
+        if (fieldType == FieldDescriptor.Type.ENUM) {
+          item = enumToSymbol(context, fieldDescriptor.getEnumType(), item);
+        }
+
+        values[i] = item;
+      }
+    }
+    repeatedField.push(context, values);
+
+    return repeatedField;
+  }
+
+  private RubyMap newMapForField(ThreadContext context, FieldDescriptor fieldDescriptor) {
+    RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
+    FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1);
+    FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2);
+    IRubyObject keyType = RubySymbol.newSymbol(context.runtime, keyField.getType().name());
+    IRubyObject valueType = RubySymbol.newSymbol(context.runtime, valueField.getType().name());
+
+    if (valueField.getType() == FieldDescriptor.Type.MESSAGE) {
+      RubyFieldDescriptor rubyFieldDescriptor =
+          (RubyFieldDescriptor) mapDescriptor.lookup(context, context.runtime.newString("value"));
+      RubyDescriptor rubyDescriptor = (RubyDescriptor) rubyFieldDescriptor.getSubtype(context);
+      return (RubyMap)
+          cMap.newInstance(
+              context, keyType, valueType, rubyDescriptor.msgclass(context), Block.NULL_BLOCK);
+
+    } else if (valueField.getType() == FieldDescriptor.Type.ENUM) {
+      RubyFieldDescriptor rubyFieldDescriptor =
+          (RubyFieldDescriptor) mapDescriptor.lookup(context, context.runtime.newString("value"));
+      RubyEnumDescriptor rubyEnumDescriptor =
+          (RubyEnumDescriptor) rubyFieldDescriptor.getSubtype(context);
+      return (RubyMap)
+          cMap.newInstance(
+              context,
+              keyType,
+              valueType,
+              rubyEnumDescriptor.enummodule(context),
+              Block.NULL_BLOCK);
+
+    } else {
+      return (RubyMap) cMap.newInstance(context, keyType, valueType, Block.NULL_BLOCK);
+    }
+  }
+
+  private boolean isWrappable(FieldDescriptor fieldDescriptor) {
+    if (fieldDescriptor.getType() != FieldDescriptor.Type.MESSAGE) return false;
+
+    return isWrapper(fieldDescriptor.getMessageType());
+  }
+
+  private static boolean isWrapper(Descriptor messageDescriptor) {
+    switch (messageDescriptor.getFullName()) {
+      case "google.protobuf.DoubleValue":
+      case "google.protobuf.FloatValue":
+      case "google.protobuf.Int64Value":
+      case "google.protobuf.UInt64Value":
+      case "google.protobuf.Int32Value":
+      case "google.protobuf.UInt32Value":
+      case "google.protobuf.BoolValue":
+      case "google.protobuf.StringValue":
+      case "google.protobuf.BytesValue":
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  private void validateMessageType(
+      ThreadContext context, FieldDescriptor fieldDescriptor, String methodName) {
+    if (descriptor != fieldDescriptor.getContainingType()) {
+      throw Utils.createTypeError(context, methodName + " method called on wrong message type");
+    }
+  }
+
+  private static RubyClass parseErrorClass;
+
+  private static final String AS_VALUE_SUFFIX = "_as_value";
+  private static final String CLEAR_PREFIX = "clear_";
+  private static final String CONST_SUFFIX = "_const";
+  private static final String HAS_PREFIX = "has_";
+  private static final String QUESTION_MARK = "?";
+  private static final int SINK_MAXIMUM_NESTING = 64;
+
+  private Descriptor descriptor;
+  private DynamicMessage.Builder builder;
+  private Map<FieldDescriptor, IRubyObject> fields;
+  private Map<OneofDescriptor, FieldDescriptor> oneofCases;
+  private RubyClass cRepeatedField;
+  private RubyClass cMap;
+  private boolean ignoreUnknownFieldsOnInit = false;
+  private boolean proto3;
 }
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofDescriptor.java
index 6f2ebdb..5ade98b 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofDescriptor.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofDescriptor.java
@@ -2,6 +2,10 @@
 
 import com.google.protobuf.Descriptors.FieldDescriptor;
 import com.google.protobuf.Descriptors.OneofDescriptor;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 import org.jruby.Ruby;
 import org.jruby.RubyClass;
 import org.jruby.RubyModule;
@@ -13,74 +17,76 @@
 import org.jruby.runtime.ThreadContext;
 import org.jruby.runtime.builtin.IRubyObject;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
 @JRubyClass(name = "OneofDescriptor", include = "Enumerable")
 public class RubyOneofDescriptor extends RubyObject {
 
-    public static void createRubyOneofDescriptor(Ruby runtime) {
-        RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
-        RubyClass cRubyOneofDescriptor = protobuf.defineClassUnder("OneofDescriptor", runtime.getObject(), new ObjectAllocator() {
-            @Override
-            public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
+  public static void createRubyOneofDescriptor(Ruby runtime) {
+    RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
+    RubyClass cRubyOneofDescriptor =
+        protobuf.defineClassUnder(
+            "OneofDescriptor",
+            runtime.getObject(),
+            new ObjectAllocator() {
+              @Override
+              public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
                 return new RubyOneofDescriptor(ruby, rubyClass);
-            }
-        });
-        cRubyOneofDescriptor.defineAnnotatedMethods(RubyOneofDescriptor.class);
-        cRubyOneofDescriptor.includeModule(runtime.getEnumerable());
+              }
+            });
+    cRubyOneofDescriptor.defineAnnotatedMethods(RubyOneofDescriptor.class);
+    cRubyOneofDescriptor.includeModule(runtime.getEnumerable());
+  }
+
+  public RubyOneofDescriptor(Ruby ruby, RubyClass rubyClass) {
+    super(ruby, rubyClass);
+    fields = new ArrayList<RubyFieldDescriptor>();
+  }
+
+  /*
+   * call-seq:
+   *     OneofDescriptor.name => name
+   *
+   * Returns the name of this oneof.
+   */
+  @JRubyMethod(name = "name")
+  public IRubyObject getName(ThreadContext context) {
+    return name;
+  }
+
+  /*
+   * call-seq:
+   *     OneofDescriptor.each(&block) => nil
+   *
+   * Iterates through fields in this oneof, yielding to the block on each one.
+   */
+  @JRubyMethod
+  public IRubyObject each(ThreadContext context, Block block) {
+    for (RubyFieldDescriptor field : fields) {
+      block.yieldSpecific(context, field);
     }
+    return context.nil;
+  }
 
-    public RubyOneofDescriptor(Ruby ruby, RubyClass rubyClass) {
-        super(ruby, rubyClass);
-        fields = new ArrayList<RubyFieldDescriptor>();
+  protected Collection<RubyFieldDescriptor> getFields() {
+    return fields;
+  }
+
+  protected OneofDescriptor getDescriptor() {
+    return descriptor;
+  }
+
+  protected void setDescriptor(
+      ThreadContext context,
+      OneofDescriptor descriptor,
+      Map<FieldDescriptor, RubyFieldDescriptor> fieldCache) {
+    this.descriptor = descriptor;
+    this.name = context.runtime.newString(descriptor.getName());
+
+    for (FieldDescriptor fd : descriptor.getFields()) {
+      fields.add(fieldCache.get(fd));
     }
+  }
 
-    /*
-     * call-seq:
-     *     OneofDescriptor.name => name
-     *
-     * Returns the name of this oneof.
-     */
-    @JRubyMethod(name = "name")
-    public IRubyObject getName(ThreadContext context) {
-        return name;
-    }
-
-    /*
-     * call-seq:
-     *     OneofDescriptor.each(&block) => nil
-     *
-     * Iterates through fields in this oneof, yielding to the block on each one.
-     */
-    @JRubyMethod
-    public IRubyObject each(ThreadContext context, Block block) {
-        for (RubyFieldDescriptor field : fields) {
-            block.yieldSpecific(context, field);
-        }
-        return context.nil;
-    }
-
-    protected Collection<RubyFieldDescriptor> getFields() {
-        return fields;
-    }
-
-    protected OneofDescriptor getDescriptor() {
-        return descriptor;
-    }
-
-    protected void setDescriptor(ThreadContext context, OneofDescriptor descriptor, Map<FieldDescriptor, RubyFieldDescriptor> fieldCache) {
-        this.descriptor = descriptor;
-        this.name = context.runtime.newString(descriptor.getName());
-
-        for (FieldDescriptor fd : descriptor.getFields()) {
-            fields.add(fieldCache.get(fd));
-        }
-    }
-
-    private IRubyObject name;
-    private List<RubyFieldDescriptor> fields;
-    private OneofDescriptor descriptor;
+  private IRubyObject name;
+  private List<RubyFieldDescriptor> fields;
+  private OneofDescriptor descriptor;
 }
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java
index 582c675..8d132be 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java
@@ -42,41 +42,42 @@
 @JRubyModule(name = "Protobuf")
 public class RubyProtobuf {
 
-    public static void createProtobuf(Ruby runtime) {
-        RubyModule mGoogle = runtime.getModule("Google");
-        RubyModule mProtobuf = mGoogle.defineModuleUnder("Protobuf");
-        mProtobuf.defineAnnotatedMethods(RubyProtobuf.class);
-        RubyModule mInternal = mProtobuf.defineModuleUnder("Internal");
-    }
+  public static void createProtobuf(Ruby runtime) {
+    RubyModule mGoogle = runtime.getModule("Google");
+    RubyModule mProtobuf = mGoogle.defineModuleUnder("Protobuf");
+    mProtobuf.defineAnnotatedMethods(RubyProtobuf.class);
+    RubyModule mInternal = mProtobuf.defineModuleUnder("Internal");
+  }
 
-    /*
-     * call-seq:
-     *     Google::Protobuf.deep_copy(obj) => copy_of_obj
-     *
-     * Performs a deep copy of either a RepeatedField instance or a message object,
-     * recursively copying its members.
-     */
-    @JRubyMethod(name = "deep_copy", meta = true)
-    public static IRubyObject deepCopy(ThreadContext context, IRubyObject self, IRubyObject message) {
-        if (message instanceof RubyMessage) {
-            return ((RubyMessage) message).deepCopy(context);
-        } else if (message instanceof RubyRepeatedField) {
-            return ((RubyRepeatedField) message).deepCopy(context);
-        } else {
-            return ((RubyMap) message).deepCopy(context);
-        }
+  /*
+   * call-seq:
+   *     Google::Protobuf.deep_copy(obj) => copy_of_obj
+   *
+   * Performs a deep copy of either a RepeatedField instance or a message object,
+   * recursively copying its members.
+   */
+  @JRubyMethod(name = "deep_copy", meta = true)
+  public static IRubyObject deepCopy(ThreadContext context, IRubyObject self, IRubyObject message) {
+    if (message instanceof RubyMessage) {
+      return ((RubyMessage) message).deepCopy(context);
+    } else if (message instanceof RubyRepeatedField) {
+      return ((RubyRepeatedField) message).deepCopy(context);
+    } else {
+      return ((RubyMap) message).deepCopy(context);
     }
+  }
 
-    /*
-     * call-seq:
-     *     Google::Protobuf.discard_unknown(msg)
-     *
-     * Discard unknown fields in the given message object and recursively discard
-     * unknown fields in submessages.
-     */
-    @JRubyMethod(name = "discard_unknown", meta = true)
-    public static IRubyObject discardUnknown(ThreadContext context, IRubyObject self, IRubyObject message) {
-        ((RubyMessage) message).discardUnknownFields(context);
-        return context.nil;
-    }
+  /*
+   * call-seq:
+   *     Google::Protobuf.discard_unknown(msg)
+   *
+   * Discard unknown fields in the given message object and recursively discard
+   * unknown fields in submessages.
+   */
+  @JRubyMethod(name = "discard_unknown", meta = true)
+  public static IRubyObject discardUnknown(
+      ThreadContext context, IRubyObject self, IRubyObject message) {
+    ((RubyMessage) message).discardUnknownFields(context);
+    return context.nil;
+  }
 }
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java
index 995171f..883d480 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java
@@ -40,384 +40,391 @@
 import org.jruby.runtime.ObjectAllocator;
 import org.jruby.runtime.ThreadContext;
 import org.jruby.runtime.builtin.IRubyObject;
-import java.util.Arrays;
 
 @JRubyClass(name = "RepeatedClass", include = "Enumerable")
 public class RubyRepeatedField extends RubyObject {
-    public static void createRubyRepeatedField(Ruby runtime) {
-        RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
-        RubyClass cRepeatedField = mProtobuf.defineClassUnder("RepeatedField", runtime.getObject(),
-                new ObjectAllocator() {
-                    @Override
-                    public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
-                        return new RubyRepeatedField(runtime, klazz);
-                    }
-                });
-        cRepeatedField.defineAnnotatedMethods(RubyRepeatedField.class);
-        cRepeatedField.includeModule(runtime.getEnumerable());
+  public static void createRubyRepeatedField(Ruby runtime) {
+    RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
+    RubyClass cRepeatedField =
+        mProtobuf.defineClassUnder(
+            "RepeatedField",
+            runtime.getObject(),
+            new ObjectAllocator() {
+              @Override
+              public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                return new RubyRepeatedField(runtime, klazz);
+              }
+            });
+    cRepeatedField.defineAnnotatedMethods(RubyRepeatedField.class);
+    cRepeatedField.includeModule(runtime.getEnumerable());
+  }
+
+  public RubyRepeatedField(Ruby runtime, RubyClass klazz) {
+    super(runtime, klazz);
+  }
+
+  public RubyRepeatedField(
+      Ruby runtime, RubyClass klazz, FieldDescriptor.Type fieldType, IRubyObject typeClass) {
+    this(runtime, klazz);
+    this.fieldType = fieldType;
+    this.storage = runtime.newArray();
+    this.typeClass = typeClass;
+  }
+
+  @JRubyMethod(required = 1, optional = 2)
+  public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
+    Ruby runtime = context.runtime;
+    this.storage = runtime.newArray();
+    IRubyObject ary = null;
+    if (!(args[0] instanceof RubySymbol)) {
+      throw runtime.newArgumentError("Expected Symbol for type name");
+    }
+    this.fieldType = Utils.rubyToFieldType(args[0]);
+    if (fieldType == FieldDescriptor.Type.MESSAGE || fieldType == FieldDescriptor.Type.ENUM) {
+      if (args.length < 2)
+        throw runtime.newArgumentError("Expected at least 2 arguments for message/enum");
+      typeClass = args[1];
+      if (args.length > 2) ary = args[2];
+      Utils.validateTypeClass(context, fieldType, typeClass);
+    } else {
+      if (args.length > 2) throw runtime.newArgumentError("Too many arguments: expected 1 or 2");
+      if (args.length > 1) ary = args[1];
+    }
+    if (ary != null) {
+      RubyArray arr = ary.convertToArray();
+      for (int i = 0; i < arr.size(); i++) {
+        this.storage.add(arr.eltInternal(i));
+      }
+    }
+    return this;
+  }
+
+  /*
+   * call-seq:
+   *     RepeatedField.[]=(index, value)
+   *
+   * Sets the element at the given index. On out-of-bounds assignments, extends
+   * the array and fills the hole (if any) with default values.
+   */
+  @JRubyMethod(name = "[]=")
+  public IRubyObject indexSet(ThreadContext context, IRubyObject index, IRubyObject value) {
+    int arrIndex = normalizeArrayIndex(index);
+    value = Utils.checkType(context, fieldType, name, value, (RubyModule) typeClass);
+    IRubyObject defaultValue = defaultValue(context);
+    for (int i = this.storage.size(); i < arrIndex; i++) {
+      this.storage.set(i, defaultValue);
+    }
+    this.storage.set(arrIndex, value);
+    return context.runtime.getNil();
+  }
+
+  /*
+   * call-seq:
+   *     RepeatedField.[](index) => value
+   *
+   * Accesses the element at the given index. Returns nil on out-of-bounds
+   */
+  @JRubyMethod(
+      required = 1,
+      optional = 1,
+      name = {"at", "[]"})
+  public IRubyObject index(ThreadContext context, IRubyObject[] args) {
+    if (args.length == 1) {
+      IRubyObject arg = args[0];
+      if (Utils.isRubyNum(arg)) {
+        /* standard case */
+        int arrIndex = normalizeArrayIndex(arg);
+        if (arrIndex < 0 || arrIndex >= this.storage.size()) {
+          return context.runtime.getNil();
+        }
+        return this.storage.eltInternal(arrIndex);
+      } else if (arg instanceof RubyRange) {
+        RubyRange range = ((RubyRange) arg);
+
+        int beg = RubyNumeric.num2int(range.first(context));
+        int len = RubyNumeric.num2int(range.size(context));
+
+        if (len == 0) return context.runtime.newEmptyArray();
+
+        return this.storage.subseq(beg, len);
+      }
+    }
+    /* assume 2 arguments */
+    int beg = RubyNumeric.num2int(args[0]);
+    int len = RubyNumeric.num2int(args[1]);
+    if (beg < 0) {
+      beg += this.storage.size();
+    }
+    if (beg >= this.storage.size()) {
+      return context.runtime.getNil();
+    }
+    return this.storage.subseq(beg, len);
+  }
+
+  /*
+   * call-seq:
+   *     RepeatedField.push(value)
+   *
+   * Adds a new element to the repeated field.
+   */
+  @JRubyMethod(
+      name = {"push", "<<"},
+      required = 1,
+      rest = true)
+  public IRubyObject push(ThreadContext context, IRubyObject[] args) {
+    for (int i = 0; i < args.length; i++) {
+      IRubyObject val = args[i];
+      if (fieldType != FieldDescriptor.Type.MESSAGE || !val.isNil()) {
+        val = Utils.checkType(context, fieldType, name, val, (RubyModule) typeClass);
+      }
+      storage.add(val);
     }
 
-    public RubyRepeatedField(Ruby runtime, RubyClass klazz) {
-        super(runtime, klazz);
-    }
+    return this;
+  }
 
-    public RubyRepeatedField(Ruby runtime, RubyClass klazz, FieldDescriptor.Type fieldType, IRubyObject typeClass) {
-        this(runtime, klazz);
-        this.fieldType = fieldType;
-        this.storage = runtime.newArray();
-        this.typeClass = typeClass;
-    }
+  /*
+   * private Ruby method used by RepeatedField.pop
+   */
+  @JRubyMethod(visibility = org.jruby.runtime.Visibility.PRIVATE)
+  public IRubyObject pop_one(ThreadContext context) {
+    IRubyObject ret = this.storage.last();
+    this.storage.remove(ret);
+    return ret;
+  }
 
-    @JRubyMethod(required = 1, optional = 2)
-    public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
-        Ruby runtime = context.runtime;
-        this.storage = runtime.newArray();
-        IRubyObject ary = null;
-        if (!(args[0] instanceof RubySymbol)) {
-            throw runtime.newArgumentError("Expected Symbol for type name");
-        }
-        this.fieldType = Utils.rubyToFieldType(args[0]);
-        if (fieldType == FieldDescriptor.Type.MESSAGE
-                || fieldType == FieldDescriptor.Type.ENUM) {
-            if (args.length < 2)
-                throw runtime.newArgumentError("Expected at least 2 arguments for message/enum");
-            typeClass = args[1];
-            if (args.length > 2)
-                ary = args[2];
-            Utils.validateTypeClass(context, fieldType, typeClass);
-        } else {
-            if (args.length > 2)
-                throw runtime.newArgumentError("Too many arguments: expected 1 or 2");
-            if (args.length > 1)
-                ary = args[1];
-        }
-        if (ary != null) {
-            RubyArray arr = ary.convertToArray();
-            for (int i = 0; i < arr.size(); i++) {
-                this.storage.add(arr.eltInternal(i));
-            }
-        }
-        return this;
-    }
+  /*
+   * call-seq:
+   *     RepeatedField.replace(list)
+   *
+   * Replaces the contents of the repeated field with the given list of elements.
+   */
+  @JRubyMethod
+  public IRubyObject replace(ThreadContext context, IRubyObject list) {
+    RubyArray arr = (RubyArray) list;
+    checkArrayElementType(context, arr);
+    this.storage = arr;
+    return this;
+  }
 
-    /*
-     * call-seq:
-     *     RepeatedField.[]=(index, value)
-     *
-     * Sets the element at the given index. On out-of-bounds assignments, extends
-     * the array and fills the hole (if any) with default values.
-     */
-    @JRubyMethod(name = "[]=")
-    public IRubyObject indexSet(ThreadContext context, IRubyObject index, IRubyObject value) {
-        int arrIndex = normalizeArrayIndex(index);
-        value = Utils.checkType(context, fieldType, name, value, (RubyModule) typeClass);
-        IRubyObject defaultValue = defaultValue(context);
-        for (int i = this.storage.size(); i < arrIndex; i++) {
-            this.storage.set(i, defaultValue);
-        }
-        this.storage.set(arrIndex, value);
+  /*
+   * call-seq:
+   *     RepeatedField.clear
+   *
+   * Clears (removes all elements from) this repeated field.
+   */
+  @JRubyMethod
+  public IRubyObject clear(ThreadContext context) {
+    this.storage.clear();
+    return this;
+  }
+
+  /*
+   * call-seq:
+   *     RepeatedField.length
+   *
+   * Returns the length of this repeated field.
+   */
+  @JRubyMethod(name = {"length", "size"})
+  public IRubyObject length(ThreadContext context) {
+    return context.runtime.newFixnum(this.storage.size());
+  }
+
+  /*
+   * call-seq:
+   *     RepeatedField.+(other) => repeated field
+   *
+   * Returns a new repeated field that contains the concatenated list of this
+   * repeated field's elements and other's elements. The other (second) list may
+   * be either another repeated field or a Ruby array.
+   */
+  @JRubyMethod(name = {"+"})
+  public IRubyObject plus(ThreadContext context, IRubyObject list) {
+    RubyRepeatedField dup = (RubyRepeatedField) dup(context);
+    if (list instanceof RubyArray) {
+      checkArrayElementType(context, (RubyArray) list);
+      dup.storage.addAll((RubyArray) list);
+    } else {
+      RubyRepeatedField repeatedField = (RubyRepeatedField) list;
+      if (!fieldType.equals(repeatedField.fieldType)
+          || (typeClass != null && !typeClass.equals(repeatedField.typeClass)))
+        throw context.runtime.newArgumentError(
+            "Attempt to append RepeatedField with different element type.");
+      dup.storage.addAll((RubyArray) repeatedField.toArray(context));
+    }
+    return dup;
+  }
+
+  /*
+   * call-seq:
+   *     RepeatedField.concat(other) => self
+   *
+   * concats the passed in array to self.  Returns a Ruby array.
+   */
+  @JRubyMethod
+  public IRubyObject concat(ThreadContext context, IRubyObject list) {
+    if (list instanceof RubyArray) {
+      checkArrayElementType(context, (RubyArray) list);
+      this.storage.addAll((RubyArray) list);
+    } else {
+      RubyRepeatedField repeatedField = (RubyRepeatedField) list;
+      if (!fieldType.equals(repeatedField.fieldType)
+          || (typeClass != null && !typeClass.equals(repeatedField.typeClass)))
+        throw context.runtime.newArgumentError(
+            "Attempt to append RepeatedField with different element type.");
+      this.storage.addAll((RubyArray) repeatedField.toArray(context));
+    }
+    return this;
+  }
+
+  /*
+   * call-seq:
+   *     RepeatedField.hash => hash_value
+   *
+   * Returns a hash value computed from this repeated field's elements.
+   */
+  @JRubyMethod
+  public IRubyObject hash(ThreadContext context) {
+    int hashCode = this.storage.hashCode();
+    return context.runtime.newFixnum(hashCode);
+  }
+
+  /*
+   * call-seq:
+   *     RepeatedField.==(other) => boolean
+   *
+   * Compares this repeated field to another. Repeated fields are equal if their
+   * element types are equal, their lengths are equal, and each element is equal.
+   * Elements are compared as per normal Ruby semantics, by calling their :==
+   * methods (or performing a more efficient comparison for primitive types).
+   */
+  @JRubyMethod(name = "==")
+  public IRubyObject eq(ThreadContext context, IRubyObject other) {
+    return this.toArray(context).op_equal(context, other);
+  }
+
+  /*
+   * call-seq:
+   *     RepeatedField.each(&block)
+   *
+   * Invokes the block once for each element of the repeated field. RepeatedField
+   * also includes Enumerable; combined with this method, the repeated field thus
+   * acts like an ordinary Ruby sequence.
+   */
+  @JRubyMethod
+  public IRubyObject each(ThreadContext context, Block block) {
+    this.storage.each(context, block);
+    return this;
+  }
+
+  @JRubyMethod(name = {"to_ary", "to_a"})
+  public IRubyObject toArray(ThreadContext context) {
+    return this.storage;
+  }
+
+  /*
+   * call-seq:
+   *     RepeatedField.dup => repeated_field
+   *
+   * Duplicates this repeated field with a shallow copy. References to all
+   * non-primitive element objects (e.g., submessages) are shared.
+   */
+  @JRubyMethod
+  public IRubyObject dup(ThreadContext context) {
+    RubyRepeatedField dup = new RubyRepeatedField(context.runtime, metaClass, fieldType, typeClass);
+    dup.push(context, storage.toJavaArray());
+    return dup;
+  }
+
+  @JRubyMethod
+  public IRubyObject inspect() {
+    return storage.inspect();
+  }
+
+  // Java API
+  protected IRubyObject get(int index) {
+    return this.storage.eltInternal(index);
+  }
+
+  protected RubyRepeatedField deepCopy(ThreadContext context) {
+    RubyRepeatedField copy =
+        new RubyRepeatedField(context.runtime, metaClass, fieldType, typeClass);
+    for (int i = 0; i < size(); i++) {
+      IRubyObject value = storage.eltInternal(i);
+      if (fieldType == FieldDescriptor.Type.MESSAGE) {
+        copy.storage.add(((RubyMessage) value).deepCopy(context));
+      } else {
+        copy.storage.add(value);
+      }
+    }
+    return copy;
+  }
+
+  protected void setName(String name) {
+    this.name = name;
+  }
+
+  protected int size() {
+    return this.storage.size();
+  }
+
+  private IRubyObject defaultValue(ThreadContext context) {
+    SentinelOuterClass.Sentinel sentinel = SentinelOuterClass.Sentinel.getDefaultInstance();
+    Object value;
+    switch (fieldType) {
+      case INT32:
+        value = sentinel.getDefaultInt32();
+        break;
+      case INT64:
+        value = sentinel.getDefaultInt64();
+        break;
+      case UINT32:
+        value = sentinel.getDefaultUnit32();
+        break;
+      case UINT64:
+        value = sentinel.getDefaultUint64();
+        break;
+      case FLOAT:
+        value = sentinel.getDefaultFloat();
+        break;
+      case DOUBLE:
+        value = sentinel.getDefaultDouble();
+        break;
+      case BOOL:
+        value = sentinel.getDefaultBool();
+        break;
+      case BYTES:
+        value = sentinel.getDefaultBytes();
+        break;
+      case STRING:
+        value = sentinel.getDefaultString();
+        break;
+      case ENUM:
+        IRubyObject defaultEnumLoc = context.runtime.newFixnum(0);
+        return RubyEnum.lookup(context, typeClass, defaultEnumLoc);
+      default:
         return context.runtime.getNil();
     }
+    return Utils.wrapPrimaryValue(context, fieldType, value);
+  }
 
-    /*
-     * call-seq:
-     *     RepeatedField.[](index) => value
-     *
-     * Accesses the element at the given index. Returns nil on out-of-bounds
-     */
-    @JRubyMethod(required=1, optional=1, name = {"at", "[]"})
-    public IRubyObject index(ThreadContext context, IRubyObject[] args) {
-        if (args.length == 1){
-            IRubyObject arg = args[0];
-            if (Utils.isRubyNum(arg)) {
-                /* standard case */
-                int arrIndex = normalizeArrayIndex(arg);
-                if (arrIndex < 0 || arrIndex >= this.storage.size()) {
-                    return context.runtime.getNil();
-                }
-                return this.storage.eltInternal(arrIndex);
-            } else if (arg instanceof RubyRange) {
-                RubyRange range = ((RubyRange) arg);
-
-                int beg = RubyNumeric.num2int(range.first(context));
-                int len = RubyNumeric.num2int(range.size(context));
-
-                if (len == 0) return context.runtime.newEmptyArray();
-
-                return this.storage.subseq(beg, len);
-            }
-        }
-        /* assume 2 arguments */
-        int beg = RubyNumeric.num2int(args[0]);
-        int len = RubyNumeric.num2int(args[1]);
-        if (beg < 0) {
-            beg += this.storage.size();
-        }
-        if (beg >= this.storage.size()) {
-            return context.runtime.getNil();
-        }
-        return this.storage.subseq(beg, len);
+  private void checkArrayElementType(ThreadContext context, RubyArray arr) {
+    for (int i = 0; i < arr.getLength(); i++) {
+      Utils.checkType(context, fieldType, name, arr.eltInternal(i), (RubyModule) typeClass);
     }
+  }
 
-    /*
-     * call-seq:
-     *     RepeatedField.push(value)
-     *
-     * Adds a new element to the repeated field.
-     */
-    @JRubyMethod(name = {"push", "<<"}, required = 1, rest = true)
-    public IRubyObject push(ThreadContext context, IRubyObject[] args) {
-        for (int i = 0; i < args.length; i++) {
-            IRubyObject val = args[i];
-            if (fieldType != FieldDescriptor.Type.MESSAGE || !val.isNil()) {
-                val = Utils.checkType(context, fieldType, name, val, (RubyModule) typeClass);
-            }
-            storage.add(val);
-        }
-
-        return this;
+  private int normalizeArrayIndex(IRubyObject index) {
+    int arrIndex = RubyNumeric.num2int(index);
+    int arrSize = this.storage.size();
+    if (arrIndex < 0 && arrSize > 0) {
+      arrIndex = arrSize + arrIndex;
     }
+    return arrIndex;
+  }
 
-    /*
-     * private Ruby method used by RepeatedField.pop
-     */
-    @JRubyMethod(visibility = org.jruby.runtime.Visibility.PRIVATE)
-    public IRubyObject pop_one(ThreadContext context) {
-        IRubyObject ret = this.storage.last();
-        this.storage.remove(ret);
-        return ret;
-    }
-
-    /*
-     * call-seq:
-     *     RepeatedField.replace(list)
-     *
-     * Replaces the contents of the repeated field with the given list of elements.
-     */
-    @JRubyMethod
-    public IRubyObject replace(ThreadContext context, IRubyObject list) {
-        RubyArray arr = (RubyArray) list;
-        checkArrayElementType(context, arr);
-        this.storage = arr;
-        return this;
-    }
-
-    /*
-     * call-seq:
-     *     RepeatedField.clear
-     *
-     * Clears (removes all elements from) this repeated field.
-     */
-    @JRubyMethod
-    public IRubyObject clear(ThreadContext context) {
-        this.storage.clear();
-        return this;
-    }
-
-    /*
-     * call-seq:
-     *     RepeatedField.length
-     *
-     * Returns the length of this repeated field.
-     */
-    @JRubyMethod(name = {"length", "size"})
-    public IRubyObject length(ThreadContext context) {
-        return context.runtime.newFixnum(this.storage.size());
-    }
-
-    /*
-     * call-seq:
-     *     RepeatedField.+(other) => repeated field
-     *
-     * Returns a new repeated field that contains the concatenated list of this
-     * repeated field's elements and other's elements. The other (second) list may
-     * be either another repeated field or a Ruby array.
-     */
-    @JRubyMethod(name = {"+"})
-    public IRubyObject plus(ThreadContext context, IRubyObject list) {
-        RubyRepeatedField dup = (RubyRepeatedField) dup(context);
-        if (list instanceof RubyArray) {
-            checkArrayElementType(context, (RubyArray) list);
-            dup.storage.addAll((RubyArray) list);
-        } else {
-            RubyRepeatedField repeatedField = (RubyRepeatedField) list;
-            if (! fieldType.equals(repeatedField.fieldType) || (typeClass != null && !
-                    typeClass.equals(repeatedField.typeClass)))
-                throw context.runtime.newArgumentError("Attempt to append RepeatedField with different element type.");
-            dup.storage.addAll((RubyArray) repeatedField.toArray(context));
-        }
-        return dup;
-    }
-
-    /*
-     * call-seq:
-     *     RepeatedField.concat(other) => self
-     *
-     * concats the passed in array to self.  Returns a Ruby array.
-     */
-    @JRubyMethod
-    public IRubyObject concat(ThreadContext context, IRubyObject list) {
-        if (list instanceof RubyArray) {
-            checkArrayElementType(context, (RubyArray) list);
-            this.storage.addAll((RubyArray) list);
-        } else {
-            RubyRepeatedField repeatedField = (RubyRepeatedField) list;
-            if (! fieldType.equals(repeatedField.fieldType) || (typeClass != null && !
-                    typeClass.equals(repeatedField.typeClass)))
-                throw context.runtime.newArgumentError("Attempt to append RepeatedField with different element type.");
-            this.storage.addAll((RubyArray) repeatedField.toArray(context));
-        }
-        return this;
-    }
-
-    /*
-     * call-seq:
-     *     RepeatedField.hash => hash_value
-     *
-     * Returns a hash value computed from this repeated field's elements.
-     */
-    @JRubyMethod
-    public IRubyObject hash(ThreadContext context) {
-        int hashCode = this.storage.hashCode();
-        return context.runtime.newFixnum(hashCode);
-    }
-
-    /*
-     * call-seq:
-     *     RepeatedField.==(other) => boolean
-     *
-     * Compares this repeated field to another. Repeated fields are equal if their
-     * element types are equal, their lengths are equal, and each element is equal.
-     * Elements are compared as per normal Ruby semantics, by calling their :==
-     * methods (or performing a more efficient comparison for primitive types).
-     */
-    @JRubyMethod(name = "==")
-    public IRubyObject eq(ThreadContext context, IRubyObject other) {
-        return this.toArray(context).op_equal(context, other);
-    }
-
-    /*
-     * call-seq:
-     *     RepeatedField.each(&block)
-     *
-     * Invokes the block once for each element of the repeated field. RepeatedField
-     * also includes Enumerable; combined with this method, the repeated field thus
-     * acts like an ordinary Ruby sequence.
-     */
-    @JRubyMethod
-    public IRubyObject each(ThreadContext context, Block block) {
-        this.storage.each(context, block);
-        return this;
-    }
-
-
-    @JRubyMethod(name = {"to_ary", "to_a"})
-    public IRubyObject toArray(ThreadContext context) {
-        return this.storage;
-    }
-
-    /*
-     * call-seq:
-     *     RepeatedField.dup => repeated_field
-     *
-     * Duplicates this repeated field with a shallow copy. References to all
-     * non-primitive element objects (e.g., submessages) are shared.
-     */
-    @JRubyMethod
-    public IRubyObject dup(ThreadContext context) {
-        RubyRepeatedField dup = new RubyRepeatedField(context.runtime, metaClass, fieldType, typeClass);
-        dup.push(context, storage.toJavaArray());
-        return dup;
-    }
-
-    @JRubyMethod
-    public IRubyObject inspect() {
-        return storage.inspect();
-    }
-
-    // Java API
-    protected IRubyObject get(int index) {
-        return this.storage.eltInternal(index);
-    }
-
-    protected RubyRepeatedField deepCopy(ThreadContext context) {
-        RubyRepeatedField copy = new RubyRepeatedField(context.runtime, metaClass, fieldType, typeClass);
-        for (int i = 0; i < size(); i++) {
-            IRubyObject value = storage.eltInternal(i);
-            if (fieldType == FieldDescriptor.Type.MESSAGE) {
-                copy.storage.add(((RubyMessage) value).deepCopy(context));
-            } else {
-                copy.storage.add(value);
-            }
-        }
-        return copy;
-    }
-
-    protected void setName(String name) {
-        this.name = name;
-    }
-
-    protected int size() {
-        return this.storage.size();
-    }
-
-    private IRubyObject defaultValue(ThreadContext context) {
-        SentinelOuterClass.Sentinel sentinel = SentinelOuterClass.Sentinel.getDefaultInstance();
-        Object value;
-        switch (fieldType) {
-            case INT32:
-                value = sentinel.getDefaultInt32();
-                break;
-            case INT64:
-                value = sentinel.getDefaultInt64();
-                break;
-            case UINT32:
-                value = sentinel.getDefaultUnit32();
-                break;
-            case UINT64:
-                value = sentinel.getDefaultUint64();
-                break;
-            case FLOAT:
-                value = sentinel.getDefaultFloat();
-                break;
-            case DOUBLE:
-                value = sentinel.getDefaultDouble();
-                break;
-            case BOOL:
-                value = sentinel.getDefaultBool();
-                break;
-            case BYTES:
-                value = sentinel.getDefaultBytes();
-                break;
-            case STRING:
-                value = sentinel.getDefaultString();
-                break;
-            case ENUM:
-                IRubyObject defaultEnumLoc = context.runtime.newFixnum(0);
-                return RubyEnum.lookup(context, typeClass, defaultEnumLoc);
-            default:
-                return context.runtime.getNil();
-        }
-        return Utils.wrapPrimaryValue(context, fieldType, value);
-    }
-
-    private void checkArrayElementType(ThreadContext context, RubyArray arr) {
-        for (int i = 0; i < arr.getLength(); i++) {
-            Utils.checkType(context, fieldType, name, arr.eltInternal(i), (RubyModule) typeClass);
-        }
-    }
-
-    private int normalizeArrayIndex(IRubyObject index) {
-        int arrIndex = RubyNumeric.num2int(index);
-        int arrSize = this.storage.size();
-        if (arrIndex < 0 && arrSize > 0) {
-            arrIndex = arrSize + arrIndex;
-        }
-        return arrIndex;
-    }
-
-    private FieldDescriptor.Type fieldType;
-    private IRubyObject typeClass;
-    private RubyArray storage;
-    private String name;
+  private FieldDescriptor.Type fieldType;
+  private IRubyObject typeClass;
+  private RubyArray storage;
+  private String name;
 }
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/SentinelOuterClass.java b/ruby/src/main/java/com/google/protobuf/jruby/SentinelOuterClass.java
index b3f23c5..3c56cf8 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/SentinelOuterClass.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/SentinelOuterClass.java
@@ -37,74 +37,53 @@
 
 public final class SentinelOuterClass {
   private SentinelOuterClass() {}
-  public static void registerAllExtensions(
-      com.google.protobuf.ExtensionRegistry registry) {
-  }
-  public interface SentinelOrBuilder extends
+
+  public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) {}
+
+  public interface SentinelOrBuilder
+      extends
       // @@protoc_insertion_point(interface_extends:com.google.protobuf.jruby.Sentinel)
       com.google.protobuf.MessageOrBuilder {
 
-    /**
-     * <code>optional int32 default_int32 = 1;</code>
-     */
+    /** <code>optional int32 default_int32 = 1;</code> */
     int getDefaultInt32();
 
-    /**
-     * <code>optional int64 default_int64 = 2;</code>
-     */
+    /** <code>optional int64 default_int64 = 2;</code> */
     long getDefaultInt64();
 
-    /**
-     * <code>optional uint32 default_unit32 = 3;</code>
-     */
+    /** <code>optional uint32 default_unit32 = 3;</code> */
     int getDefaultUnit32();
 
-    /**
-     * <code>optional uint64 default_uint64 = 4;</code>
-     */
+    /** <code>optional uint64 default_uint64 = 4;</code> */
     long getDefaultUint64();
 
-    /**
-     * <code>optional string default_string = 5;</code>
-     */
+    /** <code>optional string default_string = 5;</code> */
     java.lang.String getDefaultString();
-    /**
-     * <code>optional string default_string = 5;</code>
-     */
-    com.google.protobuf.ByteString
-        getDefaultStringBytes();
+    /** <code>optional string default_string = 5;</code> */
+    com.google.protobuf.ByteString getDefaultStringBytes();
 
-    /**
-     * <code>optional bool default_bool = 6;</code>
-     */
+    /** <code>optional bool default_bool = 6;</code> */
     boolean getDefaultBool();
 
-    /**
-     * <code>optional float default_float = 7;</code>
-     */
+    /** <code>optional float default_float = 7;</code> */
     float getDefaultFloat();
 
-    /**
-     * <code>optional double default_double = 8;</code>
-     */
+    /** <code>optional double default_double = 8;</code> */
     double getDefaultDouble();
 
-    /**
-     * <code>optional bytes default_bytes = 9;</code>
-     */
+    /** <code>optional bytes default_bytes = 9;</code> */
     com.google.protobuf.ByteString getDefaultBytes();
   }
-  /**
-   * Protobuf type {@code com.google.protobuf.jruby.Sentinel}
-   */
-  public  static final class Sentinel extends
-      com.google.protobuf.GeneratedMessage implements
+  /** Protobuf type {@code com.google.protobuf.jruby.Sentinel} */
+  public static final class Sentinel extends com.google.protobuf.GeneratedMessage
+      implements
       // @@protoc_insertion_point(message_implements:com.google.protobuf.jruby.Sentinel)
       SentinelOrBuilder {
     // Use Sentinel.newBuilder() to construct.
     private Sentinel(com.google.protobuf.GeneratedMessage.Builder builder) {
       super(builder);
     }
+
     private Sentinel() {
       defaultInt32_ = 0;
       defaultInt64_ = 0L;
@@ -118,40 +97,42 @@
     }
 
     @java.lang.Override
-    public final com.google.protobuf.UnknownFieldSet
-    getUnknownFields() {
+    public final com.google.protobuf.UnknownFieldSet getUnknownFields() {
       return com.google.protobuf.UnknownFieldSet.getDefaultInstance();
     }
-    public static final com.google.protobuf.Descriptors.Descriptor
-        getDescriptor() {
-      return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
+
+    public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
+      return com.google.protobuf.jruby.SentinelOuterClass
+          .internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
     }
 
     protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
         internalGetFieldAccessorTable() {
-      return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable
+      return com.google.protobuf.jruby.SentinelOuterClass
+          .internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable
           .ensureFieldAccessorsInitialized(
-              com.google.protobuf.jruby.SentinelOuterClass.Sentinel.class, com.google.protobuf.jruby.SentinelOuterClass.Sentinel.Builder.class);
+              com.google.protobuf.jruby.SentinelOuterClass.Sentinel.class,
+              com.google.protobuf.jruby.SentinelOuterClass.Sentinel.Builder.class);
     }
 
     public static final com.google.protobuf.Parser<Sentinel> PARSER =
         new com.google.protobuf.AbstractParser<Sentinel>() {
-      public Sentinel parsePartialFrom(
-          com.google.protobuf.CodedInputStream input,
-          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
-          throws com.google.protobuf.InvalidProtocolBufferException {
-        Builder builder = newBuilder();
-        try {
-          builder.mergeFrom(input, extensionRegistry);
-        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-          throw e.setUnfinishedMessage(builder.buildPartial());
-        } catch (java.io.IOException e) {
-          throw new com.google.protobuf.InvalidProtocolBufferException(
-              e.getMessage()).setUnfinishedMessage(builder.buildPartial());
-        }
-        return builder.buildPartial();
-      }
-    };
+          public Sentinel parsePartialFrom(
+              com.google.protobuf.CodedInputStream input,
+              com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+              throws com.google.protobuf.InvalidProtocolBufferException {
+            Builder builder = newBuilder();
+            try {
+              builder.mergeFrom(input, extensionRegistry);
+            } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+              throw e.setUnfinishedMessage(builder.buildPartial());
+            } catch (java.io.IOException e) {
+              throw new com.google.protobuf.InvalidProtocolBufferException(e.getMessage())
+                  .setUnfinishedMessage(builder.buildPartial());
+            }
+            return builder.buildPartial();
+          }
+        };
 
     @java.lang.Override
     public com.google.protobuf.Parser<Sentinel> getParserForType() {
@@ -160,52 +141,41 @@
 
     public static final int DEFAULT_INT32_FIELD_NUMBER = 1;
     private int defaultInt32_;
-    /**
-     * <code>optional int32 default_int32 = 1;</code>
-     */
+    /** <code>optional int32 default_int32 = 1;</code> */
     public int getDefaultInt32() {
       return defaultInt32_;
     }
 
     public static final int DEFAULT_INT64_FIELD_NUMBER = 2;
     private long defaultInt64_;
-    /**
-     * <code>optional int64 default_int64 = 2;</code>
-     */
+    /** <code>optional int64 default_int64 = 2;</code> */
     public long getDefaultInt64() {
       return defaultInt64_;
     }
 
     public static final int DEFAULT_UNIT32_FIELD_NUMBER = 3;
     private int defaultUnit32_;
-    /**
-     * <code>optional uint32 default_unit32 = 3;</code>
-     */
+    /** <code>optional uint32 default_unit32 = 3;</code> */
     public int getDefaultUnit32() {
       return defaultUnit32_;
     }
 
     public static final int DEFAULT_UINT64_FIELD_NUMBER = 4;
     private long defaultUint64_;
-    /**
-     * <code>optional uint64 default_uint64 = 4;</code>
-     */
+    /** <code>optional uint64 default_uint64 = 4;</code> */
     public long getDefaultUint64() {
       return defaultUint64_;
     }
 
     public static final int DEFAULT_STRING_FIELD_NUMBER = 5;
     private java.lang.Object defaultString_;
-    /**
-     * <code>optional string default_string = 5;</code>
-     */
+    /** <code>optional string default_string = 5;</code> */
     public java.lang.String getDefaultString() {
       java.lang.Object ref = defaultString_;
       if (ref instanceof java.lang.String) {
         return (java.lang.String) ref;
       } else {
-        com.google.protobuf.ByteString bs =
-            (com.google.protobuf.ByteString) ref;
+        com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
         java.lang.String s = bs.toStringUtf8();
         if (bs.isValidUtf8()) {
           defaultString_ = s;
@@ -213,16 +183,12 @@
         return s;
       }
     }
-    /**
-     * <code>optional string default_string = 5;</code>
-     */
-    public com.google.protobuf.ByteString
-        getDefaultStringBytes() {
+    /** <code>optional string default_string = 5;</code> */
+    public com.google.protobuf.ByteString getDefaultStringBytes() {
       java.lang.Object ref = defaultString_;
       if (ref instanceof java.lang.String) {
         com.google.protobuf.ByteString b =
-            com.google.protobuf.ByteString.copyFromUtf8(
-                (java.lang.String) ref);
+            com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
         defaultString_ = b;
         return b;
       } else {
@@ -232,36 +198,28 @@
 
     public static final int DEFAULT_BOOL_FIELD_NUMBER = 6;
     private boolean defaultBool_;
-    /**
-     * <code>optional bool default_bool = 6;</code>
-     */
+    /** <code>optional bool default_bool = 6;</code> */
     public boolean getDefaultBool() {
       return defaultBool_;
     }
 
     public static final int DEFAULT_FLOAT_FIELD_NUMBER = 7;
     private float defaultFloat_;
-    /**
-     * <code>optional float default_float = 7;</code>
-     */
+    /** <code>optional float default_float = 7;</code> */
     public float getDefaultFloat() {
       return defaultFloat_;
     }
 
     public static final int DEFAULT_DOUBLE_FIELD_NUMBER = 8;
     private double defaultDouble_;
-    /**
-     * <code>optional double default_double = 8;</code>
-     */
+    /** <code>optional double default_double = 8;</code> */
     public double getDefaultDouble() {
       return defaultDouble_;
     }
 
     public static final int DEFAULT_BYTES_FIELD_NUMBER = 9;
     private com.google.protobuf.ByteString defaultBytes_;
-    /**
-     * <code>optional bytes default_bytes = 9;</code>
-     */
+    /** <code>optional bytes default_bytes = 9;</code> */
     public com.google.protobuf.ByteString getDefaultBytes() {
       return defaultBytes_;
     }
@@ -271,47 +229,52 @@
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
+
     public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
         com.google.protobuf.ByteString data,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
+
     public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(byte[] data)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data);
     }
+
     public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
-        byte[] data,
-        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       return PARSER.parseFrom(data, extensionRegistry);
     }
-    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(java.io.InputStream input)
-        throws java.io.IOException {
+
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
+        java.io.InputStream input) throws java.io.IOException {
       return PARSER.parseFrom(input);
     }
+
     public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
-        java.io.InputStream input,
-        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return PARSER.parseFrom(input, extensionRegistry);
     }
-    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseDelimitedFrom(java.io.InputStream input)
-        throws java.io.IOException {
+
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseDelimitedFrom(
+        java.io.InputStream input) throws java.io.IOException {
       return PARSER.parseDelimitedFrom(input);
     }
+
     public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseDelimitedFrom(
-        java.io.InputStream input,
-        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws java.io.IOException {
       return PARSER.parseDelimitedFrom(input, extensionRegistry);
     }
+
     public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
-        com.google.protobuf.CodedInputStream input)
-        throws java.io.IOException {
+        com.google.protobuf.CodedInputStream input) throws java.io.IOException {
       return PARSER.parseFrom(input);
     }
+
     public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
         com.google.protobuf.CodedInputStream input,
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
@@ -319,36 +282,45 @@
       return PARSER.parseFrom(input, extensionRegistry);
     }
 
-    public static Builder newBuilder() { return new Builder(); }
-    public Builder newBuilderForType() { return newBuilder(); }
-    public static Builder newBuilder(com.google.protobuf.jruby.SentinelOuterClass.Sentinel prototype) {
+    public static Builder newBuilder() {
+      return new Builder();
+    }
+
+    public Builder newBuilderForType() {
+      return newBuilder();
+    }
+
+    public static Builder newBuilder(
+        com.google.protobuf.jruby.SentinelOuterClass.Sentinel prototype) {
       return newBuilder().mergeFrom(prototype);
     }
-    public Builder toBuilder() { return newBuilder(this); }
+
+    public Builder toBuilder() {
+      return newBuilder(this);
+    }
 
     @java.lang.Override
-    protected Builder newBuilderForType(
-        com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+    protected Builder newBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent) {
       Builder builder = new Builder(parent);
       return builder;
     }
-    /**
-     * Protobuf type {@code com.google.protobuf.jruby.Sentinel}
-     */
-    public static final class Builder extends
-        com.google.protobuf.GeneratedMessage.Builder<Builder> implements
+    /** Protobuf type {@code com.google.protobuf.jruby.Sentinel} */
+    public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder<Builder>
+        implements
         // @@protoc_insertion_point(builder_implements:com.google.protobuf.jruby.Sentinel)
         com.google.protobuf.jruby.SentinelOuterClass.SentinelOrBuilder {
-      public static final com.google.protobuf.Descriptors.Descriptor
-          getDescriptor() {
-        return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
+      public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
+        return com.google.protobuf.jruby.SentinelOuterClass
+            .internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
       }
 
       protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
           internalGetFieldAccessorTable() {
-        return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable
+        return com.google.protobuf.jruby.SentinelOuterClass
+            .internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable
             .ensureFieldAccessorsInitialized(
-                com.google.protobuf.jruby.SentinelOuterClass.Sentinel.class, com.google.protobuf.jruby.SentinelOuterClass.Sentinel.Builder.class);
+                com.google.protobuf.jruby.SentinelOuterClass.Sentinel.class,
+                com.google.protobuf.jruby.SentinelOuterClass.Sentinel.Builder.class);
       }
 
       // Construct using com.google.protobuf.jruby.SentinelOuterClass.Sentinel.newBuilder()
@@ -356,15 +328,15 @@
         maybeForceBuilderInitialization();
       }
 
-      private Builder(
-          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+      private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) {
         super(parent);
         maybeForceBuilderInitialization();
       }
+
       private void maybeForceBuilderInitialization() {
-        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
-        }
+        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {}
       }
+
       public Builder clear() {
         super.clear();
         defaultInt32_ = 0;
@@ -388,9 +360,9 @@
         return this;
       }
 
-      public com.google.protobuf.Descriptors.Descriptor
-          getDescriptorForType() {
-        return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
+      public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() {
+        return com.google.protobuf.jruby.SentinelOuterClass
+            .internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
       }
 
       public com.google.protobuf.jruby.SentinelOuterClass.Sentinel getDefaultInstanceForType() {
@@ -406,7 +378,8 @@
       }
 
       public com.google.protobuf.jruby.SentinelOuterClass.Sentinel buildPartial() {
-        com.google.protobuf.jruby.SentinelOuterClass.Sentinel result = new com.google.protobuf.jruby.SentinelOuterClass.Sentinel(this);
+        com.google.protobuf.jruby.SentinelOuterClass.Sentinel result =
+            new com.google.protobuf.jruby.SentinelOuterClass.Sentinel(this);
         result.defaultInt32_ = defaultInt32_;
         result.defaultInt64_ = defaultInt64_;
         result.defaultUnit32_ = defaultUnit32_;
@@ -420,26 +393,19 @@
         return result;
       }
 
-
-      private int defaultInt32_ ;
-      /**
-       * <code>optional int32 default_int32 = 1;</code>
-       */
+      private int defaultInt32_;
+      /** <code>optional int32 default_int32 = 1;</code> */
       public int getDefaultInt32() {
         return defaultInt32_;
       }
-      /**
-       * <code>optional int32 default_int32 = 1;</code>
-       */
+      /** <code>optional int32 default_int32 = 1;</code> */
       public Builder setDefaultInt32(int value) {
 
         defaultInt32_ = value;
         onChanged();
         return this;
       }
-      /**
-       * <code>optional int32 default_int32 = 1;</code>
-       */
+      /** <code>optional int32 default_int32 = 1;</code> */
       public Builder clearDefaultInt32() {
 
         defaultInt32_ = 0;
@@ -447,25 +413,19 @@
         return this;
       }
 
-      private long defaultInt64_ ;
-      /**
-       * <code>optional int64 default_int64 = 2;</code>
-       */
+      private long defaultInt64_;
+      /** <code>optional int64 default_int64 = 2;</code> */
       public long getDefaultInt64() {
         return defaultInt64_;
       }
-      /**
-       * <code>optional int64 default_int64 = 2;</code>
-       */
+      /** <code>optional int64 default_int64 = 2;</code> */
       public Builder setDefaultInt64(long value) {
 
         defaultInt64_ = value;
         onChanged();
         return this;
       }
-      /**
-       * <code>optional int64 default_int64 = 2;</code>
-       */
+      /** <code>optional int64 default_int64 = 2;</code> */
       public Builder clearDefaultInt64() {
 
         defaultInt64_ = 0L;
@@ -473,25 +433,19 @@
         return this;
       }
 
-      private int defaultUnit32_ ;
-      /**
-       * <code>optional uint32 default_unit32 = 3;</code>
-       */
+      private int defaultUnit32_;
+      /** <code>optional uint32 default_unit32 = 3;</code> */
       public int getDefaultUnit32() {
         return defaultUnit32_;
       }
-      /**
-       * <code>optional uint32 default_unit32 = 3;</code>
-       */
+      /** <code>optional uint32 default_unit32 = 3;</code> */
       public Builder setDefaultUnit32(int value) {
 
         defaultUnit32_ = value;
         onChanged();
         return this;
       }
-      /**
-       * <code>optional uint32 default_unit32 = 3;</code>
-       */
+      /** <code>optional uint32 default_unit32 = 3;</code> */
       public Builder clearDefaultUnit32() {
 
         defaultUnit32_ = 0;
@@ -499,25 +453,19 @@
         return this;
       }
 
-      private long defaultUint64_ ;
-      /**
-       * <code>optional uint64 default_uint64 = 4;</code>
-       */
+      private long defaultUint64_;
+      /** <code>optional uint64 default_uint64 = 4;</code> */
       public long getDefaultUint64() {
         return defaultUint64_;
       }
-      /**
-       * <code>optional uint64 default_uint64 = 4;</code>
-       */
+      /** <code>optional uint64 default_uint64 = 4;</code> */
       public Builder setDefaultUint64(long value) {
 
         defaultUint64_ = value;
         onChanged();
         return this;
       }
-      /**
-       * <code>optional uint64 default_uint64 = 4;</code>
-       */
+      /** <code>optional uint64 default_uint64 = 4;</code> */
       public Builder clearDefaultUint64() {
 
         defaultUint64_ = 0L;
@@ -526,14 +474,11 @@
       }
 
       private java.lang.Object defaultString_ = "";
-      /**
-       * <code>optional string default_string = 5;</code>
-       */
+      /** <code>optional string default_string = 5;</code> */
       public java.lang.String getDefaultString() {
         java.lang.Object ref = defaultString_;
         if (!(ref instanceof java.lang.String)) {
-          com.google.protobuf.ByteString bs =
-              (com.google.protobuf.ByteString) ref;
+          com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
           java.lang.String s = bs.toStringUtf8();
           if (bs.isValidUtf8()) {
             defaultString_ = s;
@@ -543,77 +488,59 @@
           return (java.lang.String) ref;
         }
       }
-      /**
-       * <code>optional string default_string = 5;</code>
-       */
-      public com.google.protobuf.ByteString
-          getDefaultStringBytes() {
+      /** <code>optional string default_string = 5;</code> */
+      public com.google.protobuf.ByteString getDefaultStringBytes() {
         java.lang.Object ref = defaultString_;
         if (ref instanceof String) {
           com.google.protobuf.ByteString b =
-              com.google.protobuf.ByteString.copyFromUtf8(
-                  (java.lang.String) ref);
+              com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
           defaultString_ = b;
           return b;
         } else {
           return (com.google.protobuf.ByteString) ref;
         }
       }
-      /**
-       * <code>optional string default_string = 5;</code>
-       */
-      public Builder setDefaultString(
-          java.lang.String value) {
+      /** <code>optional string default_string = 5;</code> */
+      public Builder setDefaultString(java.lang.String value) {
         if (value == null) {
-    throw new NullPointerException();
-  }
+          throw new NullPointerException();
+        }
 
         defaultString_ = value;
         onChanged();
         return this;
       }
-      /**
-       * <code>optional string default_string = 5;</code>
-       */
+      /** <code>optional string default_string = 5;</code> */
       public Builder clearDefaultString() {
 
         defaultString_ = getDefaultInstance().getDefaultString();
         onChanged();
         return this;
       }
-      /**
-       * <code>optional string default_string = 5;</code>
-       */
-      public Builder setDefaultStringBytes(
-          com.google.protobuf.ByteString value) {
+      /** <code>optional string default_string = 5;</code> */
+      public Builder setDefaultStringBytes(com.google.protobuf.ByteString value) {
         if (value == null) {
-    throw new NullPointerException();
-  }
+          throw new NullPointerException();
+        }
 
         defaultString_ = value;
         onChanged();
         return this;
       }
 
-      private boolean defaultBool_ ;
-      /**
-       * <code>optional bool default_bool = 6;</code>
-       */
+      private boolean defaultBool_;
+      /** <code>optional bool default_bool = 6;</code> */
       public boolean getDefaultBool() {
         return defaultBool_;
       }
-      /**
-       * <code>optional bool default_bool = 6;</code>
-       */
+      /** <code>optional bool default_bool = 6;</code> */
       public Builder setDefaultBool(boolean value) {
 
         defaultBool_ = value;
         onChanged();
         return this;
       }
-      /**
-       * <code>optional bool default_bool = 6;</code>
-       */
+      /** <code>optional bool default_bool = 6;</code> */
       public Builder clearDefaultBool() {
 
         defaultBool_ = false;
@@ -621,25 +548,19 @@
         return this;
       }
 
-      private float defaultFloat_ ;
-      /**
-       * <code>optional float default_float = 7;</code>
-       */
+      private float defaultFloat_;
+      /** <code>optional float default_float = 7;</code> */
       public float getDefaultFloat() {
         return defaultFloat_;
       }
-      /**
-       * <code>optional float default_float = 7;</code>
-       */
+      /** <code>optional float default_float = 7;</code> */
       public Builder setDefaultFloat(float value) {
 
         defaultFloat_ = value;
         onChanged();
         return this;
       }
-      /**
-       * <code>optional float default_float = 7;</code>
-       */
+      /** <code>optional float default_float = 7;</code> */
       public Builder clearDefaultFloat() {
 
         defaultFloat_ = 0F;
@@ -647,25 +568,19 @@
         return this;
       }
 
-      private double defaultDouble_ ;
-      /**
-       * <code>optional double default_double = 8;</code>
-       */
+      private double defaultDouble_;
+      /** <code>optional double default_double = 8;</code> */
       public double getDefaultDouble() {
         return defaultDouble_;
       }
-      /**
-       * <code>optional double default_double = 8;</code>
-       */
+      /** <code>optional double default_double = 8;</code> */
       public Builder setDefaultDouble(double value) {
 
         defaultDouble_ = value;
         onChanged();
         return this;
       }
-      /**
-       * <code>optional double default_double = 8;</code>
-       */
+      /** <code>optional double default_double = 8;</code> */
       public Builder clearDefaultDouble() {
 
         defaultDouble_ = 0D;
@@ -674,33 +589,28 @@
       }
 
       private com.google.protobuf.ByteString defaultBytes_ = com.google.protobuf.ByteString.EMPTY;
-      /**
-       * <code>optional bytes default_bytes = 9;</code>
-       */
+      /** <code>optional bytes default_bytes = 9;</code> */
       public com.google.protobuf.ByteString getDefaultBytes() {
         return defaultBytes_;
       }
-      /**
-       * <code>optional bytes default_bytes = 9;</code>
-       */
+      /** <code>optional bytes default_bytes = 9;</code> */
       public Builder setDefaultBytes(com.google.protobuf.ByteString value) {
         if (value == null) {
-    throw new NullPointerException();
-  }
+          throw new NullPointerException();
+        }
 
         defaultBytes_ = value;
         onChanged();
         return this;
       }
-      /**
-       * <code>optional bytes default_bytes = 9;</code>
-       */
+      /** <code>optional bytes default_bytes = 9;</code> */
       public Builder clearDefaultBytes() {
 
         defaultBytes_ = getDefaultInstance().getDefaultBytes();
         onChanged();
         return this;
       }
+
       public final Builder setUnknownFields(
           final com.google.protobuf.UnknownFieldSet unknownFields) {
         return this;
@@ -711,12 +621,13 @@
         return this;
       }
 
-
       // @@protoc_insertion_point(builder_scope:com.google.protobuf.jruby.Sentinel)
     }
 
     // @@protoc_insertion_point(class_scope:com.google.protobuf.jruby.Sentinel)
-    private static final com.google.protobuf.jruby.SentinelOuterClass.Sentinel defaultInstance;static {
+    private static final com.google.protobuf.jruby.SentinelOuterClass.Sentinel defaultInstance;
+
+    static {
       defaultInstance = new com.google.protobuf.jruby.SentinelOuterClass.Sentinel();
     }
 
@@ -727,49 +638,55 @@
     public com.google.protobuf.jruby.SentinelOuterClass.Sentinel getDefaultInstanceForType() {
       return defaultInstance;
     }
-
   }
 
   private static final com.google.protobuf.Descriptors.Descriptor
-    internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
-  private static
-    com.google.protobuf.GeneratedMessage.FieldAccessorTable
+      internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
+  private static com.google.protobuf.GeneratedMessage.FieldAccessorTable
       internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable;
 
-  public static com.google.protobuf.Descriptors.FileDescriptor
-      getDescriptor() {
+  public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
     return descriptor;
   }
-  private static com.google.protobuf.Descriptors.FileDescriptor
-      descriptor;
+
+  private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
+
   static {
     java.lang.String[] descriptorData = {
-      "\n\016sentinel.proto\022\031com.google.protobuf.jr" +
-      "uby\"\334\001\n\010Sentinel\022\025\n\rdefault_int32\030\001 \001(\005\022" +
-      "\025\n\rdefault_int64\030\002 \001(\003\022\026\n\016default_unit32" +
-      "\030\003 \001(\r\022\026\n\016default_uint64\030\004 \001(\004\022\026\n\016defaul" +
-      "t_string\030\005 \001(\t\022\024\n\014default_bool\030\006 \001(\010\022\025\n\r" +
-      "default_float\030\007 \001(\002\022\026\n\016default_double\030\010 " +
-      "\001(\001\022\025\n\rdefault_bytes\030\t \001(\014B\002H\002b\006proto3"
+      "\n\016sentinel.proto\022\031com.google.protobuf.jr"
+          + "uby\"\334\001\n\010Sentinel\022\025\n\rdefault_int32\030\001 \001(\005\022"
+          + "\025\n\rdefault_int64\030\002 \001(\003\022\026\n\016default_unit32"
+          + "\030\003 \001(\r\022\026\n\016default_uint64\030\004 \001(\004\022\026\n\016defaul"
+          + "t_string\030\005 \001(\t\022\024\n\014default_bool\030\006 \001(\010\022\025\n\r"
+          + "default_float\030\007 \001(\002\022\026\n\016default_double\030\010 "
+          + "\001(\001\022\025\n\rdefault_bytes\030\t \001(\014B\002H\002b\006proto3"
     };
     com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
-        new com.google.protobuf.Descriptors.FileDescriptor.    InternalDescriptorAssigner() {
+        new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
           public com.google.protobuf.ExtensionRegistry assignDescriptors(
               com.google.protobuf.Descriptors.FileDescriptor root) {
             descriptor = root;
             return null;
           }
         };
-    com.google.protobuf.Descriptors.FileDescriptor
-      .internalBuildGeneratedFileFrom(descriptorData,
-        new com.google.protobuf.Descriptors.FileDescriptor[] {
-        }, assigner);
+    com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(
+        descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {}, assigner);
     internal_static_com_google_protobuf_jruby_Sentinel_descriptor =
-      getDescriptor().getMessageTypes().get(0);
-    internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable = new
-      com.google.protobuf.GeneratedMessage.FieldAccessorTable(
-        internal_static_com_google_protobuf_jruby_Sentinel_descriptor,
-        new java.lang.String[] { "DefaultInt32", "DefaultInt64", "DefaultUnit32", "DefaultUint64", "DefaultString", "DefaultBool", "DefaultFloat", "DefaultDouble", "DefaultBytes", });
+        getDescriptor().getMessageTypes().get(0);
+    internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable =
+        new com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+            internal_static_com_google_protobuf_jruby_Sentinel_descriptor,
+            new java.lang.String[] {
+              "DefaultInt32",
+              "DefaultInt64",
+              "DefaultUnit32",
+              "DefaultUint64",
+              "DefaultString",
+              "DefaultBool",
+              "DefaultFloat",
+              "DefaultDouble",
+              "DefaultBytes",
+            });
   }
 
   // @@protoc_insertion_point(outer_class_scope)
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/Utils.java b/ruby/src/main/java/com/google/protobuf/jruby/Utils.java
index cd27589..65de683 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/Utils.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/Utils.java
@@ -35,6 +35,7 @@
 import com.google.protobuf.ByteString;
 import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
 import com.google.protobuf.Descriptors.FieldDescriptor;
+import java.math.BigInteger;
 import org.jcodings.specific.ASCIIEncoding;
 import org.jruby.*;
 import org.jruby.exceptions.RaiseException;
@@ -44,302 +45,363 @@
 import org.jruby.runtime.ThreadContext;
 import org.jruby.runtime.builtin.IRubyObject;
 
-import java.math.BigInteger;
-
 public class Utils {
-    public static FieldDescriptor.Type rubyToFieldType(IRubyObject typeClass) {
-        return FieldDescriptor.Type.valueOf(typeClass.asJavaString().toUpperCase());
-    }
+  public static FieldDescriptor.Type rubyToFieldType(IRubyObject typeClass) {
+    return FieldDescriptor.Type.valueOf(typeClass.asJavaString().toUpperCase());
+  }
 
-    public static IRubyObject fieldTypeToRuby(ThreadContext context, FieldDescriptor.Type type) {
-        return fieldTypeToRuby(context, type.name());
-    }
+  public static IRubyObject fieldTypeToRuby(ThreadContext context, FieldDescriptor.Type type) {
+    return fieldTypeToRuby(context, type.name());
+  }
 
-    public static IRubyObject fieldTypeToRuby(ThreadContext context, FieldDescriptorProto.Type type) {
-        return fieldTypeToRuby(context, type.name());
-    }
+  public static IRubyObject fieldTypeToRuby(ThreadContext context, FieldDescriptorProto.Type type) {
+    return fieldTypeToRuby(context, type.name());
+  }
 
-    private static IRubyObject fieldTypeToRuby(ThreadContext context, String typeName) {
+  private static IRubyObject fieldTypeToRuby(ThreadContext context, String typeName) {
 
-        return context.runtime.newSymbol(typeName.replace("TYPE_", "").toLowerCase());
-    }
+    return context.runtime.newSymbol(typeName.replace("TYPE_", "").toLowerCase());
+  }
 
-    public static IRubyObject checkType(ThreadContext context, FieldDescriptor.Type fieldType,
-                                        String fieldName, IRubyObject value, RubyModule typeClass) {
-        Ruby runtime = context.runtime;
+  public static IRubyObject checkType(
+      ThreadContext context,
+      FieldDescriptor.Type fieldType,
+      String fieldName,
+      IRubyObject value,
+      RubyModule typeClass) {
+    Ruby runtime = context.runtime;
 
-        switch(fieldType) {
-            case SFIXED32:
-            case SFIXED64:
-            case FIXED64:
-            case SINT64:
-            case SINT32:
-            case FIXED32:
-            case INT32:
-            case INT64:
-            case UINT32:
-            case UINT64:
-                if (!isRubyNum(value))
-                    throw createExpectedTypeError(context, "number", "integral", fieldName, value);
+    switch (fieldType) {
+      case SFIXED32:
+      case SFIXED64:
+      case FIXED64:
+      case SINT64:
+      case SINT32:
+      case FIXED32:
+      case INT32:
+      case INT64:
+      case UINT32:
+      case UINT64:
+        if (!isRubyNum(value))
+          throw createExpectedTypeError(context, "number", "integral", fieldName, value);
 
-                if (value instanceof RubyFloat) {
-                    double doubleVal = RubyNumeric.num2dbl(value);
-                    if (Math.floor(doubleVal) != doubleVal) {
-                        throw runtime.newRangeError("Non-integral floating point value assigned to integer field '" + fieldName + "' (given " + value.getMetaClass() + ").");
-                    }
-                }
-                if (fieldType == FieldDescriptor.Type.UINT32 || fieldType == FieldDescriptor.Type.UINT64 ||
-                    fieldType == FieldDescriptor.Type.FIXED32  || fieldType == FieldDescriptor.Type.FIXED64) {
-                    if (((RubyNumeric) value).isNegative()) {
-                        throw runtime.newRangeError("Assigning negative value to unsigned integer field '" + fieldName + "' (given " + value.getMetaClass() + ").");
-                    }
-                }
-
-                switch(fieldType) {
-                    case INT32:
-                        RubyNumeric.num2int(value);
-                        break;
-                    case UINT32:
-                    case FIXED32:
-                        num2uint(value);
-                        break;
-                    case UINT64:
-                    case FIXED64:
-                        num2ulong(context.runtime, value);
-                        break;
-                    default:
-                        RubyNumeric.num2long(value);
-                        break;
-                }
-                break;
-            case FLOAT:
-                if (!isRubyNum(value))
-                    throw createExpectedTypeError(context, "number", "float", fieldName, value);
-                break;
-            case DOUBLE:
-                if (!isRubyNum(value))
-                    throw createExpectedTypeError(context, "number", "double", fieldName, value);
-                break;
-            case BOOL:
-                if (!(value instanceof RubyBoolean))
-                    throw createInvalidTypeError(context, "boolean", fieldName, value);
-                break;
-            case BYTES:
-                value = validateAndEncodeString(context, "bytes", fieldName, value, "Encoding::ASCII_8BIT");
-                break;
-            case STRING:
-                value = validateAndEncodeString(context, "string", fieldName, symToString(value), "Encoding::UTF_8");
-                break;
-            case MESSAGE:
-                if (value.getMetaClass() != typeClass) {
-                    // See if we can convert the value before flagging it as invalid
-                    String className = typeClass.getName();
-
-                    if (className.equals("Google::Protobuf::Timestamp") && value instanceof RubyTime) {
-                        RubyTime rt = (RubyTime) value;
-                        RubyHash timestampArgs =
-                            Helpers.constructHash(runtime,
-                                runtime.newString("nanos"), rt.nsec(), false,
-                                runtime.newString("seconds"), rt.to_i(), false);
-                        return ((RubyClass) typeClass).newInstance(context, timestampArgs, Block.NULL_BLOCK);
-
-                    } else if (className.equals("Google::Protobuf::Duration") && value instanceof RubyNumeric) {
-                        IRubyObject seconds;
-                        if (value instanceof RubyFloat) {
-                            seconds = ((RubyFloat) value).truncate(context);
-                        } else if (value instanceof RubyRational) {
-                            seconds = ((RubyRational) value).to_i(context);
-                        } else if (value instanceof RubyBigDecimal) {
-                            seconds = ((RubyBigDecimal) value).to_int(context);
-                        } else {
-                            seconds = ((RubyInteger) value).to_i();
-                        }
-
-                        IRubyObject nanos = ((RubyNumeric) value).remainder(context, RubyFixnum.one(runtime));
-                        if (nanos instanceof RubyFloat) {
-                            nanos = ((RubyFloat) nanos).op_mul(context, 1000000000);
-                        } else if (nanos instanceof RubyRational) {
-                            nanos = ((RubyRational) nanos).op_mul(context, runtime.newFixnum(1000000000));
-                        } else if (nanos instanceof RubyBigDecimal) {
-                            nanos = ((RubyBigDecimal) nanos).op_mul(context, runtime.newFixnum(1000000000));
-                        } else {
-                            nanos = ((RubyInteger) nanos).op_mul(context, 1000000000);
-                        }
-
-                        RubyHash durationArgs =
-                            Helpers.constructHash(runtime,
-                                runtime.newString("nanos"), ((RubyNumeric) nanos).round(context), false,
-                                runtime.newString("seconds"), seconds, false);
-                        return ((RubyClass) typeClass).newInstance(context, durationArgs, Block.NULL_BLOCK);
-                    }
-
-                    // Not able to convert so flag as invalid
-                    throw createTypeError(context, "Invalid type " + value.getMetaClass() + " to assign to submessage field '" + fieldName + "'.");
-                }
-
-                break;
-            case ENUM:
-                boolean isValid = ((RubyEnumDescriptor) typeClass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR)).isValidValue(context, value);
-                if (!isValid) {
-                    throw runtime.newRangeError("Unknown symbol value for enum field '" + fieldName + "'.");
-                }
-                break;
-            default:
-                break;
-        }
-        return value;
-    }
-
-    public static IRubyObject wrapPrimaryValue(ThreadContext context, FieldDescriptor.Type fieldType, Object value) {
-        return wrapPrimaryValue(context, fieldType, value, false);
-    }
-
-    public static IRubyObject wrapPrimaryValue(ThreadContext context, FieldDescriptor.Type fieldType, Object value, boolean encodeBytes) {
-        Ruby runtime = context.runtime;
-        switch (fieldType) {
-            case INT32:
-            case SFIXED32:
-            case SINT32:
-                return runtime.newFixnum((Integer) value);
-            case SFIXED64:
-            case SINT64:
-            case INT64:
-                return runtime.newFixnum((Long) value);
-            case FIXED32:
-            case UINT32:
-                return runtime.newFixnum(((Integer) value) & (-1l >>> 32));
-            case FIXED64:
-            case UINT64:
-                long ret = (Long) value;
-                return ret >= 0 ? runtime.newFixnum(ret) :
-                        RubyBignum.newBignum(runtime, UINT64_COMPLEMENTARY.add(new BigInteger(ret + "")));
-            case FLOAT:
-                return runtime.newFloat((Float) value);
-            case DOUBLE:
-                return runtime.newFloat((Double) value);
-            case BOOL:
-                return (Boolean) value ? runtime.getTrue() : runtime.getFalse();
-            case BYTES: {
-                IRubyObject wrapped = encodeBytes ?
-                    RubyString.newString(runtime, ((ByteString) value).toStringUtf8(), ASCIIEncoding.INSTANCE) :
-                    RubyString.newString(runtime, ((ByteString) value).toByteArray());
-                wrapped.setFrozen(true);
-                return wrapped;
-            }
-            case STRING: {
-                IRubyObject wrapped = runtime.newString(value.toString());
-                wrapped.setFrozen(true);
-                return wrapped;
-            }
-            default:
-                return runtime.getNil();
-        }
-    }
-
-    public static int num2uint(IRubyObject value) {
-        long longVal = RubyNumeric.num2long(value);
-        if (longVal > UINT_MAX)
-            throw value.getRuntime().newRangeError("Integer " + longVal + " too big to convert to 'unsigned int'");
-        long num = longVal;
-        if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE)
-            // encode to UINT32
-            num = (-longVal ^ (-1l >>> 32) ) + 1;
-        RubyNumeric.checkInt(value, num);
-        return (int) num;
-    }
-
-    public static long num2ulong(Ruby runtime, IRubyObject value) {
         if (value instanceof RubyFloat) {
-            RubyBignum bignum = RubyBignum.newBignum(runtime, ((RubyFloat) value).getDoubleValue());
-            return RubyBignum.big2ulong(bignum);
-        } else if (value instanceof RubyBignum) {
-            return RubyBignum.big2ulong((RubyBignum) value);
-        } else {
-            return RubyNumeric.num2long(value);
+          double doubleVal = RubyNumeric.num2dbl(value);
+          if (Math.floor(doubleVal) != doubleVal) {
+            throw runtime.newRangeError(
+                "Non-integral floating point value assigned to integer field '"
+                    + fieldName
+                    + "' (given "
+                    + value.getMetaClass()
+                    + ").");
+          }
         }
-    }
-
-    /*
-     * Helper to make it easier to support symbols being passed instead of strings
-     */
-    public static IRubyObject symToString(IRubyObject sym) {
-        if (sym instanceof RubySymbol) {
-            return ((RubySymbol) sym).id2name();
+        if (fieldType == FieldDescriptor.Type.UINT32
+            || fieldType == FieldDescriptor.Type.UINT64
+            || fieldType == FieldDescriptor.Type.FIXED32
+            || fieldType == FieldDescriptor.Type.FIXED64) {
+          if (((RubyNumeric) value).isNegative()) {
+            throw runtime.newRangeError(
+                "Assigning negative value to unsigned integer field '"
+                    + fieldName
+                    + "' (given "
+                    + value.getMetaClass()
+                    + ").");
+          }
         }
-        return sym;
-    }
 
-    public static void checkNameAvailability(ThreadContext context, String name) {
-        if (context.runtime.getObject().getConstantAt(name) != null)
-            throw context.runtime.newNameError(name + " is already defined", name);
-    }
-
-    public static boolean isMapEntry(FieldDescriptor fieldDescriptor) {
-        return fieldDescriptor.getType() == FieldDescriptor.Type.MESSAGE &&
-                fieldDescriptor.isRepeated() &&
-                fieldDescriptor.getMessageType().getOptions().getMapEntry();
-    }
-
-    public static RaiseException createTypeError(ThreadContext context, String message) {
-        if (cTypeError == null) {
-            cTypeError = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::TypeError");
+        switch (fieldType) {
+          case INT32:
+            RubyNumeric.num2int(value);
+            break;
+          case UINT32:
+          case FIXED32:
+            num2uint(value);
+            break;
+          case UINT64:
+          case FIXED64:
+            num2ulong(context.runtime, value);
+            break;
+          default:
+            RubyNumeric.num2long(value);
+            break;
         }
-        return RaiseException.from(context.runtime, cTypeError, message);
-    }
+        break;
+      case FLOAT:
+        if (!isRubyNum(value))
+          throw createExpectedTypeError(context, "number", "float", fieldName, value);
+        break;
+      case DOUBLE:
+        if (!isRubyNum(value))
+          throw createExpectedTypeError(context, "number", "double", fieldName, value);
+        break;
+      case BOOL:
+        if (!(value instanceof RubyBoolean))
+          throw createInvalidTypeError(context, "boolean", fieldName, value);
+        break;
+      case BYTES:
+        value = validateAndEncodeString(context, "bytes", fieldName, value, "Encoding::ASCII_8BIT");
+        break;
+      case STRING:
+        value =
+            validateAndEncodeString(
+                context, "string", fieldName, symToString(value), "Encoding::UTF_8");
+        break;
+      case MESSAGE:
+        if (value.getMetaClass() != typeClass) {
+          // See if we can convert the value before flagging it as invalid
+          String className = typeClass.getName();
 
-    public static RaiseException createExpectedTypeError(ThreadContext context, String type, String fieldType, String fieldName, IRubyObject value) {
-        return createTypeError(context, String.format(EXPECTED_TYPE_ERROR_FORMAT, type, fieldType, fieldName, value.getMetaClass()));
-    }
+          if (className.equals("Google::Protobuf::Timestamp") && value instanceof RubyTime) {
+            RubyTime rt = (RubyTime) value;
+            RubyHash timestampArgs =
+                Helpers.constructHash(
+                    runtime,
+                    runtime.newString("nanos"),
+                    rt.nsec(),
+                    false,
+                    runtime.newString("seconds"),
+                    rt.to_i(),
+                    false);
+            return ((RubyClass) typeClass).newInstance(context, timestampArgs, Block.NULL_BLOCK);
 
-    public static RaiseException createInvalidTypeError(ThreadContext context, String fieldType, String fieldName, IRubyObject value) {
-        return createTypeError(context, String.format(INVALID_TYPE_ERROR_FORMAT, fieldType, fieldName, value.getMetaClass()));
-    }
-
-    protected static boolean isRubyNum(Object value) {
-        return value instanceof RubyFixnum || value instanceof RubyFloat || value instanceof RubyBignum;
-    }
-
-    protected static void validateTypeClass(ThreadContext context, FieldDescriptor.Type type, IRubyObject value) {
-        Ruby runtime = context.runtime;
-        if (!(value instanceof RubyModule)) {
-            throw runtime.newArgumentError("TypeClass has incorrect type");
-        }
-        RubyModule klass = (RubyModule) value;
-        IRubyObject descriptor = klass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR);
-        if (descriptor.isNil()) {
-            throw runtime.newArgumentError("Type class has no descriptor. Please pass a " +
-                    "class or enum as returned by the DescriptorPool.");
-        }
-        if (type == FieldDescriptor.Type.MESSAGE) {
-            if (! (descriptor instanceof RubyDescriptor)) {
-                throw runtime.newArgumentError("Descriptor has an incorrect type");
+          } else if (className.equals("Google::Protobuf::Duration")
+              && value instanceof RubyNumeric) {
+            IRubyObject seconds;
+            if (value instanceof RubyFloat) {
+              seconds = ((RubyFloat) value).truncate(context);
+            } else if (value instanceof RubyRational) {
+              seconds = ((RubyRational) value).to_i(context);
+            } else if (value instanceof RubyBigDecimal) {
+              seconds = ((RubyBigDecimal) value).to_int(context);
+            } else {
+              seconds = ((RubyInteger) value).to_i();
             }
-        } else if (type == FieldDescriptor.Type.ENUM) {
-            if (! (descriptor instanceof RubyEnumDescriptor)) {
-                throw runtime.newArgumentError("Descriptor has an incorrect type");
+
+            IRubyObject nanos = ((RubyNumeric) value).remainder(context, RubyFixnum.one(runtime));
+            if (nanos instanceof RubyFloat) {
+              nanos = ((RubyFloat) nanos).op_mul(context, 1000000000);
+            } else if (nanos instanceof RubyRational) {
+              nanos = ((RubyRational) nanos).op_mul(context, runtime.newFixnum(1000000000));
+            } else if (nanos instanceof RubyBigDecimal) {
+              nanos = ((RubyBigDecimal) nanos).op_mul(context, runtime.newFixnum(1000000000));
+            } else {
+              nanos = ((RubyInteger) nanos).op_mul(context, 1000000000);
             }
+
+            RubyHash durationArgs =
+                Helpers.constructHash(
+                    runtime,
+                    runtime.newString("nanos"),
+                    ((RubyNumeric) nanos).round(context),
+                    false,
+                    runtime.newString("seconds"),
+                    seconds,
+                    false);
+            return ((RubyClass) typeClass).newInstance(context, durationArgs, Block.NULL_BLOCK);
+          }
+
+          // Not able to convert so flag as invalid
+          throw createTypeError(
+              context,
+              "Invalid type "
+                  + value.getMetaClass()
+                  + " to assign to submessage field '"
+                  + fieldName
+                  + "'.");
         }
+
+        break;
+      case ENUM:
+        boolean isValid =
+            ((RubyEnumDescriptor) typeClass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR))
+                .isValidValue(context, value);
+        if (!isValid) {
+          throw runtime.newRangeError("Unknown symbol value for enum field '" + fieldName + "'.");
+        }
+        break;
+      default:
+        break;
     }
+    return value;
+  }
 
-    private static IRubyObject validateAndEncodeString(ThreadContext context, String fieldType, String fieldName, IRubyObject value, String encoding) {
-        if (!(value instanceof RubyString))
-            throw createInvalidTypeError(context, fieldType, fieldName, value);
+  public static IRubyObject wrapPrimaryValue(
+      ThreadContext context, FieldDescriptor.Type fieldType, Object value) {
+    return wrapPrimaryValue(context, fieldType, value, false);
+  }
 
-        value = ((RubyString) value).encode(context, context.runtime.evalScriptlet(encoding));
-        value.setFrozen(true);
-        return value;
+  public static IRubyObject wrapPrimaryValue(
+      ThreadContext context, FieldDescriptor.Type fieldType, Object value, boolean encodeBytes) {
+    Ruby runtime = context.runtime;
+    switch (fieldType) {
+      case INT32:
+      case SFIXED32:
+      case SINT32:
+        return runtime.newFixnum((Integer) value);
+      case SFIXED64:
+      case SINT64:
+      case INT64:
+        return runtime.newFixnum((Long) value);
+      case FIXED32:
+      case UINT32:
+        return runtime.newFixnum(((Integer) value) & (-1l >>> 32));
+      case FIXED64:
+      case UINT64:
+        long ret = (Long) value;
+        return ret >= 0
+            ? runtime.newFixnum(ret)
+            : RubyBignum.newBignum(runtime, UINT64_COMPLEMENTARY.add(new BigInteger(ret + "")));
+      case FLOAT:
+        return runtime.newFloat((Float) value);
+      case DOUBLE:
+        return runtime.newFloat((Double) value);
+      case BOOL:
+        return (Boolean) value ? runtime.getTrue() : runtime.getFalse();
+      case BYTES:
+        {
+          IRubyObject wrapped =
+              encodeBytes
+                  ? RubyString.newString(
+                      runtime, ((ByteString) value).toStringUtf8(), ASCIIEncoding.INSTANCE)
+                  : RubyString.newString(runtime, ((ByteString) value).toByteArray());
+          wrapped.setFrozen(true);
+          return wrapped;
+        }
+      case STRING:
+        {
+          IRubyObject wrapped = runtime.newString(value.toString());
+          wrapped.setFrozen(true);
+          return wrapped;
+        }
+      default:
+        return runtime.getNil();
     }
+  }
 
-    public static final String DESCRIPTOR_INSTANCE_VAR = "@descriptor";
+  public static int num2uint(IRubyObject value) {
+    long longVal = RubyNumeric.num2long(value);
+    if (longVal > UINT_MAX)
+      throw value
+          .getRuntime()
+          .newRangeError("Integer " + longVal + " too big to convert to 'unsigned int'");
+    long num = longVal;
+    if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE)
+      // encode to UINT32
+      num = (-longVal ^ (-1l >>> 32)) + 1;
+    RubyNumeric.checkInt(value, num);
+    return (int) num;
+  }
 
-    public static final String EQUAL_SIGN = "=";
+  public static long num2ulong(Ruby runtime, IRubyObject value) {
+    if (value instanceof RubyFloat) {
+      RubyBignum bignum = RubyBignum.newBignum(runtime, ((RubyFloat) value).getDoubleValue());
+      return RubyBignum.big2ulong(bignum);
+    } else if (value instanceof RubyBignum) {
+      return RubyBignum.big2ulong((RubyBignum) value);
+    } else {
+      return RubyNumeric.num2long(value);
+    }
+  }
 
-    private static final BigInteger UINT64_COMPLEMENTARY = new BigInteger("18446744073709551616"); //Math.pow(2, 64)
+  /*
+   * Helper to make it easier to support symbols being passed instead of strings
+   */
+  public static IRubyObject symToString(IRubyObject sym) {
+    if (sym instanceof RubySymbol) {
+      return ((RubySymbol) sym).id2name();
+    }
+    return sym;
+  }
 
-    private static final String EXPECTED_TYPE_ERROR_FORMAT = "Expected %s type for %s field '%s' (given %s).";
-    private static final String INVALID_TYPE_ERROR_FORMAT = "Invalid argument for %s field '%s' (given %s).";
+  public static void checkNameAvailability(ThreadContext context, String name) {
+    if (context.runtime.getObject().getConstantAt(name) != null)
+      throw context.runtime.newNameError(name + " is already defined", name);
+  }
 
-    private static final long UINT_MAX = 0xffffffffl;
+  public static boolean isMapEntry(FieldDescriptor fieldDescriptor) {
+    return fieldDescriptor.getType() == FieldDescriptor.Type.MESSAGE
+        && fieldDescriptor.isRepeated()
+        && fieldDescriptor.getMessageType().getOptions().getMapEntry();
+  }
 
-    private static RubyClass cTypeError;
+  public static RaiseException createTypeError(ThreadContext context, String message) {
+    if (cTypeError == null) {
+      cTypeError = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::TypeError");
+    }
+    return RaiseException.from(context.runtime, cTypeError, message);
+  }
+
+  public static RaiseException createExpectedTypeError(
+      ThreadContext context, String type, String fieldType, String fieldName, IRubyObject value) {
+    return createTypeError(
+        context,
+        String.format(
+            EXPECTED_TYPE_ERROR_FORMAT, type, fieldType, fieldName, value.getMetaClass()));
+  }
+
+  public static RaiseException createInvalidTypeError(
+      ThreadContext context, String fieldType, String fieldName, IRubyObject value) {
+    return createTypeError(
+        context,
+        String.format(INVALID_TYPE_ERROR_FORMAT, fieldType, fieldName, value.getMetaClass()));
+  }
+
+  protected static boolean isRubyNum(Object value) {
+    return value instanceof RubyFixnum || value instanceof RubyFloat || value instanceof RubyBignum;
+  }
+
+  protected static void validateTypeClass(
+      ThreadContext context, FieldDescriptor.Type type, IRubyObject value) {
+    Ruby runtime = context.runtime;
+    if (!(value instanceof RubyModule)) {
+      throw runtime.newArgumentError("TypeClass has incorrect type");
+    }
+    RubyModule klass = (RubyModule) value;
+    IRubyObject descriptor = klass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR);
+    if (descriptor.isNil()) {
+      throw runtime.newArgumentError(
+          "Type class has no descriptor. Please pass a "
+              + "class or enum as returned by the DescriptorPool.");
+    }
+    if (type == FieldDescriptor.Type.MESSAGE) {
+      if (!(descriptor instanceof RubyDescriptor)) {
+        throw runtime.newArgumentError("Descriptor has an incorrect type");
+      }
+    } else if (type == FieldDescriptor.Type.ENUM) {
+      if (!(descriptor instanceof RubyEnumDescriptor)) {
+        throw runtime.newArgumentError("Descriptor has an incorrect type");
+      }
+    }
+  }
+
+  private static IRubyObject validateAndEncodeString(
+      ThreadContext context,
+      String fieldType,
+      String fieldName,
+      IRubyObject value,
+      String encoding) {
+    if (!(value instanceof RubyString))
+      throw createInvalidTypeError(context, fieldType, fieldName, value);
+
+    value = ((RubyString) value).encode(context, context.runtime.evalScriptlet(encoding));
+    value.setFrozen(true);
+    return value;
+  }
+
+  public static final String DESCRIPTOR_INSTANCE_VAR = "@descriptor";
+
+  public static final String EQUAL_SIGN = "=";
+
+  private static final BigInteger UINT64_COMPLEMENTARY =
+      new BigInteger("18446744073709551616"); // Math.pow(2, 64)
+
+  private static final String EXPECTED_TYPE_ERROR_FORMAT =
+      "Expected %s type for %s field '%s' (given %s).";
+  private static final String INVALID_TYPE_ERROR_FORMAT =
+      "Invalid argument for %s field '%s' (given %s).";
+
+  private static final long UINT_MAX = 0xffffffffl;
+
+  private static RubyClass cTypeError;
 }
diff --git a/ruby/src/main/java/google/ProtobufJavaService.java b/ruby/src/main/java/google/ProtobufJavaService.java
index 713891e..00d60a1 100644
--- a/ruby/src/main/java/google/ProtobufJavaService.java
+++ b/ruby/src/main/java/google/ProtobufJavaService.java
@@ -33,30 +33,29 @@
 package google;
 
 import com.google.protobuf.jruby.*;
+import java.io.IOException;
 import org.jruby.Ruby;
 import org.jruby.runtime.load.BasicLibraryService;
 
-import java.io.IOException;
-
 public class ProtobufJavaService implements BasicLibraryService {
-    @Override
-    public boolean basicLoad(Ruby ruby) throws IOException {
-        ruby.defineModule("Google");
+  @Override
+  public boolean basicLoad(Ruby ruby) throws IOException {
+    ruby.defineModule("Google");
 
-        /*
-         * The order these happen in is important because we
-         * save a static reference to some classes and they
-         * need to exist before we try to save a reference to them
-         */
-        RubyProtobuf.createProtobuf(ruby);
-        RubyFileDescriptor.createRubyFileDescriptor(ruby);
-        RubyEnumDescriptor.createRubyEnumDescriptor(ruby);
-        RubyRepeatedField.createRubyRepeatedField(ruby);
-        RubyFieldDescriptor.createRubyFieldDescriptor(ruby);
-        RubyMap.createRubyMap(ruby);
-        RubyOneofDescriptor.createRubyOneofDescriptor(ruby);
-        RubyDescriptor.createRubyDescriptor(ruby);
-        RubyDescriptorPool.createRubyDescriptorPool(ruby);
-        return true;
-    }
+    /*
+     * The order these happen in is important because we
+     * save a static reference to some classes and they
+     * need to exist before we try to save a reference to them
+     */
+    RubyProtobuf.createProtobuf(ruby);
+    RubyFileDescriptor.createRubyFileDescriptor(ruby);
+    RubyEnumDescriptor.createRubyEnumDescriptor(ruby);
+    RubyRepeatedField.createRubyRepeatedField(ruby);
+    RubyFieldDescriptor.createRubyFieldDescriptor(ruby);
+    RubyMap.createRubyMap(ruby);
+    RubyOneofDescriptor.createRubyOneofDescriptor(ruby);
+    RubyDescriptor.createRubyDescriptor(ruby);
+    RubyDescriptorPool.createRubyDescriptorPool(ruby);
+    return true;
+  }
 }
diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb
index 4ff1c15..5b781b7 100755
--- a/ruby/tests/basic.rb
+++ b/ruby/tests/basic.rb
@@ -66,8 +66,11 @@
     def test_issue_8559_crash
       msg = TestMessage.new
       msg.repeated_int32 = ::Google::Protobuf::RepeatedField.new(:int32, [1, 2, 3])
-      # TODO: Remove the platform check once https://github.com/jruby/jruby/issues/6818 is released in JRuby 9.3.0.0
-      GC.start(full_mark: true, immediate_sweep: true) unless RUBY_PLATFORM == "java"
+
+      # https://github.com/jruby/jruby/issues/6818 was fixed in JRuby 9.3.0.0
+      if cruby_or_jruby_9_3_or_higher?
+        GC.start(full_mark: true, immediate_sweep: true)
+      end
       TestMessage.encode(msg)
     end
 
@@ -99,7 +102,7 @@
 
       begin
         encoded = msgclass.encode(m)
-      rescue java.lang.NullPointerException => e
+      rescue java.lang.NullPointerException
         flunk "NPE rescued"
       end
       decoded = msgclass.decode(encoded)
@@ -173,7 +176,7 @@
       m = TestSingularFields.new
 
       m.singular_int32 = -42
-      assert_equal -42, m.singular_int32
+      assert_equal( -42, m.singular_int32 )
       m.clear_singular_int32
       assert_equal 0, m.singular_int32
 
@@ -568,8 +571,6 @@
 
 
     def test_json_maps
-      # TODO: Fix JSON in JRuby version.
-      return if RUBY_PLATFORM == "java"
       m = MapMessage.new(:map_string_int32 => {"a" => 1})
       expected = {mapStringInt32: {a: 1}, mapStringMsg: {}, mapStringEnum: {}}
       expected_preserve = {map_string_int32: {a: 1}, map_string_msg: {}, map_string_enum: {}}
@@ -583,8 +584,6 @@
     end
 
     def test_json_maps_emit_defaults_submsg
-      # TODO: Fix JSON in JRuby version.
-      return if RUBY_PLATFORM == "java"
       m = MapMessage.new(:map_string_msg => {"a" => TestMessage2.new(foo: 0)})
       expected = {mapStringInt32: {}, mapStringMsg: {a: {foo: 0}}, mapStringEnum: {}}
 
@@ -594,8 +593,6 @@
     end
 
     def test_json_emit_defaults_submsg
-      # TODO: Fix JSON in JRuby version.
-      return if RUBY_PLATFORM == "java"
       m = TestSingularFields.new(singular_msg: proto_module::TestMessage2.new)
 
       expected = {
@@ -618,8 +615,6 @@
     end
 
     def test_respond_to
-      # This test fails with JRuby 1.7.23, likely because of an old JRuby bug.
-      return if RUBY_PLATFORM == "java"
       msg = MapMessage.new
       assert msg.respond_to?(:map_string_int32)
       assert !msg.respond_to?(:bacon)
@@ -694,5 +689,51 @@
       m2 = proto_module::TestMessage.decode(proto_module::TestMessage.encode(m))
       assert_equal m2, m
     end
+
+    def test_map_fields_respond_to? # regression test for issue 9202
+      msg = proto_module::MapMessage.new
+      assert msg.respond_to?(:map_string_int32=)
+      msg.map_string_int32 = Google::Protobuf::Map.new(:string, :int32)
+      assert msg.respond_to?(:map_string_int32)
+      assert_equal( Google::Protobuf::Map.new(:string, :int32), msg.map_string_int32 )
+      assert msg.respond_to?(:clear_map_string_int32)
+      msg.clear_map_string_int32
+
+      assert !msg.respond_to?(:has_map_string_int32?)
+      assert_raise NoMethodError do
+        msg.has_map_string_int32?
+      end
+      assert !msg.respond_to?(:map_string_int32_as_value)
+      assert_raise NoMethodError do
+        msg.map_string_int32_as_value
+      end
+      assert !msg.respond_to?(:map_string_int32_as_value=)
+      assert_raise NoMethodError do
+        msg.map_string_int32_as_value = :boom
+      end
+    end
+  end
+
+  def test_oneof_fields_respond_to? # regression test for issue 9202
+    msg = proto_module::OneofMessage.new
+    # `has_` prefix + "?" suffix actions should only work for oneofs fields.
+    assert msg.has_my_oneof?
+    assert msg.respond_to? :has_my_oneof?
+    assert !msg.respond_to?( :has_a? )
+    assert_raise NoMethodError do
+      msg.has_a?
+    end
+    assert !msg.respond_to?( :has_b? )
+    assert_raise NoMethodError do
+      msg.has_b?
+    end
+    assert !msg.respond_to?( :has_c? )
+    assert_raise NoMethodError do
+      msg.has_c?
+    end
+    assert !msg.respond_to?( :has_d? )
+    assert_raise NoMethodError do
+      msg.has_d?
+    end
   end
 end
diff --git a/ruby/tests/basic_proto2.rb b/ruby/tests/basic_proto2.rb
index a7114ea..5391c30 100755
--- a/ruby/tests/basic_proto2.rb
+++ b/ruby/tests/basic_proto2.rb
@@ -142,7 +142,7 @@
       m = TestMessageDefaults.new
 
       m.optional_int32 = -42
-      assert_equal -42, m.optional_int32
+      assert_equal( -42, m.optional_int32 )
       assert m.has_optional_int32?
       m.clear_optional_int32
       assert_equal 1, m.optional_int32
@@ -255,5 +255,20 @@
       assert_equal "tests/basic_test_proto2.proto", file_descriptor.name
       assert_equal :proto2, file_descriptor.syntax
     end
+
+    def test_oneof_fields_respond_to? # regression test for issue 9202
+      msg = proto_module::OneofMessage.new(a: "foo")
+      # `has_` prefix + "?" suffix actions should only work for oneofs fields.
+      assert msg.respond_to? :has_my_oneof?
+      assert msg.has_my_oneof?
+      assert msg.respond_to? :has_a?
+      assert msg.has_a?
+      assert msg.respond_to? :has_b?
+      assert !msg.has_b?
+      assert msg.respond_to? :has_c?
+      assert !msg.has_c?
+      assert msg.respond_to? :has_d?
+      assert !msg.has_d?
+    end
   end
 end
diff --git a/ruby/tests/common_tests.rb b/ruby/tests/common_tests.rb
index 3588469..5918c8a 100644
--- a/ruby/tests/common_tests.rb
+++ b/ruby/tests/common_tests.rb
@@ -796,7 +796,7 @@
     m.repeated_string += %w[two three]
     assert_equal %w[one two three], m.repeated_string
 
-    m.repeated_string.push *['four', 'five']
+    m.repeated_string.push( *['four', 'five'] )
     assert_equal %w[one two three four five], m.repeated_string
 
     m.repeated_string.push 'six', 'seven'
@@ -1085,8 +1085,6 @@
   end
 
   def test_json
-    # TODO: Fix JSON in JRuby version.
-    return if RUBY_PLATFORM == "java"
     m = proto_module::TestMessage.new(:optional_int32 => 1234,
                                       :optional_int64 => -0x1_0000_0000,
                                       :optional_uint32 => 0x8000_0000,
@@ -1288,6 +1286,7 @@
     m2 = proto_module::Wrapper.decode(m.to_proto)
     run_asserts.call(m2)
     m3 = proto_module::Wrapper.decode_json(m.to_json)
+    run_asserts.call(m3)
   end
 
   def test_wrapper_getters
@@ -1539,8 +1538,6 @@
       assert_nil m.bytes_as_value
     }
 
-    m = proto_module::Wrapper.new
-
     m2 = proto_module::Wrapper.new(
       double: Google::Protobuf::DoubleValue.new(value: 2.0),
       float: Google::Protobuf::FloatValue.new(value: 4.0),
@@ -1787,27 +1784,200 @@
     assert_nil h[m2]
   end
 
+  def cruby_or_jruby_9_3_or_higher?
+    # https://github.com/jruby/jruby/issues/6818 was fixed in JRuby 9.3.0.0
+    match = RUBY_PLATFORM == "java" &&
+      JRUBY_VERSION.match(/^(\d+)\.(\d+)\.\d+\.\d+$/)
+    match && (match[1].to_i > 9 || (match[1].to_i == 9 && match[2].to_i >= 3))
+  end
+
   def test_object_gc
     m = proto_module::TestMessage.new(optional_msg: proto_module::TestMessage2.new)
     m.optional_msg
-    # TODO: Remove the platform check once https://github.com/jruby/jruby/issues/6818 is released in JRuby 9.3.0.0
-    GC.start(full_mark: true, immediate_sweep: true) unless RUBY_PLATFORM == "java"
+    # https://github.com/jruby/jruby/issues/6818 was fixed in JRuby 9.3.0.0
+    GC.start(full_mark: true, immediate_sweep: true) if cruby_or_jruby_9_3_or_higher?
     m.optional_msg.inspect
   end
 
   def test_object_gc_freeze
     m = proto_module::TestMessage.new
     m.repeated_float.freeze
-    # TODO: Remove the platform check once https://github.com/jruby/jruby/issues/6818 is released in JRuby 9.3.0.0
-    GC.start(full_mark: true) unless RUBY_PLATFORM == "java"
+    # https://github.com/jruby/jruby/issues/6818 was fixed in JRuby 9.3.0.0
+    GC.start(full_mark: true) if cruby_or_jruby_9_3_or_higher?
 
     # Make sure we remember that the object is frozen.
     # The wrapper object contains this information, so we need to ensure that
     # the previous GC did not collect it.
     assert m.repeated_float.frozen?
 
-    # TODO: Remove the platform check once https://github.com/jruby/jruby/issues/6818 is released in JRuby 9.3.0.0
-    GC.start(full_mark: true, immediate_sweep: true) unless RUBY_PLATFORM == "java"
+    # https://github.com/jruby/jruby/issues/6818 was fixed in JRuby 9.3.0.0
+    GC.start(full_mark: true, immediate_sweep: true) if cruby_or_jruby_9_3_or_higher?
     assert m.repeated_float.frozen?
   end
+
+  def test_optional_fields_respond_to? # regression test for issue 9202
+    msg = proto_module::TestMessage.new
+    assert msg.respond_to? :optional_int32=
+    msg.optional_int32 = 42
+
+    assert msg.respond_to? :optional_int32
+    assert_equal 42, msg.optional_int32
+
+    assert msg.respond_to? :clear_optional_int32
+    msg.clear_optional_int32
+    assert_equal 0, msg.optional_int32
+
+    assert msg.respond_to? :has_optional_int32?
+    assert !msg.has_optional_int32?
+
+    assert !msg.respond_to?( :optional_int32_as_value= )
+    assert_raise NoMethodError do
+      msg.optional_int32_as_value = 42
+    end
+
+    assert !msg.respond_to?( :optional_int32_as_value )
+    assert_raise NoMethodError do
+      msg.optional_int32_as_value
+    end
+
+    assert msg.respond_to? :optional_enum_const
+    assert_equal 0, msg.optional_enum_const
+
+    assert !msg.respond_to?( :foo )
+    assert_raise NoMethodError do
+      msg.foo
+    end
+
+    assert !msg.respond_to?( :foo_const )
+    assert_raise NoMethodError do
+      msg.foo_const
+    end
+
+    assert !msg.respond_to?( :optional_int32_const )
+    assert_raise NoMethodError do
+      msg.optional_int32_const
+    end
+  end
+
+  def test_oneof_fields_respond_to? # regression test for issue 9202
+    msg = proto_module::OneofMessage.new
+
+    # names of the elements of a oneof and the oneof itself are valid actions.
+    assert msg.respond_to? :my_oneof
+    assert_nil msg.my_oneof
+    assert msg.respond_to? :a
+    assert_equal "", msg.a
+    assert msg.respond_to? :b
+    assert_equal 0, msg.b
+    assert msg.respond_to? :c
+    assert_nil msg.c
+    assert msg.respond_to? :d
+    assert_equal :Default, msg.d
+
+    # `clear` prefix actions work on elements of a oneof and the oneof itself.
+    assert msg.respond_to? :clear_my_oneof
+    msg.clear_my_oneof
+    # Repeatedly clearing a oneof used to cause a NoMethodError under JRuby
+    msg.clear_my_oneof
+    assert msg.respond_to? :clear_a
+    msg.clear_a
+    assert msg.respond_to? :clear_b
+    msg.clear_b
+    assert msg.respond_to? :clear_c
+    msg.clear_c
+    assert msg.respond_to? :clear_d
+    msg.clear_d
+
+    # `=` suffix actions should work on elements of a oneof but not the oneof itself.
+    assert !msg.respond_to?( :my_oneof= )
+    error = assert_raise RuntimeError do
+      msg.my_oneof = nil
+    end
+    assert_equal "Oneof accessors are read-only.", error.message
+    assert msg.respond_to? :a=
+    msg.a = "foo"
+    assert msg.respond_to? :b=
+    msg.b = 42
+    assert msg.respond_to? :c=
+    msg.c = proto_module::TestMessage2.new
+    assert msg.respond_to? :d=
+    msg.d = :Default
+
+    # `has_` prefix + "?" suffix actions work for oneofs fields.
+    assert msg.respond_to? :has_my_oneof?
+    assert msg.has_my_oneof?
+
+    # `_as_value` suffix actions should only work for wrapped fields.
+    assert !msg.respond_to?( :my_oneof_as_value )
+    assert_raise NoMethodError do
+      msg.my_oneof_as_value
+    end
+    assert !msg.respond_to?( :a_as_value )
+    assert_raise NoMethodError do
+      msg.a_as_value
+    end
+    assert !msg.respond_to?( :b_as_value )
+    assert_raise NoMethodError do
+      msg.b_as_value
+    end
+    assert !msg.respond_to?( :c_as_value )
+    assert_raise NoMethodError do
+      msg.c_as_value
+    end
+    assert !msg.respond_to?( :d_as_value )
+    assert_raise NoMethodError do
+      msg.d_as_value
+    end
+
+    # `_as_value=` suffix actions should only work for wrapped fields.
+    assert !msg.respond_to?( :my_oneof_as_value= )
+    assert_raise NoMethodError do
+      msg.my_oneof_as_value = :boom
+    end
+    assert !msg.respond_to?( :a_as_value= )
+    assert_raise NoMethodError do
+      msg.a_as_value = ""
+    end
+    assert !msg.respond_to?( :b_as_value= )
+    assert_raise NoMethodError do
+      msg.b_as_value = 42
+    end
+    assert !msg.respond_to?( :c_as_value= )
+    assert_raise NoMethodError do
+      msg.c_as_value = proto_module::TestMessage2.new
+    end
+    assert !msg.respond_to?( :d_as_value= )
+    assert_raise NoMethodError do
+      msg.d_as_value = :Default
+    end
+
+    # `_const` suffix actions should only work for enum fields.
+    assert !msg.respond_to?( :my_oneof_const )
+    assert_raise NoMethodError do
+      msg.my_oneof_const
+    end
+    assert !msg.respond_to?( :a_const )
+    assert_raise NoMethodError do
+      msg.a_const
+    end
+    assert !msg.respond_to?( :b_const )
+    assert_raise NoMethodError do
+      msg.b_const
+    end
+    assert !msg.respond_to?( :c_const )
+    assert_raise NoMethodError do
+      msg.c_const
+    end
+    assert msg.respond_to? :d_const
+    assert_equal 0, msg.d_const
+  end
+
+  def test_wrapped_fields_respond_to? # regression test for issue 9202
+    msg = proto_module::Wrapper.new
+    assert msg.respond_to?( :double_as_value= )
+    msg.double_as_value = 42
+    assert msg.respond_to?( :double_as_value )
+    assert_equal 42, msg.double_as_value
+    assert_equal Google::Protobuf::DoubleValue.new(value: 42), msg.double
+  end
 end
diff --git a/ruby/tests/repeated_field_test.rb b/ruby/tests/repeated_field_test.rb
index 7ffc0f1..6ad39b5 100755
--- a/ruby/tests/repeated_field_test.rb
+++ b/ruby/tests/repeated_field_test.rb
@@ -36,13 +36,13 @@
     end
 
     fill_test_msg(m)
-    assert_equal -10, m.repeated_int32.first
-    assert_equal -1_000_000, m.repeated_int64.first
+    assert_equal( -10, m.repeated_int32.first )
+    assert_equal( -1_000_000, m.repeated_int64.first )
     assert_equal 10, m.repeated_uint32.first
     assert_equal 1_000_000, m.repeated_uint64.first
     assert_equal true, m.repeated_bool.first
-    assert_equal -1.01,  m.repeated_float.first.round(2)
-    assert_equal -1.0000000000001, m.repeated_double.first
+    assert_equal( -1.01,  m.repeated_float.first.round(2) )
+    assert_equal( -1.0000000000001, m.repeated_double.first )
     assert_equal 'foo', m.repeated_string.first
     assert_equal "bar".encode!('ASCII-8BIT'), m.repeated_bytes.first
     assert_equal TestMessage2.new(:foo => 1), m.repeated_msg.first
@@ -61,13 +61,13 @@
       assert_nil m.send(field_name).first
     end
     fill_test_msg(m)
-    assert_equal -11, m.repeated_int32.last
-    assert_equal -1_000_001, m.repeated_int64.last
+    assert_equal( -11, m.repeated_int32.last )
+    assert_equal( -1_000_001, m.repeated_int64.last )
     assert_equal 11, m.repeated_uint32.last
     assert_equal 1_000_001, m.repeated_uint64.last
     assert_equal false, m.repeated_bool.last
-    assert_equal -1.02, m.repeated_float.last.round(2)
-    assert_equal -1.0000000000002, m.repeated_double.last
+    assert_equal( -1.02, m.repeated_float.last.round(2) )
+    assert_equal( -1.0000000000002, m.repeated_double.last )
     assert_equal 'bar', m.repeated_string.last
     assert_equal "foo".encode!('ASCII-8BIT'), m.repeated_bytes.last
     assert_equal TestMessage2.new(:foo => 2), m.repeated_msg.last
@@ -82,20 +82,20 @@
     end
     fill_test_msg(m)
 
-    assert_equal -11, m.repeated_int32.pop
-    assert_equal -10, m.repeated_int32.pop
-    assert_equal -1_000_001, m.repeated_int64.pop
-    assert_equal -1_000_000, m.repeated_int64.pop
+    assert_equal( -11, m.repeated_int32.pop )
+    assert_equal( -10, m.repeated_int32.pop )
+    assert_equal( -1_000_001, m.repeated_int64.pop )
+    assert_equal( -1_000_000, m.repeated_int64.pop )
     assert_equal 11, m.repeated_uint32.pop
     assert_equal 10, m.repeated_uint32.pop
     assert_equal 1_000_001, m.repeated_uint64.pop
     assert_equal 1_000_000, m.repeated_uint64.pop
     assert_equal false, m.repeated_bool.pop
     assert_equal true, m.repeated_bool.pop
-    assert_equal -1.02,  m.repeated_float.pop.round(2)
-    assert_equal -1.01,  m.repeated_float.pop.round(2)
-    assert_equal -1.0000000000002, m.repeated_double.pop
-    assert_equal -1.0000000000001, m.repeated_double.pop
+    assert_equal( -1.02,  m.repeated_float.pop.round(2) )
+    assert_equal( -1.01,  m.repeated_float.pop.round(2) )
+    assert_equal( -1.0000000000002, m.repeated_double.pop )
+    assert_equal( -1.0000000000001, m.repeated_double.pop )
     assert_equal 'bar', m.repeated_string.pop
     assert_equal 'foo', m.repeated_string.pop
     assert_equal "foo".encode!('ASCII-8BIT'), m.repeated_bytes.pop
@@ -487,11 +487,8 @@
   def test_shuffle!
     m = TestMessage.new
     m.repeated_string += %w(foo bar baz)
-    orig_repeated_string = m.repeated_string.clone
     result = m.repeated_string.shuffle!
     assert_equal m.repeated_string, result
-    # NOTE: sometimes it doesn't change the order...
-    # assert_not_equal m.repeated_string.to_a, orig_repeated_string.to_a
   end
 
   def test_slice!
diff --git a/src/Makefile.am b/src/Makefile.am
index 26f9d10..4139b09 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -744,6 +744,7 @@
   google/protobuf/compiler/annotation_test_util.h              \
   google/protobuf/compiler/command_line_interface_unittest.cc  \
   google/protobuf/compiler/cpp/bootstrap_unittest.cc           \
+  google/protobuf/compiler/cpp/message_size_unittest.cc        \
   google/protobuf/compiler/cpp/metadata_test.cc                \
   google/protobuf/compiler/cpp/move_unittest.cc                \
   google/protobuf/compiler/cpp/plugin_unittest.cc              \
diff --git a/src/google/protobuf/any.pb.cc b/src/google/protobuf/any.pb.cc
index 6201182..6bedc64 100644
--- a/src/google/protobuf/any.pb.cc
+++ b/src/google/protobuf/any.pb.cc
@@ -296,7 +296,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Any::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Any::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Any::GetClassData() const { return &_class_data_; }
diff --git a/src/google/protobuf/api.pb.cc b/src/google/protobuf/api.pb.cc
index 6d575a9..98eed9e 100644
--- a/src/google/protobuf/api.pb.cc
+++ b/src/google/protobuf/api.pb.cc
@@ -533,7 +533,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Api::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Api::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Api::GetClassData() const { return &_class_data_; }
@@ -957,7 +957,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Method::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Method::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Method::GetClassData() const { return &_class_data_; }
@@ -1237,7 +1237,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Mixin::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Mixin::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Mixin::GetClassData() const { return &_class_data_; }
diff --git a/src/google/protobuf/arena_unittest.cc b/src/google/protobuf/arena_unittest.cc
index b40ab99..7539b4b 100644
--- a/src/google/protobuf/arena_unittest.cc
+++ b/src/google/protobuf/arena_unittest.cc
@@ -41,7 +41,6 @@
 
 #include <google/protobuf/stubs/logging.h>
 #include <google/protobuf/stubs/common.h>
-#include <google/protobuf/test_util.h>
 #include <google/protobuf/unittest.pb.h>
 #include <google/protobuf/unittest_arena.pb.h>
 #include <gmock/gmock.h>
@@ -55,6 +54,7 @@
 #include <google/protobuf/message.h>
 #include <google/protobuf/message_lite.h>
 #include <google/protobuf/repeated_field.h>
+#include <google/protobuf/test_util.h>
 #include <google/protobuf/unknown_field_set.h>
 #include <google/protobuf/wire_format_lite.h>
 
diff --git a/src/google/protobuf/compiler/code_generator.h b/src/google/protobuf/compiler/code_generator.h
index 0a99fe1..9c0b115 100644
--- a/src/google/protobuf/compiler/code_generator.h
+++ b/src/google/protobuf/compiler/code_generator.h
@@ -189,9 +189,9 @@
 // Several code generators treat the parameter argument as holding a
 // list of options separated by commas.  This helper function parses
 // a set of comma-delimited name/value pairs: e.g.,
-//   "foo=bar,baz,qux=corge"
+//   "foo=bar,baz,moo=corge"
 // parses to the pairs:
-//   ("foo", "bar"), ("baz", ""), ("qux", "corge")
+//   ("foo", "bar"), ("baz", ""), ("moo", "corge")
 PROTOC_EXPORT void ParseGeneratorParameter(
     const std::string&, std::vector<std::pair<std::string, std::string> >*);
 
diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc
index c690935..f48135e 100644
--- a/src/google/protobuf/compiler/command_line_interface_unittest.cc
+++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc
@@ -50,24 +50,27 @@
 #include <google/protobuf/testing/file.h>
 #include <google/protobuf/testing/file.h>
 #include <google/protobuf/any.pb.h>
-#include <google/protobuf/compiler/mock_code_generator.h>
-#include <google/protobuf/compiler/subprocess.h>
-#include <google/protobuf/compiler/code_generator.h>
-#include <google/protobuf/compiler/command_line_interface.h>
 #include <google/protobuf/test_util2.h>
 #include <google/protobuf/unittest.pb.h>
 #include <google/protobuf/unittest_custom_options.pb.h>
-#include <google/protobuf/io/printer.h>
-#include <google/protobuf/io/zero_copy_stream.h>
 #include <google/protobuf/descriptor.pb.h>
-#include <google/protobuf/descriptor.h>
 #include <google/protobuf/testing/googletest.h>
 #include <gtest/gtest.h>
 #include <google/protobuf/stubs/strutil.h>
 #include <google/protobuf/stubs/substitute.h>
+#include <google/protobuf/compiler/code_generator.h>
+#include <google/protobuf/compiler/command_line_interface.h>
+#include <google/protobuf/compiler/mock_code_generator.h>
+#include <google/protobuf/compiler/subprocess.h>
+#include <google/protobuf/descriptor.h>
 #include <google/protobuf/io/io_win32.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
 
 
+// Must be included last.
+#include <google/protobuf/port_def.inc>
+
 namespace google {
 namespace protobuf {
 namespace compiler {
@@ -2777,6 +2780,8 @@
 
 #endif  // !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
 
+#include <google/protobuf/port_undef.inc>
+
 }  // namespace compiler
 }  // namespace protobuf
 }  // namespace google
diff --git a/src/google/protobuf/compiler/cpp/bootstrap_unittest.cc b/src/google/protobuf/compiler/cpp/bootstrap_unittest.cc
index fc6e3b2..2619e60 100644
--- a/src/google/protobuf/compiler/cpp/bootstrap_unittest.cc
+++ b/src/google/protobuf/compiler/cpp/bootstrap_unittest.cc
@@ -46,7 +46,6 @@
 
 #include <google/protobuf/testing/file.h>
 #include <google/protobuf/testing/file.h>
-#include <google/protobuf/compiler/cpp/helpers.h>
 #include <google/protobuf/compiler/cpp/generator.h>
 #include <google/protobuf/compiler/importer.h>
 #include <google/protobuf/test_util2.h>
@@ -56,6 +55,7 @@
 #include <google/protobuf/testing/googletest.h>
 #include <gtest/gtest.h>
 #include <google/protobuf/stubs/substitute.h>
+#include <google/protobuf/compiler/cpp/helpers.h>
 #include <google/protobuf/stubs/map_util.h>
 #include <google/protobuf/stubs/stl_util.h>
 
diff --git a/src/google/protobuf/compiler/cpp/helpers.cc b/src/google/protobuf/compiler/cpp/helpers.cc
index 5d95e72..f959a42 100644
--- a/src/google/protobuf/compiler/cpp/helpers.cc
+++ b/src/google/protobuf/compiler/cpp/helpers.cc
@@ -1529,7 +1529,8 @@
 inline bool IsMessageOwnedArenaEligible(const Descriptor* desc,
                                         const Options& options) {
   return GetOptimizeFor(desc->file(), options) != FileOptions::LITE_RUNTIME &&
-         AllocExpected(desc) && !options.bootstrap;
+         !options.bootstrap && !options.opensource_runtime &&
+         AllocExpected(desc);
 }
 
 bool EnableMessageOwnedArena(const Descriptor* desc, const Options& options) {
@@ -1543,6 +1544,14 @@
   return false;
 }
 
+bool HasMessageFieldOrExtension(const Descriptor* desc) {
+  if (desc->extension_range_count() > 0) return true;
+  for (const auto* f : FieldRange(desc)) {
+    if (f->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) return true;
+  }
+  return false;
+}
+
 }  // namespace cpp
 }  // namespace compiler
 }  // namespace protobuf
diff --git a/src/google/protobuf/compiler/cpp/helpers.h b/src/google/protobuf/compiler/cpp/helpers.h
index df71434..f51e55f 100644
--- a/src/google/protobuf/compiler/cpp/helpers.h
+++ b/src/google/protobuf/compiler/cpp/helpers.h
@@ -128,11 +128,11 @@
 // Returns the non-nested type name for the given type.  If "qualified" is
 // true, prefix the type with the full namespace.  For example, if you had:
 //   package foo.bar;
-//   message Baz { message Qux {} }
-// Then the qualified ClassName for Qux would be:
-//   ::foo::bar::Baz_Qux
+//   message Baz { message Moo {} }
+// Then the qualified ClassName for Moo would be:
+//   ::foo::bar::Baz_Moo
 // While the non-qualified version would be:
-//   Baz_Qux
+//   Baz_Moo
 inline std::string ClassName(const Descriptor* descriptor, bool qualified) {
   return qualified ? QualifiedClassName(descriptor, Options())
                    : ClassName(descriptor);
@@ -735,6 +735,18 @@
   return "";
 }
 
+// Returns true if this message has a _tracker_ field.
+inline bool HasTracker(const Descriptor* desc, const Options& options) {
+  return options.field_listener_options.inject_field_listener_events &&
+         desc->file()->options().optimize_for() !=
+             google::protobuf::FileOptions::LITE_RUNTIME;
+}
+
+// Returns true if this message needs an Impl_ struct for it's data.
+inline bool HasImplData(const Descriptor* desc, const Options& options) {
+  return !HasSimpleBaseClass(desc, options);
+}
+
 // Formatter is a functor class which acts as a closure around printer and
 // the variable map. It's much like printer->Print except it supports both named
 // variables that are substituted using a key value map and direct arguments. In
@@ -1024,6 +1036,8 @@
 
 bool IsUtf8String(const FieldDescriptor* field);
 
+bool HasMessageFieldOrExtension(const Descriptor* desc);
+
 }  // namespace cpp
 }  // namespace compiler
 }  // namespace protobuf
diff --git a/src/google/protobuf/compiler/cpp/message.cc b/src/google/protobuf/compiler/cpp/message.cc
index 46cc3d5..87f7612 100644
--- a/src/google/protobuf/compiler/cpp/message.cc
+++ b/src/google/protobuf/compiler/cpp/message.cc
@@ -547,9 +547,7 @@
   for (const auto& annotation : accessor_annotations_to_hooks) {
     (*variables)[annotation.first] = "";
   }
-  if (!options.field_listener_options.inject_field_listener_events ||
-      descriptor->file()->options().optimize_for() ==
-          google::protobuf::FileOptions::LITE_RUNTIME) {
+  if (!HasTracker(descriptor, options)) {
     return;
   }
   StringPiece tracker = (*variables)["tracker"];
@@ -630,9 +628,7 @@
   variables_["annotate_bytesize"] = "";
   variables_["annotate_mergefrom"] = "";
 
-  if (options.field_listener_options.inject_field_listener_events &&
-      descriptor->file()->options().optimize_for() !=
-          google::protobuf::FileOptions::LITE_RUNTIME) {
+  if (HasTracker(descriptor_, options_)) {
     const std::string injector_template =
         StrCat("  ", variables_["tracker"], ".");
 
@@ -1865,9 +1861,7 @@
         "\n");
   }
 
-  if (options_.field_listener_options.inject_field_listener_events &&
-      descriptor_->file()->options().optimize_for() !=
-          google::protobuf::FileOptions::LITE_RUNTIME) {
+  if (HasTracker(descriptor_, options_)) {
     format("static ::$proto_ns$::AccessListener<$1$> _tracker_;\n",
            ClassName(descriptor_));
   }
@@ -1950,9 +1944,12 @@
   }
 
   format.Outdent();
-  format(
-      "};\n"
-      "union { Impl_ _impl_; };\n");
+  format("};\n");
+
+  // Only create the _impl_ field if it contains data.
+  if (HasImplData(descriptor_, options_)) {
+    format("union { Impl_ _impl_; };\n");
+  }
 
   // The TableStruct struct needs access to the private parts, in order to
   // construct the offsets of all members.
@@ -2185,9 +2182,7 @@
         "\n");
   }
 
-  if (options_.field_listener_options.inject_field_listener_events &&
-      descriptor_->file()->options().optimize_for() !=
-          google::protobuf::FileOptions::LITE_RUNTIME) {
+  if (HasTracker(descriptor_, options_)) {
     format(
         "::$proto_ns$::AccessListener<$classtype$> "
         "$1$::$tracker$(&FullMessageName);\n",
@@ -2496,7 +2491,7 @@
 void MessageGenerator::GenerateConstexprConstructor(io::Printer* printer) {
   Formatter format(printer, variables_);
 
-  if (IsMapEntryMessage(descriptor_)) {
+  if (IsMapEntryMessage(descriptor_) || !HasImplData(descriptor_, options_)) {
     format(
         "PROTOBUF_CONSTEXPR $classname$::$classname$(\n"
         "    ::_pbi::ConstantInitialized) {}\n");
@@ -2648,65 +2643,69 @@
         "  : $superclass$() {\n");
     format.Indent();
 
-    const char* field_sep = " ";
-    const auto put_sep = [&] {
-      format("\n$1$ ", field_sep);
-      field_sep = ",";
-    };
+    if (HasImplData(descriptor_, options_)) {
+      const char* field_sep = " ";
+      const auto put_sep = [&] {
+        format("\n$1$ ", field_sep);
+        field_sep = ",";
+      };
 
-    format("new (&_impl_) Impl_{");
-    format.Indent();
+      format("new (&_impl_) Impl_{");
+      format.Indent();
 
-    if (descriptor_->extension_range_count() > 0) {
-      put_sep();
-      format("/*decltype($extensions$)*/{}");
-    }
-    if (!inlined_string_indices_.empty()) {
-      // Do not copy inlined_string_donated_, because this is not an arena
-      // constructor.
-      put_sep();
-      format("decltype($inlined_string_donated_array$){}");
-    }
-    bool need_to_emit_cached_size = !HasSimpleBaseClass(descriptor_, options_);
-    if (!has_bit_indices_.empty()) {
-      put_sep();
-      format("decltype($has_bits$){from.$has_bits$}");
+      if (descriptor_->extension_range_count() > 0) {
+        put_sep();
+        format("/*decltype($extensions$)*/{}");
+      }
+      if (!inlined_string_indices_.empty()) {
+        // Do not copy inlined_string_donated_, because this is not an arena
+        // constructor.
+        put_sep();
+        format("decltype($inlined_string_donated_array$){}");
+      }
+      bool need_to_emit_cached_size =
+          !HasSimpleBaseClass(descriptor_, options_);
+      if (!has_bit_indices_.empty()) {
+        put_sep();
+        format("decltype($has_bits$){from.$has_bits$}");
+        if (need_to_emit_cached_size) {
+          put_sep();
+          format("/*decltype($cached_size$)*/{}");
+          need_to_emit_cached_size = false;
+        }
+      }
+
+      // Initialize member variables with arena constructor.
+      for (auto field : optimized_order_) {
+        put_sep();
+        field_generators_.get(field).GenerateCopyAggregateInitializer(printer);
+      }
+      for (auto oneof : OneOfRange(descriptor_)) {
+        put_sep();
+        format("decltype(_impl_.$1$_){}", oneof->name());
+      }
+
       if (need_to_emit_cached_size) {
         put_sep();
         format("/*decltype($cached_size$)*/{}");
-        need_to_emit_cached_size = false;
       }
-    }
 
-    // Initialize member variables with arena constructor.
-    for (auto field : optimized_order_) {
-      put_sep();
-      field_generators_.get(field).GenerateCopyAggregateInitializer(printer);
+      if (descriptor_->real_oneof_decl_count() != 0) {
+        put_sep();
+        format("/*decltype($oneof_case$)*/{}");
+      }
+      if (num_weak_fields_ > 0) {
+        put_sep();
+        format("decltype($weak_field_map$){from.$weak_field_map$}");
+      }
+      if (IsAnyMessage(descriptor_, options_)) {
+        put_sep();
+        format(
+            "/*decltype($any_metadata$)*/{&_impl_.type_url_, &_impl_.value_}");
+      }
+      format.Outdent();
+      format("};\n\n");
     }
-    for (auto oneof : OneOfRange(descriptor_)) {
-      put_sep();
-      format("decltype(_impl_.$1$_){}", oneof->name());
-    }
-
-    if (need_to_emit_cached_size) {
-      put_sep();
-      format("/*decltype($cached_size$)*/{}");
-    }
-
-    if (descriptor_->real_oneof_decl_count() != 0) {
-      put_sep();
-      format("/*decltype($oneof_case$)*/{}");
-    }
-    if (num_weak_fields_ > 0) {
-      put_sep();
-      format("decltype($weak_field_map$){from.$weak_field_map$}");
-    }
-    if (IsAnyMessage(descriptor_, options_)) {
-      put_sep();
-      format("/*decltype($any_metadata$)*/{&_impl_.type_url_, &_impl_.value_}");
-    }
-    format.Outdent();
-    format("};\n\n");
 
     format(
         "_internal_metadata_.MergeFrom<$unknown_fields_type$>(from._internal_"
@@ -3145,7 +3144,7 @@
       format(
           "const ::$proto_ns$::Message::ClassData "
           "$classname$::_class_data_ = {\n"
-          "    ::$proto_ns$::Message::CopyWithSizeCheck,\n"
+          "    ::$proto_ns$::Message::CopyWithSourceCheck,\n"
           "    $classname$::MergeImpl\n"
           "};\n"
           "const ::$proto_ns$::Message::ClassData*"
@@ -3359,12 +3358,12 @@
     // takes in the Message base class as a parameter); instead we just
     // let the base Message::CopyFrom take care of it.  The base MergeFrom
     // knows how to quickly confirm the types exactly match, and if so, will
-    // use GetClassData() to get the address of Message::CopyWithSizeCheck,
+    // use GetClassData() to get the address of Message::CopyWithSourceCheck,
     // which calls Clear() and then MergeFrom(), as well as making sure that
-    // clearing the destination message doesn't alter the size of the source,
-    // when in debug builds.
-    // Most callers avoid this by passing a "from" message that is the same
-    // type as the message being merged into, rather than a generic Message.
+    // clearing the destination message doesn't alter the source, when in debug
+    // builds. Most callers avoid this by passing a "from" message that is the
+    // same type as the message being merged into, rather than a generic
+    // Message.
   }
 
   // Generate the class-specific CopyFrom.
@@ -3376,20 +3375,33 @@
 
   format("if (&from == this) return;\n");
 
-  if (!options_.opensource_runtime) {
+  if (!options_.opensource_runtime && HasMessageFieldOrExtension(descriptor_)) {
     // This check is disabled in the opensource release because we're
     // concerned that many users do not define NDEBUG in their release builds.
+    // It is also disabled if a message has neither message fields nor
+    // extensions, as it's impossible to copy from its descendant.
+    //
+    // Note that FailIfCopyFromDescendant is implemented by reflection and not
+    // available for lite runtime. In that case, check if the size of the source
+    // has changed after Clear.
+    format("#ifndef NDEBUG\n");
+    if (HasDescriptorMethods(descriptor_->file(), options_)) {
+      format("FailIfCopyFromDescendant(this, from);\n");
+    } else {
+      format("size_t from_size = from.ByteSizeLong();\n");
+    }
     format(
-        "#ifndef NDEBUG\n"
-        "size_t from_size = from.ByteSizeLong();\n"
         "#endif\n"
-        "Clear();\n"
-        "#ifndef NDEBUG\n"
-        "$CHK$_EQ(from_size, from.ByteSizeLong())\n"
-        "  << \"Source of CopyFrom changed when clearing target.  Either \"\n"
-        "     \"source is a nested message in target (not allowed), or \"\n"
-        "     \"another thread is modifying the source.\";\n"
-        "#endif\n");
+        "Clear();\n");
+    if (!HasDescriptorMethods(descriptor_->file(), options_)) {
+      format(
+          "#ifndef NDEBUG\n"
+          "$CHK$_EQ(from_size, from.ByteSizeLong())\n"
+          "  << \"Source of CopyFrom changed when clearing target.  Either \"\n"
+          "     \"source is a nested message in target (not allowed), or \"\n"
+          "     \"another thread is modifying the source.\";\n"
+          "#endif\n");
+    }
   } else {
     format("Clear();\n");
   }
diff --git a/src/google/protobuf/compiler/cpp/message_size_unittest.cc b/src/google/protobuf/compiler/cpp/message_size_unittest.cc
new file mode 100644
index 0000000..1209350
--- /dev/null
+++ b/src/google/protobuf/compiler/cpp/message_size_unittest.cc
@@ -0,0 +1,272 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <google/protobuf/unittest.pb.h>
+#include <google/protobuf/descriptor.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace cpp {
+
+// Can't use an anonymous namespace here due to brokenness of Tru64 compiler.
+namespace cpp_unittest {
+
+
+#if !defined(GOOGLE_CHECK_MESSAGE_SIZE)
+#define GOOGLE_CHECK_MESSAGE_SIZE(t, expected)
+#endif
+
+// Mock structures to lock down the size of messages in a platform-independent
+// way.  The commented sizes only apply when build with clang x86_64.
+struct MockMessageBase {
+  virtual ~MockMessageBase() = default;  // 8 bytes vtable
+  void* internal_metadata;               // 8 bytes
+};
+GOOGLE_CHECK_MESSAGE_SIZE(MockMessageBase, 16);
+
+struct MockZeroFieldsBase : public MockMessageBase {
+  int cached_size;  // 4 bytes
+  // + 4 bytes padding
+};
+GOOGLE_CHECK_MESSAGE_SIZE(MockZeroFieldsBase, 24);
+
+struct MockExtensionSet {
+  void* arena;       // 8 bytes
+  int16_t capacity;  // 4 bytes
+  int16_t size;      // 4 bytes
+  void* data;        // 8 bytes
+};
+GOOGLE_CHECK_MESSAGE_SIZE(MockExtensionSet, 24);
+
+struct MockRepeatedPtrField {
+  void* arena;       // 8 bytes
+  int current_size;  // 4 bytes
+  int total_size;    // 4 bytes
+  void* data;        // 8 bytes
+};
+GOOGLE_CHECK_MESSAGE_SIZE(MockRepeatedPtrField, 24);
+
+struct MockRepeatedField {
+  int current_size;  // 4 bytes
+  int total_size;    // 4 bytes
+  void* data;        // 8 bytes
+};
+GOOGLE_CHECK_MESSAGE_SIZE(MockRepeatedField, 16);
+
+TEST(GeneratedMessageTest, MockSizes) {
+  // Consistency checks -- if these fail, the tests below will definitely fail.
+  GOOGLE_CHECK_EQ(sizeof(MessageLite), sizeof(MockMessageBase));
+  GOOGLE_CHECK_EQ(sizeof(Message), sizeof(MockMessageBase));
+  GOOGLE_CHECK_EQ(sizeof(internal::ZeroFieldsBase), sizeof(MockZeroFieldsBase));
+  GOOGLE_CHECK_EQ(sizeof(internal::ExtensionSet), sizeof(MockExtensionSet));
+  GOOGLE_CHECK_EQ(sizeof(RepeatedPtrField<std::string>), sizeof(MockRepeatedPtrField));
+  GOOGLE_CHECK_EQ(sizeof(RepeatedField<int>), sizeof(MockRepeatedField));
+}
+
+TEST(GeneratedMessageTest, EmptyMessageSize) {
+  EXPECT_EQ(sizeof(protobuf_unittest::TestEmptyMessage),
+            sizeof(MockZeroFieldsBase));
+}
+
+TEST(GeneratedMessageTest, ReservedSize) {
+  EXPECT_EQ(sizeof(protobuf_unittest::TestReservedFields),
+            sizeof(MockZeroFieldsBase));
+}
+
+TEST(GeneratedMessageTest, EmptyMessageWithExtensionsSize) {
+  struct MockGenerated : public MockMessageBase {  // 16 bytes
+    MockExtensionSet extensions;                   // 24 bytes
+    int cached_size;                               // 4 bytes
+    // + 4 bytes of padding
+  };
+  GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 48);
+  EXPECT_EQ(sizeof(protobuf_unittest::TestEmptyMessageWithExtensions),
+            sizeof(MockGenerated));
+}
+
+TEST(GeneratedMessageTest, RecursiveMessageSize) {
+  struct MockGenerated : public MockMessageBase {  // 16 bytes
+    int has_bits[1];                               // 4 bytes
+    int cached_size;                               // 4 bytes
+    void* a;                                       // 8 bytes
+    int32_t i;                                     // 4 bytes
+    // + 4 bytes padding
+  };
+  GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 40);
+  EXPECT_EQ(sizeof(protobuf_unittest::TestRecursiveMessage),
+            sizeof(MockGenerated));
+}
+
+TEST(GeneratedMessageTest, OneStringSize) {
+  struct MockGenerated : public MockMessageBase {  // 16 bytes
+    int has_bits[1];                               // 4 bytes
+    int cached_size;                               // 4 bytes
+    void* data;                                    // 8 bytes
+  };
+  GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 32);
+  EXPECT_EQ(sizeof(protobuf_unittest::OneString), sizeof(MockGenerated));
+}
+
+TEST(GeneratedMessageTest, MoreStringSize) {
+  struct MockGenerated : public MockMessageBase {  // 16 bytes
+    int has_bits[1];                               // 4 bytes
+    int cached_size;                               // 4 bytes
+    MockRepeatedPtrField data;                     // 24 bytes
+  };
+  GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 48);
+  EXPECT_EQ(sizeof(protobuf_unittest::MoreString), sizeof(MockGenerated));
+}
+
+TEST(GeneratedMessageTest, Int32MessageSize) {
+  struct MockGenerated : public MockMessageBase {  // 16 bytes
+    int has_bits[1];                               // 4 bytes
+    int cached_size;                               // 4 bytes
+    int32_t data;                                  // 4 bytes
+    // + 4 bytes padding
+  };
+  GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 32);
+  EXPECT_EQ(sizeof(protobuf_unittest::Int32Message), sizeof(MockGenerated));
+}
+
+TEST(GeneratedMessageTest, Int64MessageSize) {
+  struct MockGenerated : public MockMessageBase {  // 16 bytes
+    int has_bits[1];                               // 4 bytes
+    int cached_size;                               // 4 bytes
+    int64_t data;                                  // 8 bytes
+  };
+  GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 32);
+  EXPECT_EQ(sizeof(protobuf_unittest::Int64Message), sizeof(MockGenerated));
+}
+
+TEST(GeneratedMessageTest, BoolMessageSize) {
+  struct MockGenerated : public MockMessageBase {  // 16 bytes
+    int has_bits[1];                               // 4 bytes
+    int cached_size;                               // 4 bytes
+    bool data;                                     // 1 byte
+    // + 3 bytes padding
+  };
+  GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 32);
+  EXPECT_EQ(sizeof(protobuf_unittest::BoolMessage), sizeof(MockGenerated));
+}
+
+TEST(GeneratedMessageTest, OneofSize) {
+  struct MockGenerated : public MockMessageBase {  // 16 bytes
+    void* foo;                                     // 8 bytes
+    int cached_size;                               // 4 bytes
+    uint32_t oneof_case[1];                        // 4 bytes
+  };
+  GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 32);
+  EXPECT_EQ(sizeof(protobuf_unittest::TestOneof), sizeof(MockGenerated));
+}
+
+TEST(GeneratedMessageTest, Oneof2Size) {
+  struct MockGenerated : public MockMessageBase {  // 16 bytes
+    int has_bits[1];                               // 4 bytes
+    int cached_size;                               // 4 bytes
+    void* baz_string;                              // 8 bytes
+    int32_t baz_int;                               // 4 bytes
+                                                   // + 4 bytes padding
+    void* foo;                                     // 8 bytes
+    void* bar;                                     // 8 bytes
+    uint32_t oneof_case[2];                        // 8 bytes
+  };
+  GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 64);
+  EXPECT_EQ(sizeof(protobuf_unittest::TestOneof2), sizeof(MockGenerated));
+}
+
+TEST(GeneratedMessageTest, FieldOrderingsSize) {
+  struct MockGenerated : public MockMessageBase {  // 16 bytes
+    int has_bits[1];                               // 4 bytes
+    int cached_size;                               // 4 bytes
+    MockExtensionSet extensions;                   // 24 bytes
+    void* my_string;                               // 8 bytes
+    void* optional_nested_message;                 // 8 bytes
+    int64_t my_int;                                // 8 bytes
+    float my_float;                                // 4 bytes
+    // + 4 bytes of padding
+  };
+  GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 80);
+  EXPECT_EQ(sizeof(protobuf_unittest::TestFieldOrderings), sizeof(MockGenerated));
+}
+
+TEST(GeneratedMessageTest, TestMessageSize) {
+  // We expect the message to contain (not in this order):
+  struct MockGenerated : public MockMessageBase {  // 16 bytes
+    int has_bits[1];                               // 4 bytes
+    int cached_size;                               // 4 bytes
+    void* m4;                                      // 8 bytes
+    int64_t m2;                                    // 8 bytes
+    bool m1;                                       // 1 bytes
+    bool m3;                                       // 1 bytes
+                                                   // + 2 bytes padding
+    int m5;                                        // 4 bytes
+    int64_t m6;                                    // 8 bytes
+  };
+  GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 56);
+  EXPECT_EQ(sizeof(protobuf_unittest::TestMessageSize), sizeof(MockGenerated));
+}
+
+TEST(GeneratedMessageTest, PackedTypesSize) {
+  struct MockGenerated : public MockMessageBase {  // 16 bytes
+    MockRepeatedField packed_int32;                // 16 bytes
+    int packed_int32_cached_byte_size;             // 4 bytes + 4 bytes padding
+    MockRepeatedField packed_int64;                // 16 bytes
+    int packed_int64_cached_byte_size;             // 4 bytes + 4 bytes padding
+    MockRepeatedField packed_uint32;               // 16 bytes
+    int packed_uint32_cached_byte_size;            // 4 bytes + 4 bytes padding
+    MockRepeatedField packed_uint64;               // 16 bytes
+    int packed_uint64_cached_byte_size;            // 4 bytes + 4 bytes padding
+    MockRepeatedField packed_sint32;               // 16 bytes
+    int packed_sint32_cached_byte_size;            // 4 bytes + 4 bytes padding
+    MockRepeatedField packed_sint64;               // 16 bytes
+    int packed_sint64_cached_byte_size;            // 4 bytes + 4 bytes padding
+    MockRepeatedField packed_fixed32;              // 16 bytes
+    MockRepeatedField packed_fixed64;              // 16 bytes
+    MockRepeatedField packed_sfixed32;             // 16 bytes
+    MockRepeatedField packed_sfixed64;             // 16 bytes
+    MockRepeatedField packed_float;                // 16 bytes
+    MockRepeatedField packed_double;               // 16 bytes
+    MockRepeatedField packed_bool;                 // 16 bytes
+    MockRepeatedField packed_enum;                 // 16 bytes
+    int packed_enum_cached_byte_size;              // 4 bytes
+    int cached_size;                               // 4 bytes
+  };
+  GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 16 * 15 + 8 * 6 + 8);
+  EXPECT_EQ(sizeof(protobuf_unittest::TestPackedTypes), sizeof(MockGenerated));
+}
+
+}  // namespace cpp_unittest
+}  // namespace cpp
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
diff --git a/src/google/protobuf/compiler/cpp/metadata_test.cc b/src/google/protobuf/compiler/cpp/metadata_test.cc
index 363e55a..1ffd357 100644
--- a/src/google/protobuf/compiler/cpp/metadata_test.cc
+++ b/src/google/protobuf/compiler/cpp/metadata_test.cc
@@ -32,13 +32,13 @@
 
 #include <google/protobuf/testing/file.h>
 #include <google/protobuf/testing/file.h>
-#include <google/protobuf/compiler/cpp/helpers.h>
 #include <google/protobuf/compiler/cpp/generator.h>
-#include <google/protobuf/compiler/annotation_test_util.h>
 #include <google/protobuf/compiler/command_line_interface.h>
 #include <google/protobuf/descriptor.pb.h>
 #include <google/protobuf/testing/googletest.h>
 #include <gtest/gtest.h>
+#include <google/protobuf/compiler/annotation_test_util.h>
+#include <google/protobuf/compiler/cpp/helpers.h>
 
 namespace google {
 namespace protobuf {
diff --git a/src/google/protobuf/compiler/cpp/names.h b/src/google/protobuf/compiler/cpp/names.h
index b27b596..7404ac5 100644
--- a/src/google/protobuf/compiler/cpp/names.h
+++ b/src/google/protobuf/compiler/cpp/names.h
@@ -51,9 +51,9 @@
 //
 // For example, if you had:
 //   package foo.bar;
-//   message Baz { message Qux {} }
+//   message Baz { message Moo {} }
 // Then the non-qualified version would be:
-//   Baz_Qux
+//   Baz_Moo
 std::string ClassName(const Descriptor* descriptor);
 std::string ClassName(const EnumDescriptor* enum_descriptor);
 
@@ -61,9 +61,9 @@
 //
 // For example, if you had:
 //   package foo.bar;
-//   message Baz { message Qux {} }
-// Then the qualified ClassName for Qux would be:
-//   ::foo::bar::Baz_Qux
+//   message Baz { message Moo {} }
+// Then the qualified ClassName for Moo would be:
+//   ::foo::bar::Baz_Moo
 std::string QualifiedClassName(const Descriptor* d);
 std::string QualifiedClassName(const EnumDescriptor* d);
 std::string QualifiedExtensionName(const FieldDescriptor* d);
diff --git a/src/google/protobuf/compiler/cpp/options.h b/src/google/protobuf/compiler/cpp/options.h
index 6f40bb6..d7fe6a2 100644
--- a/src/google/protobuf/compiler/cpp/options.h
+++ b/src/google/protobuf/compiler/cpp/options.h
@@ -79,7 +79,7 @@
   bool opensource_runtime = false;
   bool annotate_accessor = false;
   bool unused_field_stripping = false;
-  bool unverified_lazy_message_sets = true;
+  bool unverified_lazy_message_sets = false;
   bool eagerly_verified_lazy = true;
   bool profile_driven_inline_string = true;
   bool message_owned_arena_trial = false;
diff --git a/src/google/protobuf/compiler/cpp/plugin_unittest.cc b/src/google/protobuf/compiler/cpp/plugin_unittest.cc
index aa4ef4e..f023dcf 100644
--- a/src/google/protobuf/compiler/cpp/plugin_unittest.cc
+++ b/src/google/protobuf/compiler/cpp/plugin_unittest.cc
@@ -198,7 +198,7 @@
                              "    ctype = CORD\n"
                              "  ];\n"
                              "\n"
-                             "  oneof Qux {\n"
+                             "  oneof Moo {\n"
                              "    int64 oneOfInt = 20;\n"
                              "    string oneOfString = 21;\n"
                              "    Baz oneOfMessage = 22;\n"
diff --git a/src/google/protobuf/compiler/cpp/unittest.cc b/src/google/protobuf/compiler/cpp/unittest.cc
index 3d0b998..e2730d7 100644
--- a/src/google/protobuf/compiler/cpp/unittest.cc
+++ b/src/google/protobuf/compiler/cpp/unittest.cc
@@ -49,7 +49,6 @@
 #include <google/protobuf/unittest.pb.h>
 #include <google/protobuf/unittest_embed_optimize_for.pb.h>
 #include <google/protobuf/unittest_optimize_for.pb.h>
-
 #include <google/protobuf/test_util.h>
 
 #define MESSAGE_TEST_NAME MessageTest
diff --git a/src/google/protobuf/compiler/cpp/unittest.inc b/src/google/protobuf/compiler/cpp/unittest.inc
index d3dad6f..0b47176 100644
--- a/src/google/protobuf/compiler/cpp/unittest.inc
+++ b/src/google/protobuf/compiler/cpp/unittest.inc
@@ -58,16 +58,16 @@
 #endif
 #include <google/protobuf/compiler/cpp/helpers.h>
 #include <google/protobuf/compiler/cpp/test_bad_identifiers.pb.h>
-#include <google/protobuf/compiler/scc.h>
-#include <google/protobuf/compiler/importer.h>
-#include <google/protobuf/test_util2.h>
 #include <google/protobuf/unittest_no_generic_services.pb.h>
-#include <google/protobuf/io/coded_stream.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
 #include <google/protobuf/descriptor.pb.h>
 #include <google/protobuf/arena.h>
 #include <google/protobuf/descriptor.h>
 #include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/compiler/scc.h>
+#include <google/protobuf/compiler/importer.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/test_util2.h>
 
 #include <google/protobuf/stubs/callback.h>
 #include <google/protobuf/stubs/common.h>
@@ -393,6 +393,7 @@
 }
 
 
+#if !defined(PROTOBUF_TEST_NO_DESCRIPTORS) || defined(NDEBUG)
 TEST(GENERATED_MESSAGE_TEST_NAME, CopyFrom) {
   UNITTEST::TestAllTypes message1, message2;
 
@@ -405,6 +406,20 @@
   TestUtil::ExpectAllFieldsSet(message2);
 }
 
+TEST(GENERATED_MESSAGE_TEST_NAME, CopyAssignmentOperator) {
+  UNITTEST::TestAllTypes message1;
+  TestUtil::SetAllFields(&message1);
+
+  UNITTEST::TestAllTypes message2;
+  message2 = message1;
+  TestUtil::ExpectAllFieldsSet(message2);
+
+  // Make sure that self-assignment does something sane.
+  message2.operator=(message2);
+  TestUtil::ExpectAllFieldsSet(message2);
+}
+#endif  // !PROTOBUF_TEST_NO_DESCRIPTORS || NDEBUG
+
 
 TEST(GENERATED_MESSAGE_TEST_NAME, SwapWithEmpty) {
   UNITTEST::TestAllTypes message1, message2;
@@ -579,20 +594,7 @@
   TestUtil::ExpectAllFieldsSet(*message2_heap);
 }
 
-TEST(GENERATED_MESSAGE_TEST_NAME, CopyAssignmentOperator) {
-  UNITTEST::TestAllTypes message1;
-  TestUtil::SetAllFields(&message1);
-
-  UNITTEST::TestAllTypes message2;
-  message2 = message1;
-  TestUtil::ExpectAllFieldsSet(message2);
-
-  // Make sure that self-assignment does something sane.
-  message2.operator=(message2);
-  TestUtil::ExpectAllFieldsSet(message2);
-}
-
-#if !defined(PROTOBUF_TEST_NO_DESCRIPTORS) || PROTOBUF_RTTI
+#ifndef PROTOBUF_TEST_NO_DESCRIPTORS
 TEST(GENERATED_MESSAGE_TEST_NAME, UpcastCopyFrom) {
   // Test the CopyFrom method that takes in the generic const Message&
   // parameter.
@@ -605,9 +607,6 @@
 
   TestUtil::ExpectAllFieldsSet(message2);
 }
-#endif
-
-#ifndef PROTOBUF_TEST_NO_DESCRIPTORS
 
 TEST(GENERATED_MESSAGE_TEST_NAME, DynamicMessageCopyFrom) {
   // Test copying from a DynamicMessage, which must fall back to using
@@ -824,7 +823,7 @@
   message.mutable_messagefield()->set_c(6);
 
   message.add_repeatedprimitivefield(8);
-  message.add_repeatedstringfield("qux");
+  message.add_repeatedstringfield("moo");
   message.add_repeatedenumfield(UNITTEST::FOREIGN_BAR);
   message.add_repeatedmessagefield()->set_c(15);
 
@@ -834,7 +833,7 @@
   EXPECT_EQ(6, message.messagefield().c());
 
   EXPECT_EQ(8, message.repeatedprimitivefield(0));
-  EXPECT_EQ("qux", message.repeatedstringfield(0));
+  EXPECT_EQ("moo", message.repeatedstringfield(0));
   EXPECT_EQ(UNITTEST::FOREIGN_BAR, message.repeatedenumfield(0));
   EXPECT_EQ(15, message.repeatedmessagefield(0).c());
 }
@@ -949,7 +948,6 @@
 
 #endif  // !PROTOBUF_TEST_NO_DESCRIPTORS
 
-
 TEST(GENERATED_MESSAGE_TEST_NAME, FieldConstantValues) {
   UNITTEST::TestRequired message;
   EXPECT_EQ(UNITTEST::TestAllTypes_NestedMessage::kBbFieldNumber, 1);
@@ -1474,7 +1472,7 @@
   TestUtil::ExpectAtMostOneFieldSetInOneof(message);
 
 
-  message.set_foo_bytes("qux");
+  message.set_foo_bytes("moo");
   EXPECT_TRUE(message.has_foo_bytes());
   TestUtil::ExpectAtMostOneFieldSetInOneof(message);
 
@@ -1482,7 +1480,7 @@
   EXPECT_TRUE(message.has_foo_enum());
   TestUtil::ExpectAtMostOneFieldSetInOneof(message);
 
-  message.mutable_foo_message()->set_qux_int(234);
+  message.mutable_foo_message()->set_moo_int(234);
   EXPECT_TRUE(message.has_foo_message());
   TestUtil::ExpectAtMostOneFieldSetInOneof(message);
 
@@ -1505,11 +1503,11 @@
   ExpectEnumCasesWork(message);
   message.set_foo_string("foo");
   ExpectEnumCasesWork(message);
-  message.set_foo_bytes("qux");
+  message.set_foo_bytes("moo");
   ExpectEnumCasesWork(message);
   message.set_foo_enum(UNITTEST::TestOneof2::FOO);
   ExpectEnumCasesWork(message);
-  message.mutable_foo_message()->set_qux_int(234);
+  message.mutable_foo_message()->set_moo_int(234);
   ExpectEnumCasesWork(message);
   message.mutable_foogroup()->set_a(345);
   ExpectEnumCasesWork(message);
@@ -1559,15 +1557,15 @@
   EXPECT_FALSE(message.has_foo_string());
 
 
-  message.set_foo_string("qux", 3);
+  message.set_foo_string("moo", 3);
   EXPECT_TRUE(message.has_foo_string());
-  EXPECT_EQ(message.foo_string(), "qux");
+  EXPECT_EQ(message.foo_string(), "moo");
   message.clear_foo_string();
   EXPECT_FALSE(message.has_foo_string());
 
-  message.mutable_foo_string()->assign("quux");
+  message.mutable_foo_string()->assign("mooo");
   EXPECT_TRUE(message.has_foo_string());
-  EXPECT_EQ(message.foo_string(), "quux");
+  EXPECT_EQ(message.foo_string(), "mooo");
   message.clear_foo_string();
   EXPECT_FALSE(message.has_foo_string());
 
@@ -1643,11 +1641,11 @@
   // Unset field returns default instance
   EXPECT_EQ(&message.foo_message(),
             &UNITTEST::TestOneof2_NestedMessage::default_instance());
-  EXPECT_EQ(message.foo_message().qux_int(), 0);
+  EXPECT_EQ(message.foo_message().moo_int(), 0);
 
-  message.mutable_foo_message()->set_qux_int(234);
+  message.mutable_foo_message()->set_moo_int(234);
   EXPECT_TRUE(message.has_foo_message());
-  EXPECT_EQ(message.foo_message().qux_int(), 234);
+  EXPECT_EQ(message.foo_message().moo_int(), 234);
   message.clear_foo_message();
   EXPECT_FALSE(message.has_foo_message());
 }
@@ -1660,13 +1658,13 @@
   EXPECT_EQ(nullptr, message.release_foo_message());
   EXPECT_FALSE(message.has_foo_message());
 
-  message.mutable_foo_message()->set_qux_int(1);
+  message.mutable_foo_message()->set_moo_int(1);
   EXPECT_TRUE(message.has_foo_message());
   std::unique_ptr<UNITTEST::TestOneof2_NestedMessage> mes(
       message.release_foo_message());
   EXPECT_FALSE(message.has_foo_message());
   ASSERT_TRUE(mes != nullptr);
-  EXPECT_EQ(1, mes->qux_int());
+  EXPECT_EQ(1, mes->moo_int());
 
   EXPECT_EQ(nullptr, message.release_foo_message());
   EXPECT_FALSE(message.has_foo_message());
@@ -1678,7 +1676,7 @@
 
   EXPECT_FALSE(message.has_foo_message());
 
-  message.mutable_foo_message()->set_qux_int(1);
+  message.mutable_foo_message()->set_moo_int(1);
   EXPECT_TRUE(message.has_foo_message());
 
   message.set_allocated_foo_message(nullptr);
@@ -1686,14 +1684,14 @@
   EXPECT_EQ(&message.foo_message(),
             &UNITTEST::TestOneof2_NestedMessage::default_instance());
 
-  message.mutable_foo_message()->set_qux_int(1);
+  message.mutable_foo_message()->set_moo_int(1);
   UNITTEST::TestOneof2_NestedMessage* mes = message.release_foo_message();
   ASSERT_TRUE(mes != nullptr);
   EXPECT_FALSE(message.has_foo_message());
 
   message.set_allocated_foo_message(mes);
   EXPECT_TRUE(message.has_foo_message());
-  EXPECT_EQ(1, message.foo_message().qux_int());
+  EXPECT_EQ(1, message.foo_message().moo_int());
 }
 
 
@@ -1723,7 +1721,7 @@
   EXPECT_EQ(message.foo_enum(), 1);
 
   EXPECT_FALSE(message.has_foo_message());
-  EXPECT_EQ(message.foo_message().qux_int(), 0);
+  EXPECT_EQ(message.foo_message().moo_int(), 0);
 
   EXPECT_FALSE(message.has_foogroup());
   EXPECT_EQ(message.foogroup().a(), 0);
@@ -1767,14 +1765,14 @@
 
   message1.set_foo_string("FOO");
   EXPECT_TRUE(message1.has_foo_string());
-  message2.mutable_foo_message()->set_qux_int(1);
+  message2.mutable_foo_message()->set_moo_int(1);
   EXPECT_TRUE(message2.has_foo_message());
 
   message1.Swap(&message2);
   EXPECT_FALSE(message1.has_foo_string());
   EXPECT_FALSE(message2.has_foo_message());
   EXPECT_TRUE(message1.has_foo_message());
-  EXPECT_EQ(message1.foo_message().qux_int(), 1);
+  EXPECT_EQ(message1.foo_message().moo_int(), 1);
   EXPECT_TRUE(message2.has_foo_string());
   EXPECT_EQ(message2.foo_string(), "FOO");
 }
@@ -1805,16 +1803,16 @@
 
 TEST_F(OneofTest, CopyAssignmentOperator) {
   UNITTEST::TestOneof2 message1;
-  message1.mutable_foo_message()->set_qux_int(123);
+  message1.mutable_foo_message()->set_moo_int(123);
   EXPECT_TRUE(message1.has_foo_message());
 
   UNITTEST::TestOneof2 message2;
   message2 = message1;
-  EXPECT_EQ(message2.foo_message().qux_int(), 123);
+  EXPECT_EQ(message2.foo_message().moo_int(), 123);
 
   // Make sure that self-assignment does something sane.
   message2 = *&message2;  // Avoid -Wself-assign.
-  EXPECT_EQ(message2.foo_message().qux_int(), 123);
+  EXPECT_EQ(message2.foo_message().moo_int(), 123);
 }
 
 TEST_F(OneofTest, UpcastCopyFrom) {
@@ -1869,14 +1867,14 @@
   {
     UNITTEST::TestOneof2 message1, message2;
     std::string data;
-    message1.set_foo_bytes("qux");
+    message1.set_foo_bytes("moo");
     int size = message1.ByteSizeLong();
     data.resize(size);
     uint8_t* start = reinterpret_cast<uint8_t*>(::google::protobuf::string_as_array(&data));
     uint8_t* end = message1.SerializeWithCachedSizesToArray(start);
     EXPECT_EQ(size, end - start);
     EXPECT_TRUE(message2.ParseFromString(data));
-    EXPECT_EQ(message2.foo_bytes(), "qux");
+    EXPECT_EQ(message2.foo_bytes(), "moo");
   }
 
   // Enum
@@ -1897,14 +1895,14 @@
   {
     UNITTEST::TestOneof2 message1, message2;
     std::string data;
-    message1.mutable_foo_message()->set_qux_int(234);
+    message1.mutable_foo_message()->set_moo_int(234);
     int size = message1.ByteSizeLong();
     data.resize(size);
     uint8_t* start = reinterpret_cast<uint8_t*>(::google::protobuf::string_as_array(&data));
     uint8_t* end = message1.SerializeWithCachedSizesToArray(start);
     EXPECT_EQ(size, end - start);
     EXPECT_TRUE(message2.ParseFromString(data));
-    EXPECT_EQ(message2.foo_message().qux_int(), 234);
+    EXPECT_EQ(message2.foo_message().moo_int(), 234);
   }
 
   // Group
@@ -1976,7 +1974,7 @@
   {
     UNITTEST::TestOneof2 message1, message2;
     std::string data;
-    message1.set_foo_bytes("qux");
+    message1.set_foo_bytes("moo");
     int size = message1.ByteSizeLong();
     data.resize(size);
 
@@ -1990,7 +1988,7 @@
     }
 
     EXPECT_TRUE(message2.ParseFromString(data));
-    EXPECT_EQ(message2.foo_bytes(), "qux");
+    EXPECT_EQ(message2.foo_bytes(), "moo");
   }
 
   // Enum
@@ -2018,7 +2016,7 @@
   {
     UNITTEST::TestOneof2 message1, message2;
     std::string data;
-    message1.mutable_foo_message()->set_qux_int(234);
+    message1.mutable_foo_message()->set_moo_int(234);
     int size = message1.ByteSizeLong();
     data.resize(size);
 
@@ -2032,7 +2030,7 @@
     }
 
     EXPECT_TRUE(message2.ParseFromString(data));
-    EXPECT_EQ(message2.foo_message().qux_int(), 234);
+    EXPECT_EQ(message2.foo_message().moo_int(), 234);
   }
 
   // Group
@@ -2074,11 +2072,11 @@
   EXPECT_EQ(message2.foo_string(), "foo");
 
 
-  message1.set_foo_bytes("qux");
+  message1.set_foo_bytes("moo");
   message2.MergeFrom(message1);
   TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
   EXPECT_TRUE(message2.has_foo_bytes());
-  EXPECT_EQ(message2.foo_bytes(), "qux");
+  EXPECT_EQ(message2.foo_bytes(), "moo");
 
   message1.set_foo_enum(UNITTEST::TestOneof2::FOO);
   message2.MergeFrom(message1);
@@ -2086,11 +2084,11 @@
   EXPECT_TRUE(message2.has_foo_enum());
   EXPECT_EQ(message2.foo_enum(), UNITTEST::TestOneof2::FOO);
 
-  message1.mutable_foo_message()->set_qux_int(234);
+  message1.mutable_foo_message()->set_moo_int(234);
   message2.MergeFrom(message1);
   TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
   EXPECT_TRUE(message2.has_foo_message());
-  EXPECT_EQ(message2.foo_message().qux_int(), 234);
+  EXPECT_EQ(message2.foo_message().moo_int(), 234);
 
   message1.mutable_foogroup()->set_a(345);
   message2.MergeFrom(message1);
diff --git a/src/google/protobuf/compiler/csharp/csharp_enum_field.cc b/src/google/protobuf/compiler/csharp/csharp_enum_field.cc
index 186fa27..55fb60c 100644
--- a/src/google/protobuf/compiler/csharp/csharp_enum_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_enum_field.cc
@@ -109,7 +109,7 @@
   printer->Print(
     variables_,
     "$oneof_name$_ = input.ReadEnum();\n"
-    "$oneof_name$Case_ = $oneof_property_name$OneofCase.$property_name$;\n");
+    "$oneof_name$Case_ = $oneof_property_name$OneofCase.$oneof_case_name$;\n");
 }
 
 void EnumOneofFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
diff --git a/src/google/protobuf/compiler/csharp/csharp_field_base.cc b/src/google/protobuf/compiler/csharp/csharp_field_base.cc
index 146ca9e..17847e3 100644
--- a/src/google/protobuf/compiler/csharp/csharp_field_base.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_field_base.cc
@@ -130,8 +130,9 @@
   } else {
     (*variables)["has_property_check"] =
       oneof_name() + "Case_ == " + oneof_property_name() +
-      "OneofCase." + property_name();
+      "OneofCase." + oneof_case_name();
   }
+  (*variables)["oneof_case_name"] = oneof_case_name();
   (*variables)["oneof_property_name"] = oneof_property_name();
 }
 
@@ -187,6 +188,10 @@
   WriteGeneratedCodeAttributes(printer);
 }
 
+std::string FieldGeneratorBase::oneof_case_name() {
+  return GetOneofCaseName(descriptor_);
+}
+
 std::string FieldGeneratorBase::oneof_property_name() {
   return UnderscoresToCamelCase(descriptor_->containing_oneof()->name(), true);
 }
diff --git a/src/google/protobuf/compiler/csharp/csharp_field_base.h b/src/google/protobuf/compiler/csharp/csharp_field_base.h
index f875fa1..c7b7469 100644
--- a/src/google/protobuf/compiler/csharp/csharp_field_base.h
+++ b/src/google/protobuf/compiler/csharp/csharp_field_base.h
@@ -85,6 +85,7 @@
       std::map<std::string, std::string>* variables);
 
   std::string oneof_property_name();
+  std::string oneof_case_name(); 
   std::string oneof_name();
   std::string property_name();
   std::string name();
diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.cc b/src/google/protobuf/compiler/csharp/csharp_helpers.cc
index 32ef399..73ca868 100644
--- a/src/google/protobuf/compiler/csharp/csharp_helpers.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_helpers.cc
@@ -393,6 +393,13 @@
   return property_name;
 }
 
+std::string GetOneofCaseName(const FieldDescriptor* descriptor) {
+  // The name in a oneof case enum is the same as for the property, but as we always have a "None"
+  // value as well, we need to reserve that by appending an underscore.
+  std::string property_name = GetPropertyName(descriptor);
+  return property_name == "None" ? "None_" : property_name;
+}
+
 std::string GetOutputFile(const FileDescriptor* descriptor,
                           const std::string file_extension,
                           const bool generate_directories,
diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.h b/src/google/protobuf/compiler/csharp/csharp_helpers.h
index 619e7db..836bd5d 100644
--- a/src/google/protobuf/compiler/csharp/csharp_helpers.h
+++ b/src/google/protobuf/compiler/csharp/csharp_helpers.h
@@ -87,6 +87,8 @@
 
 std::string GetPropertyName(const FieldDescriptor* descriptor);
 
+std::string GetOneofCaseName(const FieldDescriptor* descriptor);
+
 int GetFixedSize(FieldDescriptor::Type type);
 
 std::string UnderscoresToCamelCase(const std::string& input,
diff --git a/src/google/protobuf/compiler/csharp/csharp_message.cc b/src/google/protobuf/compiler/csharp/csharp_message.cc
index 9dbce03..a119bdd 100644
--- a/src/google/protobuf/compiler/csharp/csharp_message.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_message.cc
@@ -238,8 +238,8 @@
     printer->Print("None = 0,\n");
     for (int j = 0; j < oneof->field_count(); j++) {
       const FieldDescriptor* field = oneof->field(j);
-      printer->Print("$field_property_name$ = $index$,\n",
-                     "field_property_name", GetPropertyName(field),
+      printer->Print("$oneof_case_name$ = $index$,\n",
+                     "oneof_case_name", GetOneofCaseName(field),
                      "index", StrCat(field->number()));
     }
     printer->Outdent();
@@ -403,10 +403,10 @@
     for (int j = 0; j < oneof->field_count(); j++) {
       const FieldDescriptor* field = oneof->field(j);
       std::unique_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
-      vars["field_property_name"] = GetPropertyName(field);
+      vars["oneof_case_name"] = GetOneofCaseName(field);
       printer->Print(
           vars,
-          "case $property_name$OneofCase.$field_property_name$:\n");
+          "case $property_name$OneofCase.$oneof_case_name$:\n");
       printer->Indent();
       generator->GenerateCloningCode(printer);
       printer->Print("break;\n");
@@ -635,10 +635,10 @@
     printer->Indent();
     for (int j = 0; j < oneof->field_count(); j++) {
       const FieldDescriptor* field = oneof->field(j);
-      vars["field_property_name"] = GetPropertyName(field);
+      vars["oneof_case_name"] = GetOneofCaseName(field);
       printer->Print(
         vars,
-        "case $property_name$OneofCase.$field_property_name$:\n");
+        "case $property_name$OneofCase.$oneof_case_name$:\n");
       printer->Indent();
       std::unique_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
       generator->GenerateMergingCode(printer);
diff --git a/src/google/protobuf/compiler/csharp/csharp_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_message_field.cc
index 034fbd9..487d01d 100644
--- a/src/google/protobuf/compiler/csharp/csharp_message_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_message_field.cc
@@ -225,7 +225,7 @@
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n"
     "  set {\n"
     "    $oneof_name$_ = value;\n"
-    "    $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n"
+    "    $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$oneof_case_name$;\n"
     "  }\n"
     "}\n");
   if (SupportsPresenceApi(descriptor_)) {
@@ -236,7 +236,7 @@
     printer->Print(
       variables_,
       "$access_level$ bool Has$property_name$ {\n"
-      "  get { return $oneof_name$Case_ == $oneof_property_name$OneofCase.$property_name$; }\n"
+      "  get { return $oneof_name$Case_ == $oneof_property_name$OneofCase.$oneof_case_name$; }\n"
       "}\n");
     printer->Print(
       variables_,
diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
index 9df1dd6..e7d5116 100644
--- a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
@@ -296,7 +296,7 @@
   }
   printer->Print(
     variables_,
-    "    $oneof_name$Case_ = $oneof_property_name$OneofCase.$property_name$;\n"
+    "    $oneof_name$Case_ = $oneof_property_name$OneofCase.$oneof_case_name$;\n"
     "  }\n"
     "}\n");
   if (SupportsPresenceApi(descriptor_)) {
@@ -307,7 +307,7 @@
     printer->Print(
       variables_,
       "$access_level$ bool Has$property_name$ {\n"
-      "  get { return $oneof_name$Case_ == $oneof_property_name$OneofCase.$property_name$; }\n"
+      "  get { return $oneof_name$Case_ == $oneof_property_name$OneofCase.$oneof_case_name$; }\n"
       "}\n");
     printer->Print(
       variables_,
diff --git a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc
index 578f54b..e638dd8 100644
--- a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc
@@ -233,7 +233,7 @@
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : ($type_name$) null; }\n"
     "  set {\n"
     "    $oneof_name$_ = value;\n"
-    "    $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n"
+    "    $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$oneof_case_name$;\n"
     "  }\n"
     "}\n");
   if (SupportsPresenceApi(descriptor_)) {
@@ -244,7 +244,7 @@
     printer->Print(
       variables_,
       "$access_level$ bool Has$property_name$ {\n"
-      "  get { return $oneof_name$Case_ == $oneof_property_name$OneofCase.$property_name$; }\n"
+      "  get { return $oneof_name$Case_ == $oneof_property_name$OneofCase.$oneof_case_name$; }\n"
       "}\n");
     printer->Print(
       variables_,
diff --git a/src/google/protobuf/compiler/mock_code_generator.cc b/src/google/protobuf/compiler/mock_code_generator.cc
index 53f6118..4d04511 100644
--- a/src/google/protobuf/compiler/mock_code_generator.cc
+++ b/src/google/protobuf/compiler/mock_code_generator.cc
@@ -47,13 +47,13 @@
 #include <google/protobuf/testing/file.h>
 #include <google/protobuf/testing/file.h>
 #include <google/protobuf/compiler/plugin.pb.h>
-#include <google/protobuf/io/printer.h>
-#include <google/protobuf/io/zero_copy_stream.h>
 #include <google/protobuf/descriptor.pb.h>
-#include <google/protobuf/descriptor.h>
-#include <google/protobuf/text_format.h>
 #include <gtest/gtest.h>
 #include <google/protobuf/stubs/substitute.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/text_format.h>
 
 #ifdef major
 #undef major
diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc
index 86d8248..5bd37d1 100644
--- a/src/google/protobuf/compiler/parser.cc
+++ b/src/google/protobuf/compiler/parser.cc
@@ -64,32 +64,33 @@
 
 typedef std::unordered_map<std::string, FieldDescriptorProto::Type> TypeNameMap;
 
-TypeNameMap MakeTypeNameTable() {
-  TypeNameMap result;
+const TypeNameMap& GetTypeNameTable() {
+  static auto* table = new auto([]() {
+    TypeNameMap result;
 
-  result["double"] = FieldDescriptorProto::TYPE_DOUBLE;
-  result["float"] = FieldDescriptorProto::TYPE_FLOAT;
-  result["uint64"] = FieldDescriptorProto::TYPE_UINT64;
-  result["fixed64"] = FieldDescriptorProto::TYPE_FIXED64;
-  result["fixed32"] = FieldDescriptorProto::TYPE_FIXED32;
-  result["bool"] = FieldDescriptorProto::TYPE_BOOL;
-  result["string"] = FieldDescriptorProto::TYPE_STRING;
-  result["group"] = FieldDescriptorProto::TYPE_GROUP;
+    result["double"] = FieldDescriptorProto::TYPE_DOUBLE;
+    result["float"] = FieldDescriptorProto::TYPE_FLOAT;
+    result["uint64"] = FieldDescriptorProto::TYPE_UINT64;
+    result["fixed64"] = FieldDescriptorProto::TYPE_FIXED64;
+    result["fixed32"] = FieldDescriptorProto::TYPE_FIXED32;
+    result["bool"] = FieldDescriptorProto::TYPE_BOOL;
+    result["string"] = FieldDescriptorProto::TYPE_STRING;
+    result["group"] = FieldDescriptorProto::TYPE_GROUP;
 
-  result["bytes"] = FieldDescriptorProto::TYPE_BYTES;
-  result["uint32"] = FieldDescriptorProto::TYPE_UINT32;
-  result["sfixed32"] = FieldDescriptorProto::TYPE_SFIXED32;
-  result["sfixed64"] = FieldDescriptorProto::TYPE_SFIXED64;
-  result["int32"] = FieldDescriptorProto::TYPE_INT32;
-  result["int64"] = FieldDescriptorProto::TYPE_INT64;
-  result["sint32"] = FieldDescriptorProto::TYPE_SINT32;
-  result["sint64"] = FieldDescriptorProto::TYPE_SINT64;
+    result["bytes"] = FieldDescriptorProto::TYPE_BYTES;
+    result["uint32"] = FieldDescriptorProto::TYPE_UINT32;
+    result["sfixed32"] = FieldDescriptorProto::TYPE_SFIXED32;
+    result["sfixed64"] = FieldDescriptorProto::TYPE_SFIXED64;
+    result["int32"] = FieldDescriptorProto::TYPE_INT32;
+    result["int64"] = FieldDescriptorProto::TYPE_INT64;
+    result["sint32"] = FieldDescriptorProto::TYPE_SINT32;
+    result["sint64"] = FieldDescriptorProto::TYPE_SINT64;
 
-  return result;
+    return result;
+  }());
+  return *table;
 }
 
-const TypeNameMap kTypeNames = MakeTypeNameTable();
-
 // Camel-case the field name and append "Entry" for generated map entry name.
 // e.g. map<KeyType, ValueType> foo_map => FooMapEntry
 std::string MapEntryName(const std::string& field_name) {
@@ -2270,8 +2271,9 @@
 
 bool Parser::ParseType(FieldDescriptorProto::Type* type,
                        std::string* type_name) {
-  TypeNameMap::const_iterator iter = kTypeNames.find(input_->current().text);
-  if (iter != kTypeNames.end()) {
+  const auto& type_names_table = GetTypeNameTable();
+  auto iter = type_names_table.find(input_->current().text);
+  if (iter != type_names_table.end()) {
     *type = iter->second;
     input_->Next();
   } else {
@@ -2283,8 +2285,9 @@
 bool Parser::ParseUserDefinedType(std::string* type_name) {
   type_name->clear();
 
-  TypeNameMap::const_iterator iter = kTypeNames.find(input_->current().text);
-  if (iter != kTypeNames.end()) {
+  const auto& type_names_table = GetTypeNameTable();
+  auto iter = type_names_table.find(input_->current().text);
+  if (iter != type_names_table.end()) {
     // Note:  The only place enum types are allowed is for field types, but
     //   if we are parsing a field type then we would not get here because
     //   primitives are allowed there as well.  So this error message doesn't
diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h
index ddb27b6..d4eb763 100644
--- a/src/google/protobuf/compiler/parser.h
+++ b/src/google/protobuf/compiler/parser.h
@@ -484,7 +484,7 @@
   // Parses a single part of a multipart option name. A multipart name consists
   // of names separated by dots. Each name is either an identifier or a series
   // of identifiers separated by dots and enclosed in parentheses. E.g.,
-  // "foo.(bar.baz).qux".
+  // "foo.(bar.baz).moo".
   bool ParseOptionNamePart(UninterpretedOption* uninterpreted_option,
                            const LocationRecorder& part_location,
                            const FileDescriptorProto* containing_file);
diff --git a/src/google/protobuf/compiler/plugin.pb.cc b/src/google/protobuf/compiler/plugin.pb.cc
index f29320f..0a25b0c 100644
--- a/src/google/protobuf/compiler/plugin.pb.cc
+++ b/src/google/protobuf/compiler/plugin.pb.cc
@@ -484,7 +484,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Version::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Version::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Version::GetClassData() const { return &_class_data_; }
@@ -844,7 +844,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CodeGeneratorRequest::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     CodeGeneratorRequest::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CodeGeneratorRequest::GetClassData() const { return &_class_data_; }
@@ -1228,7 +1228,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CodeGeneratorResponse_File::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     CodeGeneratorResponse_File::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CodeGeneratorResponse_File::GetClassData() const { return &_class_data_; }
@@ -1533,7 +1533,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CodeGeneratorResponse::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     CodeGeneratorResponse::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CodeGeneratorResponse::GetClassData() const { return &_class_data_; }
diff --git a/src/google/protobuf/compiler/python/pyi_generator.cc b/src/google/protobuf/compiler/python/pyi_generator.cc
index e356bb5..1ccc9a2 100644
--- a/src/google/protobuf/compiler/python/pyi_generator.cc
+++ b/src/google/protobuf/compiler/python/pyi_generator.cc
@@ -555,7 +555,7 @@
 
 void PyiGenerator::PrintMessages(
     const std::map<std::string, std::string>& import_map) const {
-  // Order the descriptors by name to have same output with proto_to_pyi.py
+  // Deterministically order the descriptors.
   std::vector<const Descriptor*> messages;
   messages.reserve(file_->message_type_count());
   for (int i = 0; i < file_->message_type_count(); ++i) {
@@ -593,9 +593,6 @@
   MutexLock lock(&mutex_);
   // Calculate file name.
   file_ = file;
-  // proto_to_pyi.py may set the output file name directly. To replace
-  // proto_to_pyi.py in google3, protoc also accept --pyi_out to set
-  // the output file name.
   std::string filename =
       parameter.empty() ? GetFileName(file, ".pyi") : parameter;
 
diff --git a/src/google/protobuf/compiler/test_plugin.cc b/src/google/protobuf/compiler/test_plugin.cc
index 6b56170..2556078 100644
--- a/src/google/protobuf/compiler/test_plugin.cc
+++ b/src/google/protobuf/compiler/test_plugin.cc
@@ -34,7 +34,9 @@
 // command_line_interface_unittest.
 
 #include <stdlib.h>
+
 #include <string>
+
 #include <google/protobuf/compiler/mock_code_generator.h>
 #include <google/protobuf/compiler/plugin.h>
 
diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc
index 0524fd4..205cf86 100644
--- a/src/google/protobuf/descriptor.cc
+++ b/src/google/protobuf/descriptor.cc
@@ -1101,8 +1101,9 @@
 std::set<std::string>* NewAllowedProto3Extendee() {
   auto allowed_proto3_extendees = new std::set<std::string>;
   const char* kOptionNames[] = {
-      "FileOptions",      "MessageOptions", "FieldOptions",  "EnumOptions",
-      "EnumValueOptions", "ServiceOptions", "MethodOptions", "OneofOptions"};
+      "FileOptions",   "MessageOptions",   "FieldOptions",
+      "EnumOptions",   "EnumValueOptions", "ServiceOptions",
+      "MethodOptions", "OneofOptions",     "ExtensionRangeOptions"};
   for (const char* option_name : kOptionNames) {
     // descriptor.proto has a different package name in opensource. We allow
     // both so the opensource protocol compiler can also compile internal
@@ -3772,9 +3773,9 @@
 
   // Like FindSymbol(), but looks up the name relative to some other symbol
   // name.  This first searches siblings of relative_to, then siblings of its
-  // parents, etc.  For example, LookupSymbol("foo.bar", "baz.qux.corge") makes
+  // parents, etc.  For example, LookupSymbol("foo.bar", "baz.moo.corge") makes
   // the following calls, returning the first non-null result:
-  // FindSymbol("baz.qux.foo.bar"), FindSymbol("baz.foo.bar"),
+  // FindSymbol("baz.moo.foo.bar"), FindSymbol("baz.foo.bar"),
   // FindSymbol("foo.bar").  If AllowUnknownDependencies() has been called
   // on the DescriptorPool, this will generate a placeholder type if
   // the name is not found (unless the name itself is malformed).  The
diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc
index 7f4e282..535a439 100644
--- a/src/google/protobuf/descriptor.pb.cc
+++ b/src/google/protobuf/descriptor.pb.cc
@@ -1463,7 +1463,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FileDescriptorSet::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     FileDescriptorSet::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FileDescriptorSet::GetClassData() const { return &_class_data_; }
@@ -2101,7 +2101,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FileDescriptorProto::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     FileDescriptorProto::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FileDescriptorProto::GetClassData() const { return &_class_data_; }
@@ -2428,7 +2428,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData DescriptorProto_ExtensionRange::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     DescriptorProto_ExtensionRange::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*DescriptorProto_ExtensionRange::GetClassData() const { return &_class_data_; }
@@ -2674,7 +2674,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData DescriptorProto_ReservedRange::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     DescriptorProto_ReservedRange::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*DescriptorProto_ReservedRange::GetClassData() const { return &_class_data_; }
@@ -3211,7 +3211,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData DescriptorProto::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     DescriptorProto::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*DescriptorProto::GetClassData() const { return &_class_data_; }
@@ -3461,7 +3461,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ExtensionRangeOptions::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     ExtensionRangeOptions::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ExtensionRangeOptions::GetClassData() const { return &_class_data_; }
@@ -4080,7 +4080,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FieldDescriptorProto::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     FieldDescriptorProto::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FieldDescriptorProto::GetClassData() const { return &_class_data_; }
@@ -4411,7 +4411,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData OneofDescriptorProto::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     OneofDescriptorProto::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*OneofDescriptorProto::GetClassData() const { return &_class_data_; }
@@ -4654,7 +4654,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData EnumDescriptorProto_EnumReservedRange::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     EnumDescriptorProto_EnumReservedRange::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*EnumDescriptorProto_EnumReservedRange::GetClassData() const { return &_class_data_; }
@@ -5031,7 +5031,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData EnumDescriptorProto::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     EnumDescriptorProto::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*EnumDescriptorProto::GetClassData() const { return &_class_data_; }
@@ -5343,7 +5343,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData EnumValueDescriptorProto::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     EnumValueDescriptorProto::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*EnumValueDescriptorProto::GetClassData() const { return &_class_data_; }
@@ -5661,7 +5661,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ServiceDescriptorProto::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     ServiceDescriptorProto::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ServiceDescriptorProto::GetClassData() const { return &_class_data_; }
@@ -6098,7 +6098,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData MethodDescriptorProto::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     MethodDescriptorProto::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*MethodDescriptorProto::GetClassData() const { return &_class_data_; }
@@ -7136,7 +7136,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FileOptions::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     FileOptions::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FileOptions::GetClassData() const { return &_class_data_; }
@@ -7584,7 +7584,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData MessageOptions::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     MessageOptions::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*MessageOptions::GetClassData() const { return &_class_data_; }
@@ -8025,7 +8025,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FieldOptions::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     FieldOptions::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FieldOptions::GetClassData() const { return &_class_data_; }
@@ -8269,7 +8269,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData OneofOptions::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     OneofOptions::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*OneofOptions::GetClassData() const { return &_class_data_; }
@@ -8547,7 +8547,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData EnumOptions::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     EnumOptions::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*EnumOptions::GetClassData() const { return &_class_data_; }
@@ -8811,7 +8811,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData EnumValueOptions::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     EnumValueOptions::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*EnumValueOptions::GetClassData() const { return &_class_data_; }
@@ -9063,7 +9063,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ServiceOptions::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     ServiceOptions::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ServiceOptions::GetClassData() const { return &_class_data_; }
@@ -9355,7 +9355,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData MethodOptions::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     MethodOptions::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*MethodOptions::GetClassData() const { return &_class_data_; }
@@ -9642,7 +9642,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData UninterpretedOption_NamePart::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     UninterpretedOption_NamePart::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*UninterpretedOption_NamePart::GetClassData() const { return &_class_data_; }
@@ -10086,7 +10086,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData UninterpretedOption::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     UninterpretedOption::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*UninterpretedOption::GetClassData() const { return &_class_data_; }
@@ -10519,7 +10519,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData SourceCodeInfo_Location::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     SourceCodeInfo_Location::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*SourceCodeInfo_Location::GetClassData() const { return &_class_data_; }
@@ -10731,7 +10731,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData SourceCodeInfo::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     SourceCodeInfo::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*SourceCodeInfo::GetClassData() const { return &_class_data_; }
@@ -11046,7 +11046,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData GeneratedCodeInfo_Annotation::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     GeneratedCodeInfo_Annotation::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GeneratedCodeInfo_Annotation::GetClassData() const { return &_class_data_; }
@@ -11260,7 +11260,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData GeneratedCodeInfo::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     GeneratedCodeInfo::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GeneratedCodeInfo::GetClassData() const { return &_class_data_; }
diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto
index 49ec653..f8eb216 100644
--- a/src/google/protobuf/descriptor.proto
+++ b/src/google/protobuf/descriptor.proto
@@ -740,8 +740,8 @@
   // The name of the uninterpreted option.  Each string represents a segment in
   // a dot-separated name.  is_extension is true iff a segment represents an
   // extension (denoted with parentheses in options specs in .proto files).
-  // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
-  // "foo.(bar.baz).qux".
+  // E.g.,{ ["foo", false], ["bar.baz", true], ["moo", false] } represents
+  // "foo.(bar.baz).moo".
   message NamePart {
     required string name_part = 1;
     required bool is_extension = 2;
@@ -868,13 +868,13 @@
     //   // Comment attached to baz.
     //   // Another line attached to baz.
     //
-    //   // Comment attached to qux.
+    //   // Comment attached to moo.
     //   //
-    //   // Another line attached to qux.
-    //   optional double qux = 4;
+    //   // Another line attached to moo.
+    //   optional double moo = 4;
     //
     //   // Detached comment for corge. This is not leading or trailing comments
-    //   // to qux or corge because there are blank lines separating it from
+    //   // to moo or corge because there are blank lines separating it from
     //   // both.
     //
     //   // Detached comment for corge paragraph 2.
diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc
index 2f57cd7..5f2a650 100644
--- a/src/google/protobuf/descriptor_unittest.cc
+++ b/src/google/protobuf/descriptor_unittest.cc
@@ -628,7 +628,7 @@
     //     required string      foo = 1;
     //     optional TestEnum    bar = 6;
     //     repeated TestForeign baz = 500000000;
-    //     optional group       qux = 15 {}
+    //     optional group       moo = 15 {}
     //   }
     //
     //   // in "bar.proto"
@@ -636,7 +636,7 @@
     //   message TestMessage2 {
     //     required string foo = 1;
     //     required string bar = 2;
-    //     required string quux = 6;
+    //     required string mooo = 6;
     //   }
     //
     //   // in "map.proto"
@@ -654,7 +654,7 @@
     //     optional int32 field_name6 = 6 [json_name = "@type"];
     //   }
     //
-    // We cheat and use TestForeign as the type for qux rather than create
+    // We cheat and use TestForeign as the type for moo rather than create
     // an actual nested type.
     //
     // Since all primitive types (including string) use the same building
@@ -678,7 +678,7 @@
     AddField(message, "baz", 500000000, FieldDescriptorProto::LABEL_REPEATED,
              FieldDescriptorProto::TYPE_MESSAGE)
         ->set_type_name("TestForeign");
-    AddField(message, "qux", 15, FieldDescriptorProto::LABEL_OPTIONAL,
+    AddField(message, "moo", 15, FieldDescriptorProto::LABEL_OPTIONAL,
              FieldDescriptorProto::TYPE_GROUP)
         ->set_type_name("TestForeign");
 
@@ -691,7 +691,7 @@
              FieldDescriptorProto::TYPE_STRING);
     AddField(message2, "bar", 2, FieldDescriptorProto::LABEL_REQUIRED,
              FieldDescriptorProto::TYPE_STRING);
-    AddField(message2, "quux", 6, FieldDescriptorProto::LABEL_REQUIRED,
+    AddField(message2, "mooo", 6, FieldDescriptorProto::LABEL_REQUIRED,
              FieldDescriptorProto::TYPE_STRING);
 
     FileDescriptorProto map_file;
@@ -754,7 +754,7 @@
     foo_ = message_->field(0);
     bar_ = message_->field(1);
     baz_ = message_->field(2);
-    qux_ = message_->field(3);
+    moo_ = message_->field(3);
 
     ASSERT_EQ(1, bar_file_->message_type_count());
     message2_ = bar_file_->message_type(0);
@@ -762,7 +762,7 @@
     ASSERT_EQ(3, message2_->field_count());
     foo2_ = message2_->field(0);
     bar2_ = message2_->field(1);
-    quux2_ = message2_->field(2);
+    mooo2_ = message2_->field(2);
 
     ASSERT_EQ(1, map_file_->message_type_count());
     message3_ = map_file_->message_type(0);
@@ -801,11 +801,11 @@
   const FieldDescriptor* foo_;
   const FieldDescriptor* bar_;
   const FieldDescriptor* baz_;
-  const FieldDescriptor* qux_;
+  const FieldDescriptor* moo_;
 
   const FieldDescriptor* foo2_;
   const FieldDescriptor* bar2_;
-  const FieldDescriptor* quux2_;
+  const FieldDescriptor* mooo2_;
 
   const FieldDescriptor* map_;
 };
@@ -886,7 +886,7 @@
   EXPECT_EQ(foo_, message_->field(0));
   EXPECT_EQ(bar_, message_->field(1));
   EXPECT_EQ(baz_, message_->field(2));
-  EXPECT_EQ(qux_, message_->field(3));
+  EXPECT_EQ(moo_, message_->field(3));
 }
 
 TEST_F(DescriptorTest, FindFieldByName) {
@@ -898,28 +898,28 @@
   EXPECT_EQ(foo_, message_->FindFieldByName("foo"));
   EXPECT_EQ(bar_, message_->FindFieldByName("bar"));
   EXPECT_EQ(baz_, message_->FindFieldByName("baz"));
-  EXPECT_EQ(qux_, message_->FindFieldByName("qux"));
+  EXPECT_EQ(moo_, message_->FindFieldByName("moo"));
   EXPECT_TRUE(message_->FindFieldByName("no_such_field") == nullptr);
-  EXPECT_TRUE(message_->FindFieldByName("quux") == nullptr);
+  EXPECT_TRUE(message_->FindFieldByName("mooo") == nullptr);
 
   EXPECT_EQ(foo2_, message2_->FindFieldByName("foo"));
   EXPECT_EQ(bar2_, message2_->FindFieldByName("bar"));
-  EXPECT_EQ(quux2_, message2_->FindFieldByName("quux"));
+  EXPECT_EQ(mooo2_, message2_->FindFieldByName("mooo"));
   EXPECT_TRUE(message2_->FindFieldByName("baz") == nullptr);
-  EXPECT_TRUE(message2_->FindFieldByName("qux") == nullptr);
+  EXPECT_TRUE(message2_->FindFieldByName("moo") == nullptr);
 }
 
 TEST_F(DescriptorTest, FindFieldByNumber) {
   EXPECT_EQ(foo_, message_->FindFieldByNumber(1));
   EXPECT_EQ(bar_, message_->FindFieldByNumber(6));
   EXPECT_EQ(baz_, message_->FindFieldByNumber(500000000));
-  EXPECT_EQ(qux_, message_->FindFieldByNumber(15));
+  EXPECT_EQ(moo_, message_->FindFieldByNumber(15));
   EXPECT_TRUE(message_->FindFieldByNumber(837592) == nullptr);
   EXPECT_TRUE(message_->FindFieldByNumber(2) == nullptr);
 
   EXPECT_EQ(foo2_, message2_->FindFieldByNumber(1));
   EXPECT_EQ(bar2_, message2_->FindFieldByNumber(2));
-  EXPECT_EQ(quux2_, message2_->FindFieldByNumber(6));
+  EXPECT_EQ(mooo2_, message2_->FindFieldByNumber(6));
   EXPECT_TRUE(message2_->FindFieldByNumber(15) == nullptr);
   EXPECT_TRUE(message2_->FindFieldByNumber(500000000) == nullptr);
 }
@@ -928,32 +928,32 @@
   EXPECT_EQ("foo", foo_->name());
   EXPECT_EQ("bar", bar_->name());
   EXPECT_EQ("baz", baz_->name());
-  EXPECT_EQ("qux", qux_->name());
+  EXPECT_EQ("moo", moo_->name());
 }
 
 TEST_F(DescriptorTest, FieldFullName) {
   EXPECT_EQ("TestMessage.foo", foo_->full_name());
   EXPECT_EQ("TestMessage.bar", bar_->full_name());
   EXPECT_EQ("TestMessage.baz", baz_->full_name());
-  EXPECT_EQ("TestMessage.qux", qux_->full_name());
+  EXPECT_EQ("TestMessage.moo", moo_->full_name());
 
   EXPECT_EQ("corge.grault.TestMessage2.foo", foo2_->full_name());
   EXPECT_EQ("corge.grault.TestMessage2.bar", bar2_->full_name());
-  EXPECT_EQ("corge.grault.TestMessage2.quux", quux2_->full_name());
+  EXPECT_EQ("corge.grault.TestMessage2.mooo", mooo2_->full_name());
 }
 
 TEST_F(DescriptorTest, PrintableNameIsFullNameForNonExtensionFields) {
   EXPECT_EQ("TestMessage.foo", foo_->PrintableNameForExtension());
   EXPECT_EQ("TestMessage.bar", bar_->PrintableNameForExtension());
   EXPECT_EQ("TestMessage.baz", baz_->PrintableNameForExtension());
-  EXPECT_EQ("TestMessage.qux", qux_->PrintableNameForExtension());
+  EXPECT_EQ("TestMessage.moo", moo_->PrintableNameForExtension());
 
   EXPECT_EQ("corge.grault.TestMessage2.foo",
             foo2_->PrintableNameForExtension());
   EXPECT_EQ("corge.grault.TestMessage2.bar",
             bar2_->PrintableNameForExtension());
-  EXPECT_EQ("corge.grault.TestMessage2.quux",
-            quux2_->PrintableNameForExtension());
+  EXPECT_EQ("corge.grault.TestMessage2.mooo",
+            mooo2_->PrintableNameForExtension());
 }
 
 TEST_F(DescriptorTest, PrintableNameIsFullNameForNonMessageSetExtension) {
@@ -1016,39 +1016,39 @@
   EXPECT_EQ(foo_file_, foo_->file());
   EXPECT_EQ(foo_file_, bar_->file());
   EXPECT_EQ(foo_file_, baz_->file());
-  EXPECT_EQ(foo_file_, qux_->file());
+  EXPECT_EQ(foo_file_, moo_->file());
 
   EXPECT_EQ(bar_file_, foo2_->file());
   EXPECT_EQ(bar_file_, bar2_->file());
-  EXPECT_EQ(bar_file_, quux2_->file());
+  EXPECT_EQ(bar_file_, mooo2_->file());
 }
 
 TEST_F(DescriptorTest, FieldIndex) {
   EXPECT_EQ(0, foo_->index());
   EXPECT_EQ(1, bar_->index());
   EXPECT_EQ(2, baz_->index());
-  EXPECT_EQ(3, qux_->index());
+  EXPECT_EQ(3, moo_->index());
 }
 
 TEST_F(DescriptorTest, FieldNumber) {
   EXPECT_EQ(1, foo_->number());
   EXPECT_EQ(6, bar_->number());
   EXPECT_EQ(500000000, baz_->number());
-  EXPECT_EQ(15, qux_->number());
+  EXPECT_EQ(15, moo_->number());
 }
 
 TEST_F(DescriptorTest, FieldType) {
   EXPECT_EQ(FieldDescriptor::TYPE_STRING, foo_->type());
   EXPECT_EQ(FieldDescriptor::TYPE_ENUM, bar_->type());
   EXPECT_EQ(FieldDescriptor::TYPE_MESSAGE, baz_->type());
-  EXPECT_EQ(FieldDescriptor::TYPE_GROUP, qux_->type());
+  EXPECT_EQ(FieldDescriptor::TYPE_GROUP, moo_->type());
 }
 
 TEST_F(DescriptorTest, FieldLabel) {
   EXPECT_EQ(FieldDescriptor::LABEL_REQUIRED, foo_->label());
   EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, bar_->label());
   EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, baz_->label());
-  EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, qux_->label());
+  EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, moo_->label());
 
   EXPECT_TRUE(foo_->is_required());
   EXPECT_FALSE(foo_->is_optional());
@@ -1089,18 +1089,18 @@
   EXPECT_FALSE(foo_->has_default_value());
   EXPECT_FALSE(bar_->has_default_value());
   EXPECT_FALSE(baz_->has_default_value());
-  EXPECT_FALSE(qux_->has_default_value());
+  EXPECT_FALSE(moo_->has_default_value());
 }
 
 TEST_F(DescriptorTest, FieldContainingType) {
   EXPECT_EQ(message_, foo_->containing_type());
   EXPECT_EQ(message_, bar_->containing_type());
   EXPECT_EQ(message_, baz_->containing_type());
-  EXPECT_EQ(message_, qux_->containing_type());
+  EXPECT_EQ(message_, moo_->containing_type());
 
   EXPECT_EQ(message2_, foo2_->containing_type());
   EXPECT_EQ(message2_, bar2_->containing_type());
-  EXPECT_EQ(message2_, quux2_->containing_type());
+  EXPECT_EQ(message2_, mooo2_->containing_type());
 }
 
 TEST_F(DescriptorTest, FieldMessageType) {
@@ -1108,13 +1108,13 @@
   EXPECT_TRUE(bar_->message_type() == nullptr);
 
   EXPECT_EQ(foreign_, baz_->message_type());
-  EXPECT_EQ(foreign_, qux_->message_type());
+  EXPECT_EQ(foreign_, moo_->message_type());
 }
 
 TEST_F(DescriptorTest, FieldEnumType) {
   EXPECT_TRUE(foo_->enum_type() == nullptr);
   EXPECT_TRUE(baz_->enum_type() == nullptr);
-  EXPECT_TRUE(qux_->enum_type() == nullptr);
+  EXPECT_TRUE(moo_->enum_type() == nullptr);
 
   EXPECT_EQ(enum_, bar_->enum_type());
 }
@@ -1708,7 +1708,7 @@
     //     message Foo {}
     //     message Bar {}
     //     enum Baz { A = 1; }
-    //     enum Qux { B = 1; }
+    //     enum Moo { B = 1; }
     //   }
     //
     //   // in "bar.proto"
@@ -1716,8 +1716,8 @@
     //   message TestMessage2 {
     //     message Foo {}
     //     message Baz {}
-    //     enum Qux  { A = 1; }
-    //     enum Quux { C = 1; }
+    //     enum Moo  { A = 1; }
+    //     enum Mooo { C = 1; }
     //   }
     //
     // TestMessage2 is primarily here to test FindNestedTypeByName and friends.
@@ -1735,8 +1735,8 @@
     AddNestedMessage(message, "Bar");
     EnumDescriptorProto* baz = AddNestedEnum(message, "Baz");
     AddEnumValue(baz, "A", 1);
-    EnumDescriptorProto* qux = AddNestedEnum(message, "Qux");
-    AddEnumValue(qux, "B", 1);
+    EnumDescriptorProto* moo = AddNestedEnum(message, "Moo");
+    AddEnumValue(moo, "B", 1);
 
     FileDescriptorProto bar_file;
     bar_file.set_name("bar.proto");
@@ -1745,10 +1745,10 @@
     DescriptorProto* message2 = AddMessage(&bar_file, "TestMessage2");
     AddNestedMessage(message2, "Foo");
     AddNestedMessage(message2, "Baz");
-    EnumDescriptorProto* qux2 = AddNestedEnum(message2, "Qux");
-    AddEnumValue(qux2, "A", 1);
-    EnumDescriptorProto* quux2 = AddNestedEnum(message2, "Quux");
-    AddEnumValue(quux2, "C", 1);
+    EnumDescriptorProto* moo2 = AddNestedEnum(message2, "Moo");
+    AddEnumValue(moo2, "A", 1);
+    EnumDescriptorProto* mooo2 = AddNestedEnum(message2, "Mooo");
+    AddEnumValue(mooo2, "C", 1);
 
     // Build the descriptors and get the pointers.
     foo_file_ = pool_.BuildFile(foo_file);
@@ -1766,12 +1766,12 @@
 
     ASSERT_EQ(2, message_->enum_type_count());
     baz_ = message_->enum_type(0);
-    qux_ = message_->enum_type(1);
+    moo_ = message_->enum_type(1);
 
     ASSERT_EQ(1, baz_->value_count());
     a_ = baz_->value(0);
-    ASSERT_EQ(1, qux_->value_count());
-    b_ = qux_->value(0);
+    ASSERT_EQ(1, moo_->value_count());
+    b_ = moo_->value(0);
 
     ASSERT_EQ(1, bar_file_->message_type_count());
     message2_ = bar_file_->message_type(0);
@@ -1781,13 +1781,13 @@
     baz2_ = message2_->nested_type(1);
 
     ASSERT_EQ(2, message2_->enum_type_count());
-    qux2_ = message2_->enum_type(0);
-    quux2_ = message2_->enum_type(1);
+    moo2_ = message2_->enum_type(0);
+    mooo2_ = message2_->enum_type(1);
 
-    ASSERT_EQ(1, qux2_->value_count());
-    a2_ = qux2_->value(0);
-    ASSERT_EQ(1, quux2_->value_count());
-    c2_ = quux2_->value(0);
+    ASSERT_EQ(1, moo2_->value_count());
+    a2_ = moo2_->value(0);
+    ASSERT_EQ(1, mooo2_->value_count());
+    c2_ = mooo2_->value(0);
   }
 
   DescriptorPool pool_;
@@ -1801,14 +1801,14 @@
   const Descriptor* foo_;
   const Descriptor* bar_;
   const EnumDescriptor* baz_;
-  const EnumDescriptor* qux_;
+  const EnumDescriptor* moo_;
   const EnumValueDescriptor* a_;
   const EnumValueDescriptor* b_;
 
   const Descriptor* foo2_;
   const Descriptor* baz2_;
-  const EnumDescriptor* qux2_;
-  const EnumDescriptor* quux2_;
+  const EnumDescriptor* moo2_;
+  const EnumDescriptor* mooo2_;
   const EnumValueDescriptor* a2_;
   const EnumValueDescriptor* c2_;
 };
@@ -1840,9 +1840,9 @@
 
 TEST_F(NestedDescriptorTest, FindFieldByNameDoesntFindNestedTypes) {
   EXPECT_TRUE(message_->FindFieldByName("Foo") == nullptr);
-  EXPECT_TRUE(message_->FindFieldByName("Qux") == nullptr);
+  EXPECT_TRUE(message_->FindFieldByName("Moo") == nullptr);
   EXPECT_TRUE(message_->FindExtensionByName("Foo") == nullptr);
-  EXPECT_TRUE(message_->FindExtensionByName("Qux") == nullptr);
+  EXPECT_TRUE(message_->FindExtensionByName("Moo") == nullptr);
 }
 
 TEST_F(NestedDescriptorTest, FindNestedTypeByName) {
@@ -1855,26 +1855,26 @@
   EXPECT_TRUE(message_->FindNestedTypeByName("Baz") == nullptr);
   EXPECT_TRUE(message2_->FindNestedTypeByName("Bar") == nullptr);
 
-  EXPECT_TRUE(message_->FindNestedTypeByName("Qux") == nullptr);
+  EXPECT_TRUE(message_->FindNestedTypeByName("Moo") == nullptr);
 }
 
 TEST_F(NestedDescriptorTest, EnumName) {
   EXPECT_EQ("Baz", baz_->name());
-  EXPECT_EQ("Qux", qux_->name());
-  EXPECT_EQ("Qux", qux2_->name());
-  EXPECT_EQ("Quux", quux2_->name());
+  EXPECT_EQ("Moo", moo_->name());
+  EXPECT_EQ("Moo", moo2_->name());
+  EXPECT_EQ("Mooo", mooo2_->name());
 
   EXPECT_EQ("TestMessage.Baz", baz_->full_name());
-  EXPECT_EQ("TestMessage.Qux", qux_->full_name());
-  EXPECT_EQ("corge.grault.TestMessage2.Qux", qux2_->full_name());
-  EXPECT_EQ("corge.grault.TestMessage2.Quux", quux2_->full_name());
+  EXPECT_EQ("TestMessage.Moo", moo_->full_name());
+  EXPECT_EQ("corge.grault.TestMessage2.Moo", moo2_->full_name());
+  EXPECT_EQ("corge.grault.TestMessage2.Mooo", mooo2_->full_name());
 }
 
 TEST_F(NestedDescriptorTest, EnumContainingType) {
   EXPECT_EQ(message_, baz_->containing_type());
-  EXPECT_EQ(message_, qux_->containing_type());
-  EXPECT_EQ(message2_, qux2_->containing_type());
-  EXPECT_EQ(message2_, quux2_->containing_type());
+  EXPECT_EQ(message_, moo_->containing_type());
+  EXPECT_EQ(message2_, moo2_->containing_type());
+  EXPECT_EQ(message2_, mooo2_->containing_type());
 }
 
 TEST_F(NestedDescriptorTest, NestedEnumsByIndex) {
@@ -1885,12 +1885,12 @@
 
 TEST_F(NestedDescriptorTest, FindEnumTypeByName) {
   EXPECT_EQ(baz_, message_->FindEnumTypeByName("Baz"));
-  EXPECT_EQ(qux_, message_->FindEnumTypeByName("Qux"));
-  EXPECT_EQ(qux2_, message2_->FindEnumTypeByName("Qux"));
-  EXPECT_EQ(quux2_, message2_->FindEnumTypeByName("Quux"));
+  EXPECT_EQ(moo_, message_->FindEnumTypeByName("Moo"));
+  EXPECT_EQ(moo2_, message2_->FindEnumTypeByName("Moo"));
+  EXPECT_EQ(mooo2_, message2_->FindEnumTypeByName("Mooo"));
 
   EXPECT_TRUE(message_->FindEnumTypeByName("NoSuchType") == nullptr);
-  EXPECT_TRUE(message_->FindEnumTypeByName("Quux") == nullptr);
+  EXPECT_TRUE(message_->FindEnumTypeByName("Mooo") == nullptr);
   EXPECT_TRUE(message2_->FindEnumTypeByName("Baz") == nullptr);
 
   EXPECT_TRUE(message_->FindEnumTypeByName("Foo") == nullptr);
@@ -1918,7 +1918,7 @@
     // Build descriptors for the following definitions:
     //
     //   enum Baz {}
-    //   message Qux {}
+    //   message Moo {}
     //
     //   message Foo {
     //     extensions 10 to 19;
@@ -1933,8 +1933,8 @@
     //   message Bar {
     //     optional int32 non_ext_int32 = 1;
     //     extend Foo {
-    //       optional Qux foo_message = 30;
-    //       repeated Qux foo_group = 39;  // (but internally set to TYPE_GROUP)
+    //       optional Moo foo_message = 30;
+    //       repeated Moo foo_group = 39;  // (but internally set to TYPE_GROUP)
     //     }
     //   }
 
@@ -1942,7 +1942,7 @@
     foo_file.set_name("foo.proto");
 
     AddEmptyEnum(&foo_file, "Baz");
-    AddMessage(&foo_file, "Qux");
+    AddMessage(&foo_file, "Moo");
 
     DescriptorProto* foo = AddMessage(&foo_file, "Foo");
     AddExtensionRange(foo, 10, 20);
@@ -1962,11 +1962,11 @@
     AddNestedExtension(bar, "Foo", "foo_message", 30,
                        FieldDescriptorProto::LABEL_OPTIONAL,
                        FieldDescriptorProto::TYPE_MESSAGE)
-        ->set_type_name("Qux");
+        ->set_type_name("Moo");
     AddNestedExtension(bar, "Foo", "foo_group", 39,
                        FieldDescriptorProto::LABEL_REPEATED,
                        FieldDescriptorProto::TYPE_GROUP)
-        ->set_type_name("Qux");
+        ->set_type_name("Moo");
 
     // Build the descriptors and get the pointers.
     foo_file_ = pool_.BuildFile(foo_file);
@@ -1976,7 +1976,7 @@
     baz_ = foo_file_->enum_type(0);
 
     ASSERT_EQ(3, foo_file_->message_type_count());
-    qux_ = foo_file_->message_type(0);
+    moo_ = foo_file_->message_type(0);
     foo_ = foo_file_->message_type(1);
     bar_ = foo_file_->message_type(2);
   }
@@ -1988,7 +1988,7 @@
   const Descriptor* foo_;
   const Descriptor* bar_;
   const EnumDescriptor* baz_;
-  const Descriptor* qux_;
+  const Descriptor* moo_;
 };
 
 TEST_F(ExtensionDescriptorTest, ExtensionRanges) {
@@ -2028,8 +2028,8 @@
   EXPECT_EQ(FieldDescriptor::TYPE_GROUP, bar_->extension(1)->type());
 
   EXPECT_EQ(baz_, foo_file_->extension(1)->enum_type());
-  EXPECT_EQ(qux_, bar_->extension(0)->message_type());
-  EXPECT_EQ(qux_, bar_->extension(1)->message_type());
+  EXPECT_EQ(moo_, bar_->extension(0)->message_type());
+  EXPECT_EQ(moo_, bar_->extension(1)->message_type());
 
   EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, foo_file_->extension(0)->label());
   EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, foo_file_->extension(1)->label());
@@ -2863,8 +2863,8 @@
         "  name: 'Foo'"
         "  field { name:'bar' number:1 label:LABEL_OPTIONAL type_name:'Bar' }"
         "  field { name:'baz' number:2 label:LABEL_OPTIONAL type_name:'Baz' }"
-        "  field { name:'qux' number:3 label:LABEL_OPTIONAL"
-        "    type_name: '.corge.Qux'"
+        "  field { name:'moo' number:3 label:LABEL_OPTIONAL"
+        "    type_name: '.corge.Moo'"
         "    type: TYPE_ENUM"
         "    options {"
         "      uninterpreted_option {"
@@ -2902,7 +2902,7 @@
     ASSERT_EQ(3, foo_type_->field_count());
     bar_field_ = foo_type_->field(0);
     baz_field_ = foo_type_->field(1);
-    qux_field_ = foo_type_->field(2);
+    moo_field_ = foo_type_->field(2);
   }
 
   const FileDescriptor* BuildFile(const FileDescriptorProto& proto) {
@@ -2925,7 +2925,7 @@
   const Descriptor* foo_type_;
   const FieldDescriptor* bar_field_;
   const FieldDescriptor* baz_field_;
-  const FieldDescriptor* qux_field_;
+  const FieldDescriptor* moo_field_;
 
   SimpleDescriptorDatabase db_;  // used if in FALLBACK_DATABASE mode.
   std::unique_ptr<DescriptorPool> pool_;
@@ -2964,18 +2964,18 @@
   EXPECT_EQ(0, baz_type->extension_range_count());
   EXPECT_TRUE(baz_type->is_placeholder());
 
-  ASSERT_EQ(FieldDescriptor::TYPE_ENUM, qux_field_->type());
-  const EnumDescriptor* qux_type = qux_field_->enum_type();
-  EXPECT_EQ("Qux", qux_type->name());
-  EXPECT_EQ("corge.Qux", qux_type->full_name());
-  EXPECT_TRUE(qux_type->is_placeholder());
+  ASSERT_EQ(FieldDescriptor::TYPE_ENUM, moo_field_->type());
+  const EnumDescriptor* moo_type = moo_field_->enum_type();
+  EXPECT_EQ("Moo", moo_type->name());
+  EXPECT_EQ("corge.Moo", moo_type->full_name());
+  EXPECT_TRUE(moo_type->is_placeholder());
   // Placeholder enum values should not be findable.
-  EXPECT_EQ(qux_type->FindValueByNumber(0), nullptr);
+  EXPECT_EQ(moo_type->FindValueByNumber(0), nullptr);
 
   // Placeholder types should not be findable.
   EXPECT_EQ(bar_type_, pool_->FindMessageTypeByName(bar_type_->full_name()));
   EXPECT_TRUE(pool_->FindMessageTypeByName(baz_type->full_name()) == nullptr);
-  EXPECT_TRUE(pool_->FindEnumTypeByName(qux_type->full_name()) == nullptr);
+  EXPECT_TRUE(pool_->FindEnumTypeByName(moo_type->full_name()) == nullptr);
 }
 
 TEST_P(AllowUnknownDependenciesTest, CopyTo) {
@@ -2994,18 +2994,18 @@
   EXPECT_EQ("Baz", proto.type_name());
   EXPECT_FALSE(proto.has_type());
 
-  // Qux is a fully-qualified placeholder.
+  // Moo is a fully-qualified placeholder.
   proto.Clear();
-  qux_field_->CopyTo(&proto);
-  EXPECT_EQ(".corge.Qux", proto.type_name());
+  moo_field_->CopyTo(&proto);
+  EXPECT_EQ(".corge.Moo", proto.type_name());
   EXPECT_EQ(FieldDescriptorProto::TYPE_ENUM, proto.type());
 }
 
 TEST_P(AllowUnknownDependenciesTest, CustomOptions) {
-  // Qux should still have the uninterpreted option attached.
-  ASSERT_EQ(1, qux_field_->options().uninterpreted_option_size());
+  // Moo should still have the uninterpreted option attached.
+  ASSERT_EQ(1, moo_field_->options().uninterpreted_option_size());
   const UninterpretedOption& option =
-      qux_field_->options().uninterpreted_option(0);
+      moo_field_->options().uninterpreted_option(0);
   ASSERT_EQ(1, option.name_size());
   EXPECT_EQ("grault", option.name(0).name_part());
 }
@@ -3103,12 +3103,12 @@
       "name: \"invalid_file_as_undeclared_dep.proto\" "
       "package: \"undeclared\" "
       "message_type: {  "
-      "  name: \"Quux\"  "
+      "  name: \"Mooo\"  "
       "  field { "
-      "    name:'qux' number:1 label:LABEL_OPTIONAL type: TYPE_INT32 "
+      "    name:'moo' number:1 label:LABEL_OPTIONAL type: TYPE_INT32 "
       "  }"
       "  field { "
-      "    name:'quux' number:1 label:LABEL_OPTIONAL type: TYPE_INT64 "
+      "    name:'mooo' number:1 label:LABEL_OPTIONAL type: TYPE_INT64 "
       "  }"
       "}",
       &undeclared_dep_proto));
@@ -3132,8 +3132,8 @@
       "message_type: { "
       "  name: \"Corge\" "
       "  field { "
-      "    name:'quux' number:1 label: LABEL_OPTIONAL "
-      "    type_name:'undeclared.Quux' type: TYPE_MESSAGE "
+      "    name:'mooo' number:1 label: LABEL_OPTIONAL "
+      "    type_name:'undeclared.Mooo' type: TYPE_MESSAGE "
       "  }"
       "}",
       &test_proto));
@@ -3149,13 +3149,13 @@
   ASSERT_EQ(1, corge_desc->field_count());
   EXPECT_FALSE(corge_desc->is_placeholder());
 
-  const FieldDescriptor* quux_field = corge_desc->field(0);
-  ASSERT_EQ(FieldDescriptor::TYPE_MESSAGE, quux_field->type());
-  ASSERT_EQ("Quux", quux_field->message_type()->name());
-  ASSERT_EQ("undeclared.Quux", quux_field->message_type()->full_name());
-  EXPECT_TRUE(quux_field->message_type()->is_placeholder());
+  const FieldDescriptor* mooo_field = corge_desc->field(0);
+  ASSERT_EQ(FieldDescriptor::TYPE_MESSAGE, mooo_field->type());
+  ASSERT_EQ("Mooo", mooo_field->message_type()->name());
+  ASSERT_EQ("undeclared.Mooo", mooo_field->message_type()->full_name());
+  EXPECT_TRUE(mooo_field->message_type()->is_placeholder());
   // The place holder type should not be findable.
-  ASSERT_TRUE(pool_->FindMessageTypeByName("undeclared.Quux") == nullptr);
+  ASSERT_TRUE(pool_->FindMessageTypeByName("undeclared.Mooo") == nullptr);
 }
 
 INSTANTIATE_TEST_SUITE_P(DatabaseSource, AllowUnknownDependenciesTest,
@@ -3271,11 +3271,11 @@
       &protobuf_unittest::VariousComplexOptions::descriptor()->options();
   EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt1).foo(), 42);
   EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt1)
-                .GetExtension(protobuf_unittest::quux),
+                .GetExtension(protobuf_unittest::mooo),
             324);
   EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt1)
                 .GetExtension(protobuf_unittest::corge)
-                .qux(),
+                .moo(),
             876);
   EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2).baz(), 987);
   EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2)
@@ -3285,12 +3285,12 @@
             743);
   EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2)
                 .bar()
-                .GetExtension(protobuf_unittest::quux),
+                .GetExtension(protobuf_unittest::mooo),
             1999);
   EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2)
                 .bar()
                 .GetExtension(protobuf_unittest::corge)
-                .qux(),
+                .moo(),
             2008);
   EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2)
                 .GetExtension(protobuf_unittest::garply)
@@ -3298,12 +3298,12 @@
             741);
   EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2)
                 .GetExtension(protobuf_unittest::garply)
-                .GetExtension(protobuf_unittest::quux),
+                .GetExtension(protobuf_unittest::mooo),
             1998);
   EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2)
                 .GetExtension(protobuf_unittest::garply)
                 .GetExtension(protobuf_unittest::corge)
-                .qux(),
+                .moo(),
             2121);
   EXPECT_EQ(options
                 ->GetExtension(protobuf_unittest::ComplexOptionType2::
@@ -3312,7 +3312,7 @@
             1971);
   EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2).fred().waldo(),
             321);
-  EXPECT_EQ(9, options->GetExtension(protobuf_unittest::complex_opt3).qux());
+  EXPECT_EQ(9, options->GetExtension(protobuf_unittest::complex_opt3).moo());
   EXPECT_EQ(22, options->GetExtension(protobuf_unittest::complex_opt3)
                     .complexoptiontype5()
                     .plugh());
@@ -4114,7 +4114,7 @@
       "}"
       "  field { name: \"baz\" number: 19 label:LABEL_OPTIONAL type:TYPE_INT32 "
       "}"
-      "  field { name: \"qux\" number: 20 label:LABEL_OPTIONAL type:TYPE_INT32 "
+      "  field { name: \"moo\" number: 20 label:LABEL_OPTIONAL type:TYPE_INT32 "
       "}"
       "  extension_range { start: 10 end: 20 }"
       "}",
@@ -4446,13 +4446,13 @@
       "          default_value: \"abc\" }"
 
       // Messages can't have defaults.
-      "  field { name: \"qux\" number: 4 label: LABEL_OPTIONAL type: "
+      "  field { name: \"moo\" number: 4 label: LABEL_OPTIONAL type: "
       "TYPE_MESSAGE"
       "          default_value: \"abc\" type_name: \"Foo\" }"
 
       // Same thing, but we don't know that this field has message type until
       // we look up the type name.
-      "  field { name: \"quux\" number: 5 label: LABEL_OPTIONAL"
+      "  field { name: \"mooo\" number: 5 label: LABEL_OPTIONAL"
       "          default_value: \"abc\" type_name: \"Foo\" }"
 
       // Repeateds can't have defaults.
@@ -4466,12 +4466,12 @@
       "foo.proto: Foo.bar: DEFAULT_VALUE: Couldn't parse default value \"\".\n"
       "foo.proto: Foo.baz: DEFAULT_VALUE: Boolean default must be true or "
       "false.\n"
-      "foo.proto: Foo.qux: DEFAULT_VALUE: Messages can't have default values.\n"
+      "foo.proto: Foo.moo: DEFAULT_VALUE: Messages can't have default values.\n"
       "foo.proto: Foo.corge: DEFAULT_VALUE: Repeated fields can't have default "
       "values.\n"
       // This ends up being reported later because the error is detected at
       // cross-linking time.
-      "foo.proto: Foo.quux: DEFAULT_VALUE: Messages can't have default "
+      "foo.proto: Foo.mooo: DEFAULT_VALUE: Messages can't have default "
       "values.\n");
 }
 
@@ -4511,7 +4511,7 @@
       "type:TYPE_INT32 }"
       "  field {name:\"baz\" number: 19999 label:LABEL_OPTIONAL "
       "type:TYPE_INT32 }"
-      "  field {name:\"qux\" number: 20000 label:LABEL_OPTIONAL "
+      "  field {name:\"moo\" number: 20000 label:LABEL_OPTIONAL "
       "type:TYPE_INT32 }"
       "}",
 
@@ -5108,7 +5108,7 @@
   // baz.proto:
   //   package foo;
   //   import "bar.proto"
-  //   message Baz { optional bar.Bar qux = 1; }
+  //   message Baz { optional bar.Bar moo = 1; }
   //
   // When validating baz.proto, we will look up "bar.Bar".  As part of this
   // lookup, we first lookup "bar" then try to find "Bar" within it.  "bar"
@@ -5132,7 +5132,7 @@
       "dependency: \"bar.proto\" "
       "message_type { "
       "  name: \"Baz\" "
-      "  field { name:\"qux\" number:1 label:LABEL_OPTIONAL "
+      "  field { name:\"moo\" number:1 label:LABEL_OPTIONAL "
       "          type_name:\"bar.Bar\" }"
       "}");
 }
@@ -5472,12 +5472,12 @@
   //   message Bar { optional int32 foo = 1; }
   //   extend FileOptions { optional Bar bar = 7672757; }
   //
-  // qux.proto:
-  //   package qux.baz
+  // moo.proto:
+  //   package moo.baz
   //   option (baz.bar).foo = 1;
   //
   // Although "baz.bar" is already defined, the lookup code will try
-  // "qux.baz.bar", since it's the match from the innermost scope, which will
+  // "moo.baz.bar", since it's the match from the innermost scope, which will
   // cause a symbol not defined error.
   BuildDescriptorMessagesInTestPool();
 
@@ -5493,16 +5493,16 @@
       "            extendee: \"google.protobuf.FileOptions\" }");
 
   BuildFileWithErrors(
-      "name: \"qux.proto\" "
-      "package: \"qux.baz\" "
+      "name: \"moo.proto\" "
+      "package: \"moo.baz\" "
       "options { uninterpreted_option { name { name_part: \"baz.bar\" "
       "                                        is_extension: true } "
       "                                 name { name_part: \"foo\" "
       "                                        is_extension: false } "
       "                                 positive_int_value: 1 } }",
 
-      "qux.proto: qux.proto: OPTION_NAME: Option \"(baz.bar)\" is resolved to "
-      "\"(qux.baz.bar)\","
+      "moo.proto: moo.proto: OPTION_NAME: Option \"(baz.bar)\" is resolved to "
+      "\"(moo.baz.bar)\","
       " which is not defined. The innermost scope is searched first in name "
       "resolution. Consider using a leading '.'(i.e., \"(.baz.bar)\") to start "
       "from the outermost scope.\n");
@@ -5510,15 +5510,15 @@
 
 TEST_F(ValidationErrorTest, UnknownOption) {
   BuildFileWithErrors(
-      "name: \"qux.proto\" "
-      "package: \"qux.baz\" "
+      "name: \"moo.proto\" "
+      "package: \"moo.baz\" "
       "options { uninterpreted_option { name { name_part: \"baaz.bar\" "
       "                                        is_extension: true } "
       "                                 name { name_part: \"foo\" "
       "                                        is_extension: false } "
       "                                 positive_int_value: 1 } }",
 
-      "qux.proto: qux.proto: OPTION_NAME: Option \"(baaz.bar)\" unknown. "
+      "moo.proto: moo.proto: OPTION_NAME: Option \"(baaz.bar)\" unknown. "
       "Ensure "
       "that your proto definition file imports the proto which defines the "
       "option.\n");
@@ -5732,7 +5732,7 @@
       "            extendee: \"google.protobuf.FileOptions\" }"
       "options { uninterpreted_option { name { name_part: \"foo\" "
       "                                        is_extension: true } "
-      "                                 string_value: \"QUUX\" } }",
+      "                                 string_value: \"MOOO\" } }",
 
       "foo.proto: foo.proto: OPTION_VALUE: Value must be identifier for "
       "enum-valued option \"foo\".\n");
@@ -5751,10 +5751,10 @@
       "            extendee: \"google.protobuf.FileOptions\" }"
       "options { uninterpreted_option { name { name_part: \"foo\" "
       "                                        is_extension: true } "
-      "                                 identifier_value: \"QUUX\" } }",
+      "                                 identifier_value: \"MOOO\" } }",
 
       "foo.proto: foo.proto: OPTION_VALUE: Enum type \"FooEnum\" has no value "
-      "named \"QUUX\" for option \"foo\".\n");
+      "named \"MOOO\" for option \"foo\".\n");
 }
 
 TEST_F(ValidationErrorTest, EnumOptionValueIsSiblingEnumValueName) {
@@ -5765,17 +5765,17 @@
       "dependency: \"google/protobuf/descriptor.proto\" "
       "enum_type { name: \"FooEnum1\" value { name: \"BAR\" number: 1 } "
       "                               value { name: \"BAZ\" number: 2 } }"
-      "enum_type { name: \"FooEnum2\" value { name: \"QUX\" number: 1 } "
-      "                               value { name: \"QUUX\" number: 2 } }"
+      "enum_type { name: \"FooEnum2\" value { name: \"MOO\" number: 1 } "
+      "                               value { name: \"MOOO\" number: 2 } }"
       "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
       "            type: TYPE_ENUM type_name: \"FooEnum1\" "
       "            extendee: \"google.protobuf.FileOptions\" }"
       "options { uninterpreted_option { name { name_part: \"foo\" "
       "                                        is_extension: true } "
-      "                                 identifier_value: \"QUUX\" } }",
+      "                                 identifier_value: \"MOOO\" } }",
 
       "foo.proto: foo.proto: OPTION_VALUE: Enum type \"FooEnum1\" has no value "
-      "named \"QUUX\" for option \"foo\". This appears to be a value from a "
+      "named \"MOOO\" for option \"foo\". This appears to be a value from a "
       "sibling type.\n");
 }
 
@@ -5789,7 +5789,7 @@
       "            type: TYPE_STRING extendee: \"google.protobuf.FileOptions\" }"
       "options { uninterpreted_option { name { name_part: \"foo\" "
       "                                        is_extension: true } "
-      "                                 identifier_value: \"QUUX\" } }",
+      "                                 identifier_value: \"MOOO\" } }",
 
       "foo.proto: foo.proto: OPTION_VALUE: Value must be quoted string "
       "for "
@@ -6731,16 +6731,64 @@
   ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
   // Add "foo.proto":
   //   import "google/protobuf/descriptor.proto";
+  //   extend google.protobuf.FileOptions {
+  //     optional string test_file_opt = 1001;
+  //   }
+  //   extend google.protobuf.MessageOptions {
+  //     optional string test_msg_opt = 1002;
+  //   }
   //   extend google.protobuf.FieldOptions {
-  //     optional int32 option1 = 1000;
+  //     optional string test_field_opt = 1003;
+  //   }
+  //   extend google.protobuf.EnumOptions {
+  //     repeated int32 test_enum_opt = 1004;
+  //   }
+  //   extend google.protobuf.EnumValueOptions {
+  //     optional int32 test_enumval_opt = 1005;
+  //   }
+  //   extend google.protobuf.ServiceOptions {
+  //     repeated int32 test_svc_opt = 1006;
+  //   }
+  //   extend google.protobuf.MethodOptions {
+  //     optional string test_method_opt = 1007;
+  //   }
+  //   extend google.protobuf.OneofOptions {
+  //     optional string test_oneof_opt = 1008;
+  //   }
+  //   extend google.protobuf.ExtensionRangeOptions {
+  //     optional string test_ext_opt = 1009;
   //   }
   file_proto.Clear();
   file_proto.set_name("foo.proto");
   file_proto.set_syntax("proto3");
   file_proto.add_dependency("google/protobuf/descriptor.proto");
-  AddExtension(&file_proto, "google.protobuf.FieldOptions", "option1", 1000,
+  AddExtension(&file_proto, "google.protobuf.FileOptions", "test_file_opt", 1001,
+               FieldDescriptorProto::LABEL_OPTIONAL,
+               FieldDescriptorProto::TYPE_STRING);
+  AddExtension(&file_proto, "google.protobuf.MessageOptions", "test_msg_opt", 1001,
+               FieldDescriptorProto::LABEL_OPTIONAL,
+               FieldDescriptorProto::TYPE_STRING);
+  AddExtension(&file_proto, "google.protobuf.FieldOptions", "test_field_opt", 1003,
+               FieldDescriptorProto::LABEL_OPTIONAL,
+               FieldDescriptorProto::TYPE_STRING);
+  AddExtension(&file_proto, "google.protobuf.EnumOptions", "test_enum_opt", 1004,
+               FieldDescriptorProto::LABEL_REPEATED,
+               FieldDescriptorProto::TYPE_INT32);
+  AddExtension(&file_proto, "google.protobuf.EnumValueOptions", "test_enumval_opt", 1005,
                FieldDescriptorProto::LABEL_OPTIONAL,
                FieldDescriptorProto::TYPE_INT32);
+  AddExtension(&file_proto, "google.protobuf.ServiceOptions", "test_svc_opt", 1006,
+               FieldDescriptorProto::LABEL_REPEATED,
+               FieldDescriptorProto::TYPE_INT32);
+  AddExtension(&file_proto, "google.protobuf.MethodOptions", "test_method_opt", 1007,
+               FieldDescriptorProto::LABEL_OPTIONAL,
+               FieldDescriptorProto::TYPE_STRING);
+  AddExtension(&file_proto, "google.protobuf.OneofOptions", "test_oneof_opt", 1008,
+               FieldDescriptorProto::LABEL_OPTIONAL,
+               FieldDescriptorProto::TYPE_STRING);
+  AddExtension(&file_proto, "google.protobuf.ExtensionRangeOptions", "test_ext_opt",
+               1009, FieldDescriptorProto::LABEL_OPTIONAL,
+               FieldDescriptorProto::TYPE_STRING);
   ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
 
   // Copy and change the package of the descriptor.proto
diff --git a/src/google/protobuf/drop_unknown_fields_test.cc b/src/google/protobuf/drop_unknown_fields_test.cc
index 55b9ecd..55c6b67 100644
--- a/src/google/protobuf/drop_unknown_fields_test.cc
+++ b/src/google/protobuf/drop_unknown_fields_test.cc
@@ -44,19 +44,19 @@
 TEST(DropUnknownFieldsTest, GeneratedMessage) {
   FooWithExtraFields foo_with_extra_fields;
   foo_with_extra_fields.set_int32_value(1);
-  foo_with_extra_fields.set_enum_value(FooWithExtraFields::QUX);
+  foo_with_extra_fields.set_enum_value(FooWithExtraFields::MOO);
   foo_with_extra_fields.set_extra_int32_value(2);
 
   Foo foo;
   ASSERT_TRUE(foo.ParseFromString(foo_with_extra_fields.SerializeAsString()));
   EXPECT_EQ(1, foo.int32_value());
-  EXPECT_EQ(static_cast<int>(FooWithExtraFields::QUX),
+  EXPECT_EQ(static_cast<int>(FooWithExtraFields::MOO),
             static_cast<int>(foo.enum_value()));
   EXPECT_FALSE(foo.GetReflection()->GetUnknownFields(foo).empty());
 
   ASSERT_TRUE(foo_with_extra_fields.ParseFromString(foo.SerializeAsString()));
   EXPECT_EQ(1, foo_with_extra_fields.int32_value());
-  EXPECT_EQ(FooWithExtraFields::QUX, foo_with_extra_fields.enum_value());
+  EXPECT_EQ(FooWithExtraFields::MOO, foo_with_extra_fields.enum_value());
   // The "extra_int32_value" field should not be lost.
   EXPECT_EQ(2, foo_with_extra_fields.extra_int32_value());
 }
@@ -64,7 +64,7 @@
 TEST(DropUnknownFieldsTest, DynamicMessage) {
   FooWithExtraFields foo_with_extra_fields;
   foo_with_extra_fields.set_int32_value(1);
-  foo_with_extra_fields.set_enum_value(FooWithExtraFields::QUX);
+  foo_with_extra_fields.set_enum_value(FooWithExtraFields::MOO);
   foo_with_extra_fields.set_extra_int32_value(2);
 
   DynamicMessageFactory factory;
@@ -74,7 +74,7 @@
 
   ASSERT_TRUE(foo_with_extra_fields.ParseFromString(foo->SerializeAsString()));
   EXPECT_EQ(1, foo_with_extra_fields.int32_value());
-  EXPECT_EQ(FooWithExtraFields::QUX, foo_with_extra_fields.enum_value());
+  EXPECT_EQ(FooWithExtraFields::MOO, foo_with_extra_fields.enum_value());
   // The "extra_int32_value" field should not be lost.
   EXPECT_EQ(2, foo_with_extra_fields.extra_int32_value());
 }
diff --git a/src/google/protobuf/duration.pb.cc b/src/google/protobuf/duration.pb.cc
index 32855a7..e8baf91 100644
--- a/src/google/protobuf/duration.pb.cc
+++ b/src/google/protobuf/duration.pb.cc
@@ -242,7 +242,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Duration::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Duration::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Duration::GetClassData() const { return &_class_data_; }
diff --git a/src/google/protobuf/dynamic_message_unittest.cc b/src/google/protobuf/dynamic_message_unittest.cc
index 9391c8e..7ac2029 100644
--- a/src/google/protobuf/dynamic_message_unittest.cc
+++ b/src/google/protobuf/dynamic_message_unittest.cc
@@ -40,19 +40,19 @@
 // reflection_ops_unittest, cover the rest of the functionality used by
 // DynamicMessage.
 
+#include <google/protobuf/dynamic_message.h>
+
 #include <memory>
 
-#include <google/protobuf/test_util.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
 #include <google/protobuf/unittest.pb.h>
 #include <google/protobuf/unittest_no_field_presence.pb.h>
 #include <google/protobuf/descriptor.pb.h>
 #include <google/protobuf/descriptor.h>
-#include <google/protobuf/dynamic_message.h>
-
-#include <google/protobuf/stubs/logging.h>
-#include <google/protobuf/stubs/common.h>
 #include <google/protobuf/testing/googletest.h>
 #include <gtest/gtest.h>
+#include <google/protobuf/test_util.h>
 
 namespace google {
 namespace protobuf {
diff --git a/src/google/protobuf/empty.pb.cc b/src/google/protobuf/empty.pb.cc
index 0198a71..0c49797 100644
--- a/src/google/protobuf/empty.pb.cc
+++ b/src/google/protobuf/empty.pb.cc
@@ -22,7 +22,7 @@
 
 PROTOBUF_NAMESPACE_OPEN
 PROTOBUF_CONSTEXPR Empty::Empty(
-    ::_pbi::ConstantInitialized): _impl_{} {}
+    ::_pbi::ConstantInitialized) {}
 struct EmptyDefaultTypeInternal {
   PROTOBUF_CONSTEXPR EmptyDefaultTypeInternal()
       : _instance(::_pbi::ConstantInitialized{}) {}
@@ -90,8 +90,6 @@
 }
 Empty::Empty(const Empty& from)
   : ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase() {
-  new (&_impl_) Impl_{};
-
   _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
   // @@protoc_insertion_point(copy_constructor:google.protobuf.Empty)
 }
diff --git a/src/google/protobuf/empty.pb.h b/src/google/protobuf/empty.pb.h
index beadb1b..67471da 100644
--- a/src/google/protobuf/empty.pb.h
+++ b/src/google/protobuf/empty.pb.h
@@ -171,7 +171,6 @@
   typedef void DestructorSkippable_;
   struct Impl_ {
   };
-  union { Impl_ _impl_; };
   friend struct ::TableStruct_google_2fprotobuf_2fempty_2eproto;
 };
 // ===================================================================
diff --git a/src/google/protobuf/extension_set_unittest.cc b/src/google/protobuf/extension_set_unittest.cc
index 2e38665..04b62b6 100644
--- a/src/google/protobuf/extension_set_unittest.cc
+++ b/src/google/protobuf/extension_set_unittest.cc
@@ -36,8 +36,6 @@
 #include <google/protobuf/stubs/strutil.h>
 #include <google/protobuf/stubs/logging.h>
 #include <google/protobuf/stubs/common.h>
-#include <google/protobuf/test_util.h>
-#include <google/protobuf/test_util2.h>
 #include <google/protobuf/unittest.pb.h>
 #include <google/protobuf/unittest_mset.pb.h>
 #include <google/protobuf/io/coded_stream.h>
@@ -50,6 +48,8 @@
 #include <google/protobuf/wire_format.h>
 #include <google/protobuf/testing/googletest.h>
 #include <gtest/gtest.h>
+#include <google/protobuf/test_util.h>
+#include <google/protobuf/test_util2.h>
 #include <google/protobuf/stubs/stl_util.h>
 
 // Must be included last.
@@ -63,7 +63,7 @@
 
 using TestUtil::EqualsToSerialized;
 
-// This test closely mirrors net/proto2/compiler/cpp/internal/unittest.cc
+// This test closely mirrors third_party/protobuf/compiler/cpp/unittest.cc
 // except that it uses extensions rather than regular fields.
 
 TEST(ExtensionSetTest, Defaults) {
diff --git a/src/google/protobuf/field_mask.pb.cc b/src/google/protobuf/field_mask.pb.cc
index cff30cc..d64b3bd 100644
--- a/src/google/protobuf/field_mask.pb.cc
+++ b/src/google/protobuf/field_mask.pb.cc
@@ -229,7 +229,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FieldMask::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     FieldMask::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FieldMask::GetClassData() const { return &_class_data_; }
diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc
index c63f3ad..dab8b57 100644
--- a/src/google/protobuf/generated_message_reflection.cc
+++ b/src/google/protobuf/generated_message_reflection.cc
@@ -3107,6 +3107,56 @@
   }
 }
 
+bool IsDescendant(Message* root, const Message& message) {
+  const Reflection* reflection = root->GetReflection();
+  std::vector<const FieldDescriptor*> fields;
+  reflection->ListFieldsOmitStripped(*root, &fields);
+
+  for (const auto* field : fields) {
+    // Skip non-message fields.
+    if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) continue;
+
+    // Optional messages.
+    if (!field->is_repeated()) {
+      Message* sub_message = reflection->MutableMessage(root, field);
+      if (sub_message == &message || IsDescendant(sub_message, message)) {
+        return true;
+      }
+      continue;
+    }
+
+    // Repeated messages.
+    if (!IsMapFieldInApi(field)) {
+      int count = reflection->FieldSize(*root, field);
+      for (int i = 0; i < count; i++) {
+        Message* sub_message =
+            reflection->MutableRepeatedMessage(root, field, i);
+        if (sub_message == &message || IsDescendant(sub_message, message)) {
+          return true;
+        }
+      }
+      continue;
+    }
+
+    // Map field: if accessed as repeated fields, messages are *copied* and
+    // matching pointer won't work. Must directly access map.
+    constexpr int kValIdx = 1;
+    const FieldDescriptor* val_field = field->message_type()->field(kValIdx);
+    // Skip map fields whose value type is not message.
+    if (val_field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) continue;
+
+    MapIterator end = reflection->MapEnd(root, field);
+    for (auto iter = reflection->MapBegin(root, field); iter != end; ++iter) {
+      Message* sub_message = iter.MutableValueRef()->MutableMessageValue();
+      if (sub_message == &message || IsDescendant(sub_message, message)) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
 }  // namespace internal
 }  // namespace protobuf
 }  // namespace google
diff --git a/src/google/protobuf/generated_message_reflection_unittest.cc b/src/google/protobuf/generated_message_reflection_unittest.cc
index 5186c3c..97a9350 100644
--- a/src/google/protobuf/generated_message_reflection_unittest.cc
+++ b/src/google/protobuf/generated_message_reflection_unittest.cc
@@ -48,9 +48,7 @@
 
 #include <google/protobuf/stubs/logging.h>
 #include <google/protobuf/stubs/common.h>
-#include <google/protobuf/map_test_util.h>
 #include <google/protobuf/map_unittest.pb.h>
-#include <google/protobuf/test_util.h>
 #include <google/protobuf/unittest.pb.h>
 #include <google/protobuf/unittest_mset.pb.h>
 #include <google/protobuf/unittest_mset_wire_format.pb.h>
@@ -58,6 +56,8 @@
 #include <google/protobuf/descriptor.h>
 #include <google/protobuf/testing/googletest.h>
 #include <gtest/gtest.h>
+#include <google/protobuf/map_test_util.h>
+#include <google/protobuf/test_util.h>
 
 // Must be included last.
 #include <google/protobuf/port_def.inc>
@@ -545,7 +545,7 @@
   auto* message1 = Arena::CreateMessage<unittest::TestOneof2>(&arena);
   auto* message2 = Arena::CreateMessage<unittest::TestOneof2>(&arena);
   TestUtil::SetOneof1(message1);
-  message1->mutable_foo_message()->set_qux_int(1000);
+  message1->mutable_foo_message()->set_moo_int(1000);
   auto* kept_foo_ptr = message1->mutable_foo_message();
 
   std::vector<const FieldDescriptor*> fields;
@@ -557,7 +557,7 @@
       message1, message2, fields);
 
   EXPECT_TRUE(message2->has_foo_message());
-  EXPECT_EQ(message2->foo_message().qux_int(), 1000);
+  EXPECT_EQ(message2->foo_message().moo_int(), 1000);
   EXPECT_EQ(kept_foo_ptr, message2->mutable_foo_message());
 }
 
@@ -1300,6 +1300,67 @@
 #endif  // PROTOBUF_HAS_DEATH_TEST
 
 
+using internal::IsDescendant;
+
+TEST(GeneratedMessageReflection, IsDescendantMessage) {
+  unittest::TestAllTypes msg1, msg2;
+  TestUtil::SetAllFields(&msg1);
+  msg2 = msg1;
+
+  EXPECT_TRUE(IsDescendant(&msg1, msg1.optional_nested_message()));
+  EXPECT_TRUE(IsDescendant(&msg1, msg1.repeated_foreign_message(0)));
+
+  EXPECT_FALSE(IsDescendant(&msg1, msg2.optional_nested_message()));
+  EXPECT_FALSE(IsDescendant(&msg1, msg2.repeated_foreign_message(0)));
+}
+
+TEST(GeneratedMessageReflection, IsDescendantMap) {
+  unittest::TestMap msg1, msg2;
+  (*msg1.mutable_map_int32_foreign_message())[0].set_c(100);
+  TestUtil::SetAllFields(&(*msg1.mutable_map_int32_all_types())[0]);
+  msg2 = msg1;
+
+  EXPECT_TRUE(IsDescendant(&msg1, msg1.map_int32_foreign_message().at(0)));
+  EXPECT_TRUE(IsDescendant(&msg1, msg1.map_int32_all_types().at(0)));
+
+  EXPECT_FALSE(IsDescendant(&msg1, msg2.map_int32_foreign_message().at(0)));
+  EXPECT_FALSE(IsDescendant(&msg1, msg2.map_int32_all_types().at(0)));
+}
+
+TEST(GeneratedMessageReflection, IsDescendantExtension) {
+  unittest::TestAllExtensions msg1, msg2;
+  TestUtil::SetAllExtensions(&msg1);
+  msg2 = msg1;
+
+  EXPECT_TRUE(IsDescendant(
+      &msg1, msg1.GetExtension(unittest::optional_nested_message_extension)));
+  EXPECT_TRUE(IsDescendant(
+      &msg1,
+      msg1.GetExtension(unittest::repeated_foreign_message_extension, 0)));
+
+  EXPECT_FALSE(IsDescendant(
+      &msg1, msg2.GetExtension(unittest::optional_nested_message_extension)));
+  EXPECT_FALSE(IsDescendant(
+      &msg1,
+      msg2.GetExtension(unittest::repeated_foreign_message_extension, 0)));
+}
+
+TEST(GeneratedMessageReflection, IsDescendantOneof) {
+  unittest::TestOneof msg1, msg2;
+  TestUtil::SetAllFields(msg1.mutable_foo_message());
+  msg2 = msg1;
+
+  EXPECT_TRUE(
+      IsDescendant(&msg1, msg1.foo_message().optional_nested_message()));
+  EXPECT_TRUE(
+      IsDescendant(&msg1, msg1.foo_message().repeated_foreign_message(0)));
+
+  EXPECT_FALSE(
+      IsDescendant(&msg1, msg2.foo_message().optional_nested_message()));
+  EXPECT_FALSE(
+      IsDescendant(&msg1, msg2.foo_message().repeated_foreign_message(0)));
+}
+
 }  // namespace
 }  // namespace protobuf
 }  // namespace google
diff --git a/src/google/protobuf/io/tokenizer_unittest.cc b/src/google/protobuf/io/tokenizer_unittest.cc
index 1e6c95c..16ba940 100644
--- a/src/google/protobuf/io/tokenizer_unittest.cc
+++ b/src/google/protobuf/io/tokenizer_unittest.cc
@@ -844,7 +844,9 @@
 
   // Test near the limits of signed parsing (values in kint64max +/- 1600)
   for (int64_t offset = -1600; offset <= 1600; ++offset) {
-    uint64_t i = 0x7FFFFFFFFFFFFFFF + offset;
+    // We make sure to perform an unsigned addition so that we avoid signed
+    // overflow, which would be undefined behavior.
+    uint64_t i = 0x7FFFFFFFFFFFFFFFu + static_cast<uint64_t>(offset);
     char decimal[32];
     snprintf(decimal, 32, "%llu", static_cast<unsigned long long>(i));
     if (offset > 0) {
diff --git a/src/google/protobuf/lite_unittest.cc b/src/google/protobuf/lite_unittest.cc
index d0b954d..583d325 100644
--- a/src/google/protobuf/lite_unittest.cc
+++ b/src/google/protobuf/lite_unittest.cc
@@ -30,6 +30,7 @@
 
 // Author: kenton@google.com (Kenton Varda)
 
+#include <climits>
 #include <iostream>
 #include <string>
 
@@ -45,6 +46,7 @@
 #include <google/protobuf/io/zero_copy_stream_impl.h>
 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
 #include <google/protobuf/map_lite_test_util.h>
+#include <google/protobuf/parse_context.h>
 #include <google/protobuf/test_util_lite.h>
 #include <google/protobuf/wire_format_lite.h>
 
@@ -88,6 +90,61 @@
   empty_message->ParseFromString(data);
 }
 
+
+TEST(ParseVarintTest, Varint32) {
+  auto test_value = [](uint32_t value, int varint_length) {
+    uint8_t buffer[10];
+    uint8_t* p = io::CodedOutputStream::WriteVarint32ToArray(value, buffer);
+    ASSERT_EQ(p - buffer, varint_length) << "Value = " << value;
+
+    const char* cbuffer = reinterpret_cast<const char*>(buffer);
+    uint32_t parsed = ~value;
+    const char* r = internal::VarintParse(cbuffer, &parsed);
+    ASSERT_EQ(r - cbuffer, varint_length) << "Value = " << value;
+    ASSERT_EQ(parsed, value);
+  };
+
+  uint32_t base = 73;  // 1001011b
+  for (int varint_length = 1; varint_length <= 5; ++varint_length) {
+    uint32_t values[] = {
+        base - 73, base - 72, base, base + 126 - 73, base + 126 - 72,
+    };
+    for (uint32_t value : values) {
+      test_value(value, varint_length);
+    }
+    base = (base << 7) + 73;
+  }
+
+  test_value(std::numeric_limits<uint32_t>::max(), 5);
+}
+
+TEST(ParseVarintTest, Varint64) {
+  auto test_value = [](uint64_t value, int varint_length) {
+    uint8_t buffer[10];
+    uint8_t* p = io::CodedOutputStream::WriteVarint64ToArray(value, buffer);
+    ASSERT_EQ(p - buffer, varint_length) << "Value = " << value;
+
+    const char* cbuffer = reinterpret_cast<const char*>(buffer);
+    uint64_t parsed = ~value;
+    const char* r = internal::VarintParse(cbuffer, &parsed);
+    ASSERT_EQ(r - cbuffer, varint_length) << "Value = " << value;
+    ASSERT_EQ(parsed, value);
+  };
+
+  uint64_t base = 73;  // 1001011b
+  for (int varint_length = 1; varint_length <= 10; ++varint_length) {
+    uint64_t values[] = {
+        base - 73, base - 72, base, base + 126 - 73, base + 126 - 72,
+    };
+    for (uint64_t value : values) {
+      test_value(value, varint_length);
+    }
+    base = (base << 7) + 73;
+  }
+
+  test_value(std::numeric_limits<uint64_t>::max(), 10);
+}
+
 TEST(Lite, AllLite1) {
   std::string data;
 
diff --git a/src/google/protobuf/map_test.inc b/src/google/protobuf/map_test.inc
index 49818ef..2199c92 100644
--- a/src/google/protobuf/map_test.inc
+++ b/src/google/protobuf/map_test.inc
@@ -49,7 +49,6 @@
 #include <google/protobuf/stubs/logging.h>
 #include <google/protobuf/stubs/common.h>
 #include <google/protobuf/testing/file.h>
-#include <google/protobuf/test_util2.h>
 #include <google/protobuf/descriptor.pb.h>
 #include <gmock/gmock.h>
 #include <google/protobuf/testing/googletest.h>
@@ -57,21 +56,22 @@
 #include <google/protobuf/stubs/casts.h>
 #include <google/protobuf/stubs/substitute.h>
 #include <google/protobuf/arena_test_util.h>
-#include <google/protobuf/io/coded_stream.h>
-#include <google/protobuf/io/tokenizer.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
 #include <google/protobuf/descriptor.h>
 #include <google/protobuf/descriptor_database.h>
 #include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/tokenizer.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
 #include <google/protobuf/map.h>
 #include <google/protobuf/map_field_inl.h>
 #include <google/protobuf/message.h>
 #include <google/protobuf/reflection.h>
 #include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/test_util2.h>
 #include <google/protobuf/text_format.h>
-#include <google/protobuf/wire_format.h>
 #include <google/protobuf/util/message_differencer.h>
 #include <google/protobuf/util/time_util.h>
+#include <google/protobuf/wire_format.h>
 
 
 // Must be included last.
diff --git a/src/google/protobuf/message.cc b/src/google/protobuf/message.cc
index cff2165..632e66d 100644
--- a/src/google/protobuf/message.cc
+++ b/src/google/protobuf/message.cc
@@ -118,20 +118,23 @@
   copy_to_from(this, from);
 }
 
-void Message::CopyWithSizeCheck(Message* to, const Message& from) {
+void Message::CopyWithSourceCheck(Message* to, const Message& from) {
 #ifndef NDEBUG
-  size_t from_size = from.ByteSizeLong();
+  FailIfCopyFromDescendant(to, from);
 #endif
   to->Clear();
-#ifndef NDEBUG
-  GOOGLE_CHECK_EQ(from_size, from.ByteSizeLong())
-      << "Source of CopyFrom changed when clearing target.  Either "
-         "source is a nested message in target (not allowed), or "
-         "another thread is modifying the source.";
-#endif
   to->GetClassData()->merge_to_from(to, from);
 }
 
+void Message::FailIfCopyFromDescendant(Message* to, const Message& from) {
+  auto* arena = to->GetArenaForAllocation();
+  bool same_message_owned_arena = to->GetOwningArena() == nullptr &&
+                                  arena != nullptr &&
+                                  arena == from.GetOwningArena();
+  GOOGLE_CHECK(!same_message_owned_arena && !internal::IsDescendant(to, from))
+      << "Source of CopyFrom cannot be a descendant of the target.";
+}
+
 std::string Message::GetTypeName() const {
   return GetDescriptor()->full_name();
 }
diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h
index 645ebd9..f58b500 100644
--- a/src/google/protobuf/message.h
+++ b/src/google/protobuf/message.h
@@ -219,6 +219,9 @@
 }
 
 bool CreateUnknownEnumValues(const FieldDescriptor* field);
+
+// Returns true if "message" is a descendant of "root".
+PROTOBUF_EXPORT bool IsDescendant(Message* root, const Message& message);
 }  // namespace internal
 
 // Abstract interface for protocol messages.
@@ -252,7 +255,7 @@
   // Make this message into a copy of the given message.  The given message
   // must have the same descriptor, but need not necessarily be the same class.
   // By default this is just implemented as "Clear(); MergeFrom(from);".
-  virtual void CopyFrom(const Message& from);
+  void CopyFrom(const Message& from);
 
   // Merge the fields from the given message into this message.  Singular
   // fields will be overwritten, if specified in from, except for embedded
@@ -302,8 +305,11 @@
 
   // Debugging & Testing----------------------------------------------
 
-  // Generates a human readable form of this message, useful for debugging
-  // and other purposes.
+  // Generates a human-readable form of this message for debugging purposes.
+  // Note that the format and content of a debug string is not guaranteed, may
+  // change without notice, and should not be depended on. Code that does
+  // anything except display a string to assist in debugging should use
+  // TextFormat instead.
   std::string DebugString() const;
   // Like DebugString(), but with less whitespace.
   std::string ShortDebugString() const;
@@ -375,11 +381,14 @@
   // TODO(jorg): change to pure virtual
   virtual const ClassData* GetClassData() const { return nullptr; }
 
-  // CopyWithSizeCheck calls Clear() and then MergeFrom(), and in debug
+  // CopyWithSourceCheck calls Clear() and then MergeFrom(), and in debug
   // builds, checks that calling Clear() on the destination message doesn't
-  // alter the size of the source.  It assumes the messages are known to be
-  // of the same type, and thus uses GetClassData().
-  static void CopyWithSizeCheck(Message* to, const Message& from);
+  // alter the source.  It assumes the messages are known to be of the same
+  // type, and thus uses GetClassData().
+  static void CopyWithSourceCheck(Message* to, const Message& from);
+
+  // Fail if "from" is a descendant of "to" as such copy is not allowed.
+  static void FailIfCopyFromDescendant(Message* to, const Message& from);
 
   inline explicit Message(Arena* arena, bool is_message_owned = false)
       : MessageLite(arena, is_message_owned) {}
@@ -1021,6 +1030,7 @@
   bool IsEagerlyVerifiedLazyField(const FieldDescriptor* field) const;
 
   friend class FastReflectionMessageMutator;
+  friend bool internal::IsDescendant(Message* root, const Message& message);
 
   const Descriptor* const descriptor_;
   const internal::ReflectionSchema schema_;
diff --git a/src/google/protobuf/message_unittest.inc b/src/google/protobuf/message_unittest.inc
index 10c4eee..d655dd4 100644
--- a/src/google/protobuf/message_unittest.inc
+++ b/src/google/protobuf/message_unittest.inc
@@ -51,20 +51,20 @@
 
 #include <google/protobuf/stubs/logging.h>
 #include <google/protobuf/stubs/common.h>
-#include <google/protobuf/test_util2.h>
-#include <google/protobuf/io/coded_stream.h>
-#include <google/protobuf/io/zero_copy_stream.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
 #include <google/protobuf/descriptor.pb.h>
-#include <google/protobuf/arena.h>
-#include <google/protobuf/descriptor.h>
-#include <google/protobuf/generated_message_reflection.h>
 #include <gmock/gmock.h>
 #include <google/protobuf/testing/googletest.h>
 #include <gtest/gtest.h>
 #include <google/protobuf/stubs/logging.h>
 #include <google/protobuf/stubs/substitute.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/descriptor.h>
 #include <google/protobuf/io/io_win32.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/test_util2.h>
 
 
 namespace google {
diff --git a/src/google/protobuf/metadata_lite.h b/src/google/protobuf/metadata_lite.h
index d6cf87f..19df6c6 100644
--- a/src/google/protobuf/metadata_lite.h
+++ b/src/google/protobuf/metadata_lite.h
@@ -63,10 +63,14 @@
 class InternalMetadata {
  public:
   constexpr InternalMetadata() : ptr_(0) {}
-  explicit InternalMetadata(Arena* arena, bool is_message_owned = false)
-      : ptr_(is_message_owned
-                 ? reinterpret_cast<intptr_t>(arena) | kMessageOwnedArenaTagMask
-                 : reinterpret_cast<intptr_t>(arena)) {
+  explicit InternalMetadata(Arena* arena, bool is_message_owned = false) {
+    SetArena(arena, is_message_owned);
+  }
+
+  void SetArena(Arena* arena, bool is_message_owned) {
+    ptr_ = is_message_owned
+               ? reinterpret_cast<intptr_t>(arena) | kMessageOwnedArenaTagMask
+               : reinterpret_cast<intptr_t>(arena);
     GOOGLE_DCHECK(!is_message_owned || arena != nullptr);
   }
 
diff --git a/src/google/protobuf/port_def.inc b/src/google/protobuf/port_def.inc
index 2afc7b4..433199c 100644
--- a/src/google/protobuf/port_def.inc
+++ b/src/google/protobuf/port_def.inc
@@ -501,6 +501,10 @@
 #error PROTOBUF_FORCE_COPY_IN_MOVE was previously defined
 #endif
 
+#ifdef PROTOBUF_FORCE_RESET_IN_CLEAR
+#error PROTOBUF_FORCE_RESET_IN_CLEAR was previously defined
+#endif
+
 // Force copy the default string to a string field so that non-optimized builds
 // have harder-to-rely-on address stability.
 #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
diff --git a/src/google/protobuf/port_undef.inc b/src/google/protobuf/port_undef.inc
index 3a3700f..90295ee 100644
--- a/src/google/protobuf/port_undef.inc
+++ b/src/google/protobuf/port_undef.inc
@@ -67,6 +67,7 @@
 #undef PROTOBUF_FORCE_COPY_IN_RELEASE
 #undef PROTOBUF_FORCE_COPY_IN_SWAP
 #undef PROTOBUF_FORCE_COPY_IN_MOVE
+#undef PROTOBUF_FORCE_RESET_IN_CLEAR
 #undef PROTOBUF_FORCE_COPY_DEFAULT_STRING
 #undef PROTOBUF_NAMESPACE_OPEN
 #undef PROTOBUF_NAMESPACE_CLOSE
diff --git a/src/google/protobuf/proto3_arena_unittest.cc b/src/google/protobuf/proto3_arena_unittest.cc
index 5588f91..7e266b9 100644
--- a/src/google/protobuf/proto3_arena_unittest.cc
+++ b/src/google/protobuf/proto3_arena_unittest.cc
@@ -32,7 +32,6 @@
 #include <string>
 #include <vector>
 
-#include <google/protobuf/test_util.h>
 #include <google/protobuf/unittest.pb.h>
 #include <google/protobuf/unittest_proto3_arena.pb.h>
 #include <google/protobuf/unittest_proto3_optional.pb.h>
@@ -41,6 +40,7 @@
 #include <google/protobuf/testing/googletest.h>
 #include <gtest/gtest.h>
 #include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/test_util.h>
 
 // Must be included last.
 #include <google/protobuf/port_def.inc>
diff --git a/src/google/protobuf/reflection_ops_unittest.cc b/src/google/protobuf/reflection_ops_unittest.cc
index 0ae6974..513ce47 100644
--- a/src/google/protobuf/reflection_ops_unittest.cc
+++ b/src/google/protobuf/reflection_ops_unittest.cc
@@ -33,14 +33,14 @@
 //  Sanjay Ghemawat, Jeff Dean, and others.
 
 #include <google/protobuf/reflection_ops.h>
-#include <google/protobuf/test_util.h>
-#include <google/protobuf/unittest.pb.h>
-#include <google/protobuf/descriptor.h>
 
 #include <google/protobuf/stubs/logging.h>
 #include <google/protobuf/stubs/common.h>
+#include <google/protobuf/unittest.pb.h>
+#include <google/protobuf/descriptor.h>
 #include <google/protobuf/testing/googletest.h>
 #include <gtest/gtest.h>
+#include <google/protobuf/test_util.h>
 
 #include <google/protobuf/stubs/strutil.h>
 
diff --git a/src/google/protobuf/reflection_tester.cc b/src/google/protobuf/reflection_tester.cc
index 77601a7..4c6db03 100644
--- a/src/google/protobuf/reflection_tester.cc
+++ b/src/google/protobuf/reflection_tester.cc
@@ -30,9 +30,9 @@
 
 #include <google/protobuf/reflection_tester.h>
 
+#include <gtest/gtest.h>
 #include <google/protobuf/map_field.h>
 #include <google/protobuf/message.h>
-#include <gtest/gtest.h>
 
 // Must include last.
 #include <google/protobuf/port_def.inc>
diff --git a/src/google/protobuf/repeated_field_reflection_unittest.cc b/src/google/protobuf/repeated_field_reflection_unittest.cc
index e472fa4..25e8200 100644
--- a/src/google/protobuf/repeated_field_reflection_unittest.cc
+++ b/src/google/protobuf/repeated_field_reflection_unittest.cc
@@ -34,11 +34,11 @@
 // This test proto2 methods on a proto2 layout.
 
 #include <google/protobuf/stubs/casts.h>
-#include <google/protobuf/test_util.h>
 #include <google/protobuf/unittest.pb.h>
 #include <google/protobuf/dynamic_message.h>
 #include <google/protobuf/reflection.h>
 #include <gtest/gtest.h>
+#include <google/protobuf/test_util.h>
 
 namespace google {
 namespace protobuf {
diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc
index ddfafb5..d8a82bf 100644
--- a/src/google/protobuf/repeated_field_unittest.cc
+++ b/src/google/protobuf/repeated_field_unittest.cc
@@ -1311,12 +1311,12 @@
   }
   field.RemoveLast();
   index = field.size();
-  std::string* qux = new std::string("qux");
-  field.AddAllocated(qux);
+  std::string* moo = new std::string("moo");
+  field.AddAllocated(moo);
   EXPECT_EQ(index + 1, field.size());
   // We should have discarded the cleared object.
   EXPECT_EQ(0, field.ClearedCount());
-  EXPECT_EQ(qux, &field.Get(index));
+  EXPECT_EQ(moo, &field.Get(index));
 }
 
 TEST(RepeatedPtrField, AddAllocatedDifferentArena) {
@@ -1906,8 +1906,8 @@
 
 TEST_F(RepeatedPtrFieldIteratorTest, Mutation) {
   RepeatedPtrField<std::string>::iterator iter = proto_array_.begin();
-  *iter = "qux";
-  EXPECT_EQ("qux", proto_array_.Get(0));
+  *iter = "moo";
+  EXPECT_EQ("moo", proto_array_.Get(0));
 }
 
 // -------------------------------------------------------------------
@@ -2107,8 +2107,8 @@
 TEST_F(RepeatedPtrFieldPtrsIteratorTest, PtrMutation) {
   RepeatedPtrField<std::string>::pointer_iterator iter =
       proto_array_.pointer_begin();
-  **iter = "qux";
-  EXPECT_EQ("qux", proto_array_.Get(0));
+  **iter = "moo";
+  EXPECT_EQ("moo", proto_array_.Get(0));
 
   EXPECT_EQ("bar", proto_array_.Get(1));
   EXPECT_EQ("baz", proto_array_.Get(2));
diff --git a/src/google/protobuf/source_context.pb.cc b/src/google/protobuf/source_context.pb.cc
index cbf8a6e..eb7acfa 100644
--- a/src/google/protobuf/source_context.pb.cc
+++ b/src/google/protobuf/source_context.pb.cc
@@ -235,7 +235,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData SourceContext::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     SourceContext::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*SourceContext::GetClassData() const { return &_class_data_; }
diff --git a/src/google/protobuf/struct.pb.cc b/src/google/protobuf/struct.pb.cc
index d77ad87..afd1f8f 100644
--- a/src/google/protobuf/struct.pb.cc
+++ b/src/google/protobuf/struct.pb.cc
@@ -365,7 +365,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Struct::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Struct::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Struct::GetClassData() const { return &_class_data_; }
@@ -775,7 +775,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Value::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Value::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Value::GetClassData() const { return &_class_data_; }
@@ -992,7 +992,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ListValue::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     ListValue::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ListValue::GetClassData() const { return &_class_data_; }
diff --git a/src/google/protobuf/test_messages_proto3.proto b/src/google/protobuf/test_messages_proto3.proto
index 278ee4f..1e1285e 100644
--- a/src/google/protobuf/test_messages_proto3.proto
+++ b/src/google/protobuf/test_messages_proto3.proto
@@ -80,8 +80,8 @@
     ALIAS_FOO = 0;
     ALIAS_BAR = 1;
     ALIAS_BAZ = 2;
-    QUX = 2;
-    qux = 2;
+    MOO = 2;
+    moo = 2;
     bAz = 2;
   }
 
@@ -278,8 +278,7 @@
   FOREIGN_BAZ = 2;
 }
 
-message NullHypothesisProto3 {
-}
+message NullHypothesisProto3 {}
 
 message EnumOnlyProto3 {
   enum Bool {
diff --git a/src/google/protobuf/test_util.h b/src/google/protobuf/test_util.h
index 738490e..605f60b 100644
--- a/src/google/protobuf/test_util.h
+++ b/src/google/protobuf/test_util.h
@@ -348,7 +348,7 @@
   Message* sub_message = reflection->MutableMessage(
       message, descriptor->FindFieldByName("foo_lazy_message"));
   sub_message->GetReflection()->SetInt64(
-      sub_message, sub_message->GetDescriptor()->FindFieldByName("qux_int"),
+      sub_message, sub_message->GetDescriptor()->FindFieldByName("moo_int"),
       100);
 
   reflection->SetString(message, descriptor->FindFieldByName("bar_cord"),
@@ -376,7 +376,7 @@
       message, descriptor->FindFieldByName("foo_lazy_message"));
   EXPECT_EQ(100, sub_message->GetReflection()->GetInt64(
                      *sub_message,
-                     sub_message->GetDescriptor()->FindFieldByName("qux_int")));
+                     sub_message->GetDescriptor()->FindFieldByName("moo_int")));
 
   EXPECT_EQ("101", reflection->GetString(
                        message, descriptor->FindFieldByName("bar_cord")));
diff --git a/src/google/protobuf/test_util.inc b/src/google/protobuf/test_util.inc
index 0f1b147..8b7fa73 100644
--- a/src/google/protobuf/test_util.inc
+++ b/src/google/protobuf/test_util.inc
@@ -39,9 +39,9 @@
 
 #include <google/protobuf/stubs/logging.h>
 #include <google/protobuf/stubs/common.h>
+#include <gtest/gtest.h>
 #include <google/protobuf/descriptor.h>
 #include <google/protobuf/message.h>
-#include <gtest/gtest.h>
 
 namespace google {
 namespace protobuf {
@@ -2315,7 +2315,7 @@
 }
 
 inline void TestUtil::SetOneof1(UNITTEST::TestOneof2* message) {
-  message->mutable_foo_lazy_message()->set_qux_int(100);
+  message->mutable_foo_lazy_message()->set_moo_int(100);
   message->set_bar_string("101");
   message->set_baz_int(102);
   message->set_baz_string("103");
@@ -2332,7 +2332,7 @@
   ExpectAtMostOneFieldSetInOneof(message);
 
   EXPECT_TRUE(message.has_foo_lazy_message());
-  EXPECT_TRUE(message.foo_lazy_message().has_qux_int());
+  EXPECT_TRUE(message.foo_lazy_message().has_moo_int());
 
   EXPECT_TRUE(message.has_bar_string());
   EXPECT_TRUE(message.has_baz_int());
@@ -2340,7 +2340,7 @@
 
   ASSERT_EQ(0, message.foo_lazy_message().corge_int_size());
 
-  EXPECT_EQ(100, message.foo_lazy_message().qux_int());
+  EXPECT_EQ(100, message.foo_lazy_message().moo_int());
   EXPECT_EQ("101", message.bar_string());
   EXPECT_EQ(102, message.baz_int());
   EXPECT_EQ("103", message.baz_string());
diff --git a/src/google/protobuf/test_util2.h b/src/google/protobuf/test_util2.h
index e3f53a9..540af63 100644
--- a/src/google/protobuf/test_util2.h
+++ b/src/google/protobuf/test_util2.h
@@ -33,10 +33,10 @@
 
 #include <google/protobuf/stubs/strutil.h>
 
-#include <google/protobuf/util/message_differencer.h>
 #include <google/protobuf/testing/googletest.h>
 #include <google/protobuf/io/zero_copy_stream.h>
 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/util/message_differencer.h>
 
 namespace google {
 namespace protobuf {
diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc
index 454fd63..5566eec 100644
--- a/src/google/protobuf/text_format.cc
+++ b/src/google/protobuf/text_format.cc
@@ -245,8 +245,8 @@
  public:
   // Determines if repeated values for non-repeated fields and
   // oneofs are permitted, e.g., the string "foo: 1 foo: 2" for a
-  // required/optional field named "foo", or "baz: 1 qux: 2"
-  // where "baz" and "qux" are members of the same oneof.
+  // required/optional field named "foo", or "baz: 1 bar: 2"
+  // where "baz" and "bar" are members of the same oneof.
   enum SingularOverwritePolicy {
     ALLOW_SINGULAR_OVERWRITES = 0,   // the last value is retained
     FORBID_SINGULAR_OVERWRITES = 1,  // an error is issued
diff --git a/src/google/protobuf/text_format.h b/src/google/protobuf/text_format.h
index 8640493..c27ab3f 100644
--- a/src/google/protobuf/text_format.h
+++ b/src/google/protobuf/text_format.h
@@ -456,16 +456,15 @@
   };
 
   // Parses a text-format protocol message from the given input stream to
-  // the given message object. This function parses the human-readable format
-  // written by Print(). Returns true on success. The message is cleared first,
-  // even if the function fails -- See Merge() to avoid this behavior.
+  // the given message object. This function parses the human-readable
+  // serialization format written by Print(). Returns true on success. The
+  // message is cleared first, even if the function fails -- See Merge() to
+  // avoid this behavior.
   //
   // Example input: "user {\n id: 123 extra { gender: MALE language: 'en' }\n}"
   //
-  // One use for this function is parsing handwritten strings in test code.
-  // Another use is to parse the output from google::protobuf::Message::DebugString()
-  // (or ShortDebugString()), because these functions output using
-  // google::protobuf::TextFormat::Print().
+  // One common use for this function is parsing handwritten strings in test
+  // code.
   //
   // If you would like to read a protocol buffer serialized in the
   // (non-human-readable) binary wire format, see
diff --git a/src/google/protobuf/text_format_unittest.cc b/src/google/protobuf/text_format_unittest.cc
index 32a33d8..9417b87 100644
--- a/src/google/protobuf/text_format_unittest.cc
+++ b/src/google/protobuf/text_format_unittest.cc
@@ -47,8 +47,6 @@
 #include <google/protobuf/testing/file.h>
 #include <google/protobuf/any.pb.h>
 #include <google/protobuf/map_unittest.pb.h>
-#include <google/protobuf/test_util.h>
-#include <google/protobuf/test_util2.h>
 #include <google/protobuf/unittest.pb.h>
 #include <google/protobuf/unittest_mset.pb.h>
 #include <google/protobuf/unittest_mset_wire_format.pb.h>
@@ -61,6 +59,8 @@
 #include <gtest/gtest.h>
 #include <google/protobuf/stubs/logging.h>
 #include <google/protobuf/stubs/substitute.h>
+#include <google/protobuf/test_util.h>
+#include <google/protobuf/test_util2.h>
 
 
 // Must be included last.
diff --git a/src/google/protobuf/timestamp.pb.cc b/src/google/protobuf/timestamp.pb.cc
index e64764b..6c8f3b2 100644
--- a/src/google/protobuf/timestamp.pb.cc
+++ b/src/google/protobuf/timestamp.pb.cc
@@ -242,7 +242,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Timestamp::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Timestamp::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Timestamp::GetClassData() const { return &_class_data_; }
diff --git a/src/google/protobuf/type.pb.cc b/src/google/protobuf/type.pb.cc
index 1971fc9..618159b 100644
--- a/src/google/protobuf/type.pb.cc
+++ b/src/google/protobuf/type.pb.cc
@@ -667,7 +667,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Type::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Type::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Type::GetClassData() const { return &_class_data_; }
@@ -1169,7 +1169,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Field::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Field::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Field::GetClassData() const { return &_class_data_; }
@@ -1552,7 +1552,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Enum::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Enum::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Enum::GetClassData() const { return &_class_data_; }
@@ -1829,7 +1829,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData EnumValue::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     EnumValue::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*EnumValue::GetClassData() const { return &_class_data_; }
@@ -2084,7 +2084,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Option::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Option::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Option::GetClassData() const { return &_class_data_; }
diff --git a/src/google/protobuf/unittest.proto b/src/google/protobuf/unittest.proto
index 3a76195..f009ce7 100644
--- a/src/google/protobuf/unittest.proto
+++ b/src/google/protobuf/unittest.proto
@@ -842,7 +842,7 @@
   optional string baz_string = 19 [default = "BAZ"];
 
   message NestedMessage {
-    optional int64 qux_int = 1;
+    optional int64 moo_int = 1;
     repeated int32 corge_int = 2;
   }
 
@@ -1034,6 +1034,17 @@
   optional string a = 1 [default="*/ <- Neither should this."];
 }
 
+// Used to check that the c++ code generator re-orders messages to reduce
+// padding.
+message TestMessageSize {
+  optional bool m1 = 1;
+  optional int64 m2 = 2;
+  optional bool m3 = 3;
+  optional string m4 = 4;
+  optional int32 m5 = 5;
+  optional int64 m6 = 6;
+}
+
 
 // Test that RPC services work.
 message FooRequest  {}
diff --git a/src/google/protobuf/unittest_custom_options.proto b/src/google/protobuf/unittest_custom_options.proto
index 1812d71..d741661 100644
--- a/src/google/protobuf/unittest_custom_options.proto
+++ b/src/google/protobuf/unittest_custom_options.proto
@@ -245,7 +245,7 @@
 }
 
 message ComplexOptionType3 {
-  optional int32 qux = 1;
+  optional int32 moo = 1;
 
   optional group ComplexOptionType5 = 2 {
     optional int32 plugh = 3;
@@ -253,7 +253,7 @@
 }
 
 extend ComplexOptionType1 {
-  optional int32 quux = 7663707;
+  optional int32 mooo = 7663707;
   optional ComplexOptionType3 corge = 7663442;
 }
 
@@ -274,18 +274,18 @@
 // Note that we try various different ways of naming the same extension.
 message VariousComplexOptions {
   option (.protobuf_unittest.complex_opt1).foo = 42;
-  option (protobuf_unittest.complex_opt1).(.protobuf_unittest.quux) = 324;
-  option (.protobuf_unittest.complex_opt1).(protobuf_unittest.corge).qux = 876;
+  option (protobuf_unittest.complex_opt1).(.protobuf_unittest.mooo) = 324;
+  option (.protobuf_unittest.complex_opt1).(protobuf_unittest.corge).moo = 876;
   option (protobuf_unittest.complex_opt1).foo4 = 99;
   option (protobuf_unittest.complex_opt1).foo4 = 88;
   option (complex_opt2).baz = 987;
   option (complex_opt2).(grault) = 654;
   option (complex_opt2).bar.foo = 743;
-  option (complex_opt2).bar.(quux) = 1999;
-  option (complex_opt2).bar.(protobuf_unittest.corge).qux = 2008;
+  option (complex_opt2).bar.(mooo) = 1999;
+  option (complex_opt2).bar.(protobuf_unittest.corge).moo = 2008;
   option (complex_opt2).(garply).foo = 741;
-  option (complex_opt2).(garply).(.protobuf_unittest.quux) = 1998;
-  option (complex_opt2).(protobuf_unittest.garply).(corge).qux = 2121;
+  option (complex_opt2).(garply).(.protobuf_unittest.mooo) = 1998;
+  option (complex_opt2).(protobuf_unittest.garply).(corge).moo = 2121;
   option (ComplexOptionType2.ComplexOptionType4.complex_opt4).waldo = 1971;
   option (complex_opt2).fred.waldo = 321;
   option (complex_opt2).barney = {
@@ -294,7 +294,7 @@
   option (complex_opt2).barney = {
     waldo: 212
   };
-  option (protobuf_unittest.complex_opt3).qux = 9;
+  option (protobuf_unittest.complex_opt3).moo = 9;
   option (complex_opt3).complexoptiontype5.plugh = 22;
   option (complexopt6).xyzzy = 24;
 }
@@ -439,7 +439,9 @@
 // Custom message option that has a required enum field.
 // WARNING: this is strongly discouraged!
 message OldOptionType {
-  enum TestEnum { OLD_VALUE = 0; }
+  enum TestEnum {
+    OLD_VALUE = 0;
+  }
   required TestEnum value = 1;
 }
 
diff --git a/src/google/protobuf/unittest_drop_unknown_fields.proto b/src/google/protobuf/unittest_drop_unknown_fields.proto
index 8aa3a37..a8a98ad 100644
--- a/src/google/protobuf/unittest_drop_unknown_fields.proto
+++ b/src/google/protobuf/unittest_drop_unknown_fields.proto
@@ -31,8 +31,8 @@
 syntax = "proto3";
 
 package unittest_drop_unknown_fields;
-option objc_class_prefix = "DropUnknowns";
 
+option objc_class_prefix = "DropUnknowns";
 option csharp_namespace = "Google.Protobuf.TestProtos";
 
 message Foo {
@@ -50,7 +50,7 @@
     FOO = 0;
     BAR = 1;
     BAZ = 2;
-    QUX = 3;
+    MOO = 3;
   }
   int32 int32_value = 1;
   NestedEnum enum_value = 2;
diff --git a/src/google/protobuf/unknown_field_set.cc b/src/google/protobuf/unknown_field_set.cc
index e592093..74c358e 100644
--- a/src/google/protobuf/unknown_field_set.cc
+++ b/src/google/protobuf/unknown_field_set.cc
@@ -111,7 +111,7 @@
 size_t UnknownFieldSet::SpaceUsedExcludingSelfLong() const {
   if (fields_.empty()) return 0;
 
-  size_t total_size = sizeof(fields_) + sizeof(UnknownField) * fields_.size();
+  size_t total_size = sizeof(UnknownField) * fields_.capacity();
 
   for (const UnknownField& field : fields_) {
     switch (field.type()) {
diff --git a/src/google/protobuf/unknown_field_set_unittest.cc b/src/google/protobuf/unknown_field_set_unittest.cc
index de07d7c..f4d51ae 100644
--- a/src/google/protobuf/unknown_field_set_unittest.cc
+++ b/src/google/protobuf/unknown_field_set_unittest.cc
@@ -37,12 +37,13 @@
 
 #include <google/protobuf/unknown_field_set.h>
 
+#include <string>
 #include <unordered_set>
+#include <vector>
 
 #include <google/protobuf/stubs/callback.h>
 #include <google/protobuf/stubs/common.h>
 #include <google/protobuf/stubs/logging.h>
-#include <google/protobuf/test_util.h>
 #include <google/protobuf/unittest.pb.h>
 #include <google/protobuf/unittest_lite.pb.h>
 #include <google/protobuf/io/coded_stream.h>
@@ -54,6 +55,7 @@
 #include <google/protobuf/testing/googletest.h>
 #include <gtest/gtest.h>
 #include <google/protobuf/stubs/time.h>
+#include <google/protobuf/test_util.h>
 #include <google/protobuf/stubs/stl_util.h>
 
 namespace google {
@@ -533,38 +535,61 @@
 TEST_F(UnknownFieldSetTest, SpaceUsedExcludingSelf) {
   UnknownFieldSet empty;
   empty.AddVarint(1, 0);
-  EXPECT_EQ(sizeof(std::vector<UnknownField>) + sizeof(UnknownField),
-            empty.SpaceUsedExcludingSelf());
+  EXPECT_EQ(sizeof(UnknownField), empty.SpaceUsedExcludingSelf());
 }
 
 TEST_F(UnknownFieldSetTest, SpaceUsed) {
+  // Keep shadow vectors to avoid making assumptions about its capacity growth.
+  // We imitate the push back calls here to determine the expected capacity.
+  std::vector<UnknownField> shadow_vector, shadow_vector_group;
   unittest::TestEmptyMessage empty_message;
 
   // Make sure an unknown field set has zero space used until a field is
   // actually added.
-  size_t base_size = empty_message.SpaceUsedLong();
+  const size_t base = empty_message.SpaceUsedLong();
+  std::string* str = nullptr;
+  UnknownFieldSet* group = nullptr;
+  const auto total = [&] {
+    size_t result = base;
+    result += shadow_vector.capacity() * sizeof(UnknownField);
+    result += shadow_vector_group.capacity() * sizeof(UnknownField);
+    if (str != nullptr) {
+      result += sizeof(std::string);
+      static const size_t sso_capacity = std::string().capacity();
+      if (str->capacity() > sso_capacity) result += str->capacity();
+    }
+    if (group != nullptr) {
+      result += sizeof(UnknownFieldSet);
+    }
+    return result;
+  };
+
   UnknownFieldSet* unknown_fields = empty_message.mutable_unknown_fields();
-  EXPECT_EQ(base_size, empty_message.SpaceUsedLong());
+  EXPECT_EQ(total(), empty_message.SpaceUsedLong());
 
   // Make sure each thing we add to the set increases the SpaceUsedLong().
   unknown_fields->AddVarint(1, 0);
-  EXPECT_LT(base_size, empty_message.SpaceUsedLong());
-  base_size = empty_message.SpaceUsedLong();
+  shadow_vector.emplace_back();
+  EXPECT_EQ(total(), empty_message.SpaceUsedLong()) << "Var";
 
-  std::string* str = unknown_fields->AddLengthDelimited(1);
-  EXPECT_LT(base_size, empty_message.SpaceUsedLong());
-  base_size = empty_message.SpaceUsedLong();
+  str = unknown_fields->AddLengthDelimited(1);
+  shadow_vector.emplace_back();
+  EXPECT_EQ(total(), empty_message.SpaceUsedLong()) << "Str";
 
   str->assign(sizeof(std::string) + 1, 'x');
-  EXPECT_LT(base_size, empty_message.SpaceUsedLong());
-  base_size = empty_message.SpaceUsedLong();
+  EXPECT_EQ(total(), empty_message.SpaceUsedLong()) << "Str2";
 
-  UnknownFieldSet* group = unknown_fields->AddGroup(1);
-  EXPECT_LT(base_size, empty_message.SpaceUsedLong());
-  base_size = empty_message.SpaceUsedLong();
+  group = unknown_fields->AddGroup(1);
+  shadow_vector.emplace_back();
+  EXPECT_EQ(total(), empty_message.SpaceUsedLong()) << "Group";
 
   group->AddVarint(1, 0);
-  EXPECT_LT(base_size, empty_message.SpaceUsedLong());
+  shadow_vector_group.emplace_back();
+  EXPECT_EQ(total(), empty_message.SpaceUsedLong()) << "Group2";
+
+  unknown_fields->AddVarint(1, 0);
+  shadow_vector.emplace_back();
+  EXPECT_EQ(total(), empty_message.SpaceUsedLong()) << "Var2";
 }
 
 
diff --git a/src/google/protobuf/util/internal/expecting_objectwriter.h b/src/google/protobuf/util/internal/expecting_objectwriter.h
index cb0f2aa..76fe2b6 100644
--- a/src/google/protobuf/util/internal/expecting_objectwriter.h
+++ b/src/google/protobuf/util/internal/expecting_objectwriter.h
@@ -28,8 +28,8 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_EXPECTING_OBJECTWRITER_H__
-#define GOOGLE_PROTOBUF_UTIL_CONVERTER_EXPECTING_OBJECTWRITER_H__
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_EXPECTING_OBJECTWRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_EXPECTING_OBJECTWRITER_H__
 
 // An implementation of ObjectWriter that automatically sets the
 // gmock expectations for the response to a method. Every method
@@ -53,9 +53,9 @@
 #include <cstdint>
 
 #include <google/protobuf/stubs/common.h>
-#include <google/protobuf/util/internal/object_writer.h>
 #include <gmock/gmock.h>
 #include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/object_writer.h>
 
 namespace google {
 namespace protobuf {
@@ -247,4 +247,4 @@
 }  // namespace protobuf
 }  // namespace google
 
-#endif  // GOOGLE_PROTOBUF_UTIL_CONVERTER_EXPECTING_OBJECTWRITER_H__
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_EXPECTING_OBJECTWRITER_H__
diff --git a/src/google/protobuf/util/internal/mock_error_listener.h b/src/google/protobuf/util/internal/mock_error_listener.h
index a00fab0..3fbdd88 100644
--- a/src/google/protobuf/util/internal/mock_error_listener.h
+++ b/src/google/protobuf/util/internal/mock_error_listener.h
@@ -28,13 +28,13 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_MOCK_ERROR_LISTENER_H__
-#define GOOGLE_PROTOBUF_UTIL_CONVERTER_MOCK_ERROR_LISTENER_H__
+#ifndef GOOGLE_PROTOBUF_UTIL_INTERNAL_MOCK_ERROR_LISTENER_H__
+#define GOOGLE_PROTOBUF_UTIL_INTERNAL_MOCK_ERROR_LISTENER_H__
 
-#include <google/protobuf/util/internal/error_listener.h>
-#include <google/protobuf/util/internal/location_tracker.h>
 #include <gmock/gmock.h>
 #include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/error_listener.h>
+#include <google/protobuf/util/internal/location_tracker.h>
 
 namespace google {
 namespace protobuf {
@@ -65,4 +65,4 @@
 }  // namespace protobuf
 }  // namespace google
 
-#endif  // GOOGLE_PROTOBUF_UTIL_CONVERTER_MOCK_ERROR_LISTENER_H__
+#endif  // GOOGLE_PROTOBUF_UTIL_INTERNAL_MOCK_ERROR_LISTENER_H__
diff --git a/src/google/protobuf/util/message_differencer.h b/src/google/protobuf/util/message_differencer.h
index 4bdf2cb..f63cd54 100644
--- a/src/google/protobuf/util/message_differencer.h
+++ b/src/google/protobuf/util/message_differencer.h
@@ -223,16 +223,16 @@
   // itself and the second will be the actual field in the embedded message
   // that was added/deleted/modified.
   // Fields will be reported in PostTraversalOrder.
-  // For example, given following proto, if both baz and quux are changed.
+  // For example, given following proto, if both baz and mooo are changed.
   // foo {
   //   bar {
   //     baz: 1
-  //     quux: 2
+  //     mooo: 2
   //   }
   // }
   // ReportModified will be invoked with following order:
-  // 1. foo.bar.baz or foo.bar.quux
-  // 2. foo.bar.quux or foo.bar.baz
+  // 1. foo.bar.baz or foo.bar.mooo
+  // 2. foo.bar.mooo or foo.bar.baz
   // 2. foo.bar
   // 3. foo
   class PROTOBUF_EXPORT Reporter {
diff --git a/src/google/protobuf/wire_format_unittest.inc b/src/google/protobuf/wire_format_unittest.inc
index 54cd87a..d583ddd 100644
--- a/src/google/protobuf/wire_format_unittest.inc
+++ b/src/google/protobuf/wire_format_unittest.inc
@@ -34,7 +34,6 @@
 
 #include <google/protobuf/stubs/logging.h>
 #include <google/protobuf/stubs/common.h>
-#include <google/protobuf/test_util2.h>
 #include <google/protobuf/io/coded_stream.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
@@ -48,6 +47,7 @@
 #include <google/protobuf/stubs/logging.h>
 #include <google/protobuf/stubs/strutil.h>
 #include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/test_util2.h>
 #include <google/protobuf/stubs/stl_util.h>
 
 // clang-format off
diff --git a/src/google/protobuf/wrappers.pb.cc b/src/google/protobuf/wrappers.pb.cc
index 01c63a3..f41ed67 100644
--- a/src/google/protobuf/wrappers.pb.cc
+++ b/src/google/protobuf/wrappers.pb.cc
@@ -405,7 +405,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData DoubleValue::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     DoubleValue::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*DoubleValue::GetClassData() const { return &_class_data_; }
@@ -598,7 +598,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData FloatValue::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     FloatValue::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*FloatValue::GetClassData() const { return &_class_data_; }
@@ -783,7 +783,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Int64Value::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Int64Value::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Int64Value::GetClassData() const { return &_class_data_; }
@@ -964,7 +964,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData UInt64Value::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     UInt64Value::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*UInt64Value::GetClassData() const { return &_class_data_; }
@@ -1145,7 +1145,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Int32Value::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     Int32Value::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Int32Value::GetClassData() const { return &_class_data_; }
@@ -1326,7 +1326,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData UInt32Value::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     UInt32Value::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*UInt32Value::GetClassData() const { return &_class_data_; }
@@ -1507,7 +1507,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData BoolValue::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     BoolValue::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*BoolValue::GetClassData() const { return &_class_data_; }
@@ -1708,7 +1708,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData StringValue::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     StringValue::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*StringValue::GetClassData() const { return &_class_data_; }
@@ -1909,7 +1909,7 @@
 }
 
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData BytesValue::_class_data_ = {
-    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck,
+    ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
     BytesValue::MergeImpl
 };
 const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*BytesValue::GetClassData() const { return &_class_data_; }
diff --git a/tests.sh b/tests.sh
index 73460fd..2b08a1e 100755
--- a/tests.sh
+++ b/tests.sh
@@ -165,6 +165,14 @@
 
   # Run csharp compatibility test between last released and the current version.
   csharp/compatibility_tests/v3.0.0/test.sh $LAST_RELEASED
+  
+  # Regression test for https://github.com/protocolbuffers/protobuf/issues/9526
+  # - all line endings in .proto and .cs (and .csproj) files should be LF.
+  if git ls-files --eol csharp | grep -E '\.cs|\.proto' | grep -v w/lf
+  then
+    echo "The files listed above have mixed or CRLF line endings; please change to LF."
+    exit 1
+  fi
 }
 
 build_golang() {
@@ -436,7 +444,7 @@
 build_jruby93() {
   internal_build_cpp                # For conformance tests.
   internal_build_java jdk8 && cd .. # For Maven protobuf jar with local changes
-  cd ruby && bash travis-test.sh jruby-9.3.3.0 && cd ..
+  cd ruby && bash travis-test.sh jruby-9.3.4.0 && cd ..
 }
 
 build_javascript() {
diff --git a/update_version.py b/update_version.py
index 3a96991..e1f259e 100755
--- a/update_version.py
+++ b/update_version.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Usage: ./update_version.py <MAJOR>.<MINOR>.<MICRO> [<RC version>]
 #
 # Example:
@@ -201,7 +201,7 @@
         '#if %s < PROTOBUF_MIN_PROTOC_VERSION' % cpp_version,
         line)
     return line
-    
+
   RewriteTextFile('src/google/protobuf/stubs/common.h', RewriteCommon)
   RewriteTextFile('src/google/protobuf/port_def.inc', RewritePortDef)
   RewriteTextFile('src/google/protobuf/any.pb.h', RewritePbH)
@@ -268,7 +268,7 @@
   RewriteXml('protoc-artifacts/pom.xml',
     lambda document : ReplaceText(
       Find(document.documentElement, 'version'), GetFullVersion()))
-  
+
   RewriteTextFile('java/README.md',
     lambda line : re.sub(
       r'<version>.*</version>',