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<T></c>
- /// and <c>MapField<TKey, TValue></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<T></c>
+ /// and <c>MapField<TKey, TValue></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>',